forked from Minecraft/minecraft_manager
Initial commit for Gitea
commit
da44791186
|
@ -0,0 +1,3 @@
|
|||
*.pyc
|
||||
__pycache__/*
|
||||
docs/build/*
|
|
@ -0,0 +1,106 @@
|
|||
from __future__ import absolute_import
|
||||
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from minecraft_manager.models import Application, Warning, Ticket, Player, IP, UserSettings, Alert, Note
|
||||
|
||||
|
||||
class PlayerInline(admin.StackedInline):
|
||||
model = Player
|
||||
can_delete = False
|
||||
verbose_name_plural = 'player'
|
||||
|
||||
|
||||
class UserAdmin(BaseUserAdmin):
|
||||
inlines = (PlayerInline, )
|
||||
actions = ['deactivate_account']
|
||||
|
||||
def deactivate_account(self, request, queryset):
|
||||
rows_updated = queryset.update(is_active=False)
|
||||
if rows_updated == 1:
|
||||
message_bit = "1 user was"
|
||||
else:
|
||||
message_bit = "%s users were" % rows_updated
|
||||
self.message_user(request, "%s successfully deactivated." % message_bit)
|
||||
deactivate_account.short_description = "Deactivate selected accounts"
|
||||
|
||||
|
||||
|
||||
|
||||
class PlayerAdmin(admin.ModelAdmin):
|
||||
search_fields = ["username", "uuid"]
|
||||
|
||||
|
||||
class ApplicationAdmin(admin.ModelAdmin):
|
||||
search_fields = ["username"]
|
||||
#date_hierarchy = 'date'
|
||||
|
||||
|
||||
class TicketPriorityFilter(admin.SimpleListFilter):
|
||||
title = _('Priority')
|
||||
parameter_name = 'priority'
|
||||
|
||||
def lookups(self, request, model_admin):
|
||||
return (
|
||||
('0', _('Low')),
|
||||
('1', _('Medium')),
|
||||
('2', _('High'))
|
||||
)
|
||||
|
||||
def queryset(self, request, queryset):
|
||||
if self.value() == '0':
|
||||
return queryset.filter(priority='L')
|
||||
if self.value() == '1':
|
||||
return queryset.filter(priority='M')
|
||||
if self.value() == '2':
|
||||
return queryset.filter(priority='H')
|
||||
|
||||
|
||||
class TicketAdmin(admin.ModelAdmin):
|
||||
search_fields = ["player__username", "staff__username"]
|
||||
#date_hierarchy = 'date'
|
||||
list_filter = (TicketPriorityFilter,)
|
||||
|
||||
|
||||
class WarningSeverityFilter(admin.SimpleListFilter):
|
||||
title = _('Severity')
|
||||
parameter_name = 'severity'
|
||||
|
||||
def lookups(self, request, model_admin):
|
||||
return (
|
||||
('0', _('Low')),
|
||||
('1', _('Medium')),
|
||||
('2', _('High'))
|
||||
)
|
||||
|
||||
def queryset(self, request, queryset):
|
||||
if self.value() == '0':
|
||||
return queryset.filter(priority='L')
|
||||
if self.value() == '1':
|
||||
return queryset.filter(priority='M')
|
||||
if self.value() == '2':
|
||||
return queryset.filter(priority='H')
|
||||
|
||||
|
||||
class WarningAdmin(admin.ModelAdmin):
|
||||
search_fields = ["player__username", "staff__username"]
|
||||
#date_hierarchy = 'date'
|
||||
list_filter = (WarningSeverityFilter,)
|
||||
|
||||
|
||||
try:
|
||||
admin.site.unregister(User)
|
||||
admin.site.register(User, UserAdmin)
|
||||
admin.site.register(UserSettings)
|
||||
admin.site.register(Application, ApplicationAdmin)
|
||||
admin.site.register(Warning, WarningAdmin)
|
||||
admin.site.register(Ticket, TicketAdmin)
|
||||
admin.site.register(Player, PlayerAdmin)
|
||||
admin.site.register(IP)
|
||||
admin.site.register(Alert)
|
||||
admin.site.register(Note)
|
||||
except admin.sites.AlreadyRegistered:
|
||||
pass
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
import socket, requests, logging, os, datetime, pytz, mcstatus, discord
|
||||
from minecraft_manager.models import Alert
|
||||
from django.contrib.auth.models import User
|
||||
from django.conf import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def create_alert(message):
|
||||
users = User.objects.filter(is_active=True)
|
||||
for user in users:
|
||||
alert = Alert(user=user, message=message)
|
||||
alert.save()
|
||||
|
||||
|
||||
PLUGIN_APPLICATION = 'application'
|
||||
PLUGIN_ACCEPT = 'accept'
|
||||
PLUGIN_DENY = 'deny'
|
||||
PLUGIN_GLOBAL_CHAT = 'global'
|
||||
PLUGIN_STAFF_CHAT = 'staff'
|
||||
|
||||
|
||||
def plugin(key, command):
|
||||
host = '127.0.0.1'
|
||||
port = getattr(settings, 'PLUGIN_PORT', None)
|
||||
full_command = "{0} {1}".format(key, command)
|
||||
if port and plugin_exists():
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.connect((host, port))
|
||||
sock.sendall(full_command.encode('utf-8'))
|
||||
sock.close()
|
||||
|
||||
|
||||
def discord_mcm(message='', embeds=None, ping=False):
|
||||
discord_mcm_webhook = getattr(settings, 'DISCORD_MCM_WEBHOOK', None)
|
||||
if discord_mcm_webhook:
|
||||
ping_list = getattr(settings, 'DISCORD_PING_LIST', [])
|
||||
if ping and ping_list:
|
||||
ping_list = ["<@&{0}>".format(ping) for ping in ping_list]
|
||||
message = "{0}\n{1}".format(" ".join(ping_list), message)
|
||||
data = {}
|
||||
if message:
|
||||
data['content'] = message
|
||||
if embeds:
|
||||
data['embeds'] = embeds
|
||||
return requests.post(discord_mcm_webhook, json=data)
|
||||
return None
|
||||
|
||||
|
||||
def discord_notification(message='', embeds=None, ping=False):
|
||||
discord_notification_webhook = getattr(settings, 'DISCORD_NOTIFICATION_WEBHOOK', None)
|
||||
if discord_notification_webhook:
|
||||
ping_list = getattr(settings, 'DISCORD_PING_LIST', [])
|
||||
if ping and ping_list:
|
||||
ping_list = ["<@&{0}>".format(ping) for ping in ping_list]
|
||||
message = "{0}\n{1}".format(" ".join(ping_list), message)
|
||||
data = {}
|
||||
if message:
|
||||
data['content'] = message
|
||||
if embeds:
|
||||
data['embeds'] = embeds
|
||||
return requests.post(discord_notification_webhook, json=data)
|
||||
return None
|
||||
|
||||
|
||||
def discord_webhook(webhook_name=None, message=None, embed=None, ping=False):
|
||||
webhook = getattr(settings, 'DISCORD_{}_WEBHOOK'.format(webhook_name.upper()), getattr(settings, 'DISCORD_WEBHOOK', None))
|
||||
if webhook and (message or embed):
|
||||
ping_list = getattr(settings, 'DISCORD_PING_LIST', [])
|
||||
if ping and ping_list:
|
||||
ping_list = ["<@&{0}>".format(ping) for ping in ping_list]
|
||||
message = "{0}\n{1}".format(" ".join(ping_list), message)
|
||||
data = {}
|
||||
if message:
|
||||
data['content'] = message
|
||||
if embed:
|
||||
data['embeds'] = embed
|
||||
return requests.post(webhook, json=data)
|
||||
return None
|
||||
|
||||
|
||||
|
||||
def strip_format(message):
|
||||
return message.replace("§0", "").replace("§1", "").replace("§2", "").replace("§3", "").replace("§4", "") \
|
||||
.replace("§5", "").replace("§6", "").replace("§7", "").replace("§8", "").replace("§9", "").replace("§a", "") \
|
||||
.replace("§b", "").replace("§c", "").replace("§d", "").replace("§e", "").replace("§f", "").replace("§r", "") \
|
||||
.replace("§k", "").replace("§l", "").replace("§m", "").replace("§n", "").replace("§o", "")
|
||||
|
||||
|
||||
def plugin_exists():
|
||||
return os.path.exists(os.path.join(getattr(settings, 'MINECRAFT_BASE_DIR', ''), 'plugins/MinecraftManager'))
|
||||
|
||||
|
||||
def get_chats(as_timezone):
|
||||
global_log = getattr(settings, 'GLOBAL_LOG', "")
|
||||
staff_log = getattr(settings, 'STAFF_LOG', "")
|
||||
log_time_format = getattr(settings, 'LOG_TIME_FORMAT', '%m/%d/%y %I:%M %p')
|
||||
if plugin_exists():
|
||||
try:
|
||||
with open(global_log, encoding='utf-8') as g:
|
||||
g_chat = g.readlines()[-100:]
|
||||
except OSError:
|
||||
g_chat = []
|
||||
for idx, gc in enumerate(g_chat):
|
||||
try:
|
||||
date_str = gc.split("] ")[0].replace("[", "")
|
||||
msg = gc.split("] ", 1)[1]
|
||||
dt = datetime.datetime.strptime(date_str, log_time_format)
|
||||
dt = pytz.timezone('US/Central').localize(dt)
|
||||
dt = dt.astimezone(pytz.timezone(as_timezone))
|
||||
g_chat[idx] = {"date": dt.strftime(log_time_format), "text": msg}
|
||||
except:
|
||||
g_chat[idx] = {"date": "01/01/17 00:00 AM", "name": "Error", "text": "Parsing Line"}
|
||||
try:
|
||||
with open(staff_log, encoding='utf-8') as s:
|
||||
s_chat = s.readlines()[-100:]
|
||||
except OSError:
|
||||
s_chat = []
|
||||
for idx, sc in enumerate(s_chat):
|
||||
try:
|
||||
date_str = sc.split("] ")[0].replace("[", "")
|
||||
msg = sc.split("] ", 1)[1]
|
||||
dt = datetime.datetime.strptime(date_str, log_time_format)
|
||||
dt = pytz.timezone('US/Central').localize(dt)
|
||||
dt = dt.astimezone(pytz.timezone(as_timezone))
|
||||
s_chat[idx] = {"date": dt.strftime(log_time_format), "text": msg}
|
||||
except:
|
||||
s_chat[idx] = {"date": "01/01/17 00:00 AM", "name": "Error", "text": "Parsing Line"}
|
||||
|
||||
return {'global': g_chat, 'staff': s_chat}
|
||||
return None
|
||||
|
||||
|
||||
def get_query():
|
||||
try:
|
||||
server = mcstatus.MinecraftServer.lookup(getattr(settings, 'SERVER_QUERY_IP', None))
|
||||
query = server.query()
|
||||
return {'max': str(query.players.max), 'online': str(query.players.online),
|
||||
'players': sorted(query.players.names)}
|
||||
except:
|
||||
return {'max': 0, 'online': 0,
|
||||
'players': []}
|
|
@ -0,0 +1,296 @@
|
|||
import discord, logging, re, sys, traceback, asyncio, datetime, os, time
|
||||
from minecraft_manager.models import Application, Player
|
||||
from minecraft_manager.api import api
|
||||
from django.conf import settings
|
||||
from django.db import close_old_connections
|
||||
from threading import Thread
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Discord(discord.Client):
|
||||
discord_game = 'MCM'
|
||||
prefix = getattr(settings, 'DISCORD_BOT_PREFIX', '!')
|
||||
auth_roles = getattr(settings, 'DISCORD_BOT_ROLES', [])
|
||||
error_users = getattr(settings, 'DISCORD_ERROR_USERS', [])
|
||||
token = None
|
||||
|
||||
def __init__(self, token, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.token = token
|
||||
|
||||
@asyncio.coroutine
|
||||
def on_ready(self):
|
||||
print('Logged in as')
|
||||
print(self.user.name)
|
||||
print(self.user.id)
|
||||
print(discord.__version__)
|
||||
print('Voice Loaded: {0}'.format(discord.opus.is_loaded()))
|
||||
print('OAuth URL: https://discordapp.com/api/oauth2/authorize?client_id={0}&permissions=0&scope=bot'.format(self.user.id))
|
||||
print('------')
|
||||
logger.info('Logged in as {0} ({1}) with discord.py v{2}'.format(self.user.name, self.user.id, discord.__version__))
|
||||
yield from self.change_presence(game=discord.Game(name=self.discord_game))
|
||||
|
||||
@asyncio.coroutine
|
||||
def discord_message(self, channel, message):
|
||||
if isinstance(message, discord.Embed):
|
||||
for idx, field in enumerate(message.fields):
|
||||
if not field.value:
|
||||
message.set_field_at(idx, name=field.name, value="N/A")
|
||||
yield from self.send_message(channel, embed=message)
|
||||
else:
|
||||
yield from self.send_message(channel, message)
|
||||
|
||||
@asyncio.coroutine
|
||||
def on_message(self, message):
|
||||
|
||||
# IGNORE DM AND BOTS
|
||||
if message.author.bot is True or message.channel.is_private is True:
|
||||
return
|
||||
|
||||
member_roles = [role.id for role in message.author.roles]
|
||||
|
||||
# IF MEMBER IS NOT AUTHORIZED, IGNORE
|
||||
if not any(role in self.auth_roles for role in member_roles):
|
||||
return
|
||||
|
||||
# FIX STALE DB CONNECTIONS
|
||||
close_old_connections()
|
||||
|
||||
# HELP
|
||||
match = re.match("[{0}]help$".format(self.prefix), message.content)
|
||||
if match:
|
||||
embed = discord.Embed(colour=discord.Colour(0x417505))
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/avatars/454457830918062081/b5792489bc43d9e17b8f657880a17dd4.png")
|
||||
embed.add_field(name="Minecraft Manager Help", value="-----------------------------")
|
||||
embed.add_field(name="{}[app ]search <username>".format(self.prefix), value="Search for applications by partial or exact username.")
|
||||
embed.add_field(name="{}[app ]info <app ID>".format(self.prefix), value="Get detailed information about a specific application.")
|
||||
embed.add_field(name="{}[app ]accept|deny <app ID>".format(self.prefix), value="Take action on an application.")
|
||||
embed.add_field(name="{}compare".format(self.prefix), value="Compare Discord users to the Whitelist")
|
||||
yield from self.discord_message(message.channel, embed)
|
||||
# APP COMMANDS WITH APP ID
|
||||
match = re.match("[{0}](?:app )?(i|info|a|accept|d|deny) (\d+)$".format(self.prefix), message.content)
|
||||
if match:
|
||||
if match.group(1) and match.group(2):
|
||||
action = match.group(1)[:1]
|
||||
action_display = "accept" if action == "a" else "deny" if action == "d" else ""
|
||||
yield from self.discord_message(message.channel, "{0} App ID **{1}**".format(
|
||||
"Retrieving info for" if action == "i" else "{0}ing".format(action_display.capitalize()),
|
||||
match.group(2)))
|
||||
application = None
|
||||
try:
|
||||
application = Application.objects.get(id=match.group(2))
|
||||
except:
|
||||
yield from self.discord_message(message.channel, "An Application with that ID doesn't exist.")
|
||||
return
|
||||
if action == "i":
|
||||
# Info
|
||||
msg = self.build_info(application)
|
||||
else:
|
||||
# Action
|
||||
accept = True if action == "a" else False
|
||||
if not application.accepted:
|
||||
application.accepted = accept
|
||||
application.save()
|
||||
msg = "App ID **{0}** was successfully {1}.".format(match.group(2), "accepted" if accept else "denied")
|
||||
api.plugin("accept" if accept else "deny", application.username)
|
||||
else:
|
||||
msg = "App ID **{0}** was already {1}.".format(match.group(2), "accepted" if application.accepted else "denied")
|
||||
yield from self.discord_message(message.channel, msg)
|
||||
return
|
||||
# APP INFO WITH PARTIAL NAME SEARCH
|
||||
match = re.match("[{0}](?:app )?(?:search|info) (\S+)?$".format(self.prefix), message.content)
|
||||
if match:
|
||||
search = match.group(1)
|
||||
yield from self.discord_message(message.channel,"Searching for applications whose username contains '{0}'".format(search))
|
||||
applications = Application.objects.filter(username__icontains=search)[:10]
|
||||
count = Application.objects.filter(username__icontains=search).count()
|
||||
if count > 0:
|
||||
if count == 1:
|
||||
info = self.build_info(applications[0])
|
||||
else:
|
||||
info = "**Found the following applications**"
|
||||
for app in applications:
|
||||
info += "\n{0} - {1} ({2})".format(app.id, app.username.replace("_", "\\_"), app.status)
|
||||
if count > 10:
|
||||
info += "\n**This is only 10 applications out of {0} found. Please narrow your search if possible.**".format(len(applications))
|
||||
else:
|
||||
info = "No applications matched that search."
|
||||
yield from self.discord_message(message.channel, info)
|
||||
# COMPARE DISCORD USERS TO WHITELIST
|
||||
match = re.match("[{0}]compare".format(self.prefix), message.content)
|
||||
if match:
|
||||
yield from self.delete_message(message)
|
||||
yield from self.send_typing(message.channel)
|
||||
no_player = []
|
||||
no_application = []
|
||||
for member in message.server.members:
|
||||
if member.bot:
|
||||
continue
|
||||
name = member.nick if member.nick else member.name
|
||||
try:
|
||||
Player.objects.get(username__iexact=name)
|
||||
except:
|
||||
no_player.append(name)
|
||||
try:
|
||||
Application.objects.get(username__iexact=name)
|
||||
except:
|
||||
no_player = no_player[:-1]
|
||||
no_application.append(name)
|
||||
header = "**The following users have an application match, but no player match on the whitelist:**\n"
|
||||
yield from self.discord_message(message.author, "{}```{}```".format(header, "\n".join(no_player)))
|
||||
header = "**The following users do not have an application or player match on the whitelist:**\n"
|
||||
yield from self.discord_message(message.author, "{}```{}```".format(header, "\n".join(no_application)))
|
||||
|
||||
|
||||
def build_info(self, application):
|
||||
embed = discord.Embed(colour=discord.Colour(0x417505))
|
||||
embed.set_thumbnail(
|
||||
url="https://minotar.net/helm/{0}/100.png".format(application.username))
|
||||
embed.add_field(name="Application ID", value=application.id)
|
||||
embed.add_field(name="Username", value=application.username.replace("_", "\\_"))
|
||||
embed.add_field(name="Age", value=application.age)
|
||||
embed.add_field(name="Type of Player", value=application.player_type)
|
||||
embed.add_field(name="Ever been banned", value=application.ever_banned)
|
||||
if application.ever_banned:
|
||||
embed.add_field(name="Reason for being banned", value=application.ever_banned_explanation)
|
||||
embed.add_field(name="Reference", value=application.reference)
|
||||
embed.add_field(name="Read the Rules", value=application.read_rules)
|
||||
embed.add_field(name="Date", value=application.date_display)
|
||||
embed.add_field(name="Status", value=application.status)
|
||||
return embed
|
||||
|
||||
@asyncio.coroutine
|
||||
def on_error(self, event, *args, **kwargs):
|
||||
print(sys.exc_info())
|
||||
print("Exception raised by " + event)
|
||||
error = '{0}\n{1}'.format(sys.exc_info()[1], ''.join(traceback.format_tb(sys.exc_info()[2])))
|
||||
logger.error(error)
|
||||
for user in self.error_users:
|
||||
try:
|
||||
user = discord.User(id=user)
|
||||
yield from self.discord_message(user, '```python\n{}```'.format(error))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def run_bot(self):
|
||||
self.run(self.token)
|
||||
|
||||
|
||||
class OreAlert:
|
||||
# Options
|
||||
log = os.path.join(settings.MINECRAFT_BASE_DIR, 'logs/latest.log')
|
||||
purge = 30 # How long until we purge, in minutes
|
||||
rotate = 5 # How long without input before we assume the log has rotated
|
||||
notify_start = 5 # How many veins found within the above purge minutes to notify
|
||||
notify_each = 1 # After the initial alert, how many should be found in addition before more alerts?
|
||||
notify_ping = 5 # After the initial alert, how many should be found in addition before more pings?
|
||||
|
||||
playerList = []
|
||||
|
||||
class Player:
|
||||
|
||||
def __init__(self, name, time, prev=[]):
|
||||
self.name = name
|
||||
self.time = time
|
||||
self.prev = prev
|
||||
|
||||
def compare(self, player):
|
||||
if self.name == player.name:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def to_string(self):
|
||||
return str(self.name) + ": " + str(self.time) + ", previous: " + self.prev_to_string()
|
||||
|
||||
def prev_to_string(self):
|
||||
ret = ""
|
||||
if len(self.prev) > 0:
|
||||
for p in self.prev:
|
||||
ret += str(p) + " "
|
||||
|
||||
return ret.rstrip()
|
||||
else:
|
||||
return ret
|
||||
|
||||
def minute_interval(self, start, end):
|
||||
reverse = False
|
||||
if start > end:
|
||||
start, end = end, start
|
||||
reverse = True
|
||||
delta = (end.hour - start.hour) * 60 + end.minute - start.minute + (end.second - start.second) / 60.0
|
||||
if reverse:
|
||||
delta = 24 * 60 - delta
|
||||
return delta
|
||||
|
||||
def follow(self, filename):
|
||||
thefile = open(filename, 'r', encoding='utf-8')
|
||||
thefile.seek(0, 2) # Go to the end of the file
|
||||
start = datetime.datetime.now()
|
||||
end = datetime.datetime.now()
|
||||
api.discord_notification('OreAlert has started successfully.')
|
||||
while True:
|
||||
line = thefile.readline()
|
||||
if not line:
|
||||
if self.minute_interval(start, end) > self.rotate:
|
||||
thefile.close()
|
||||
time.sleep(5)
|
||||
thefile = open(filename, 'r', encoding='utf-8')
|
||||
thefile.seek(0, 2) # Go to the end of the file
|
||||
start = datetime.datetime.now()
|
||||
end = datetime.datetime.now()
|
||||
# api.discord_notification('OreAlert has closed and re-opened the log...hopefully it just rotated.')
|
||||
continue
|
||||
end = end + datetime.timedelta(milliseconds=100)
|
||||
time.sleep(0.1) # Sleep briefly
|
||||
continue
|
||||
start = datetime.datetime.now()
|
||||
end = datetime.datetime.now()
|
||||
yield line
|
||||
|
||||
def run_bot(self):
|
||||
cur_line = ""
|
||||
try:
|
||||
loglines = self.follow(self.log)
|
||||
for line in loglines:
|
||||
# [00: 03:46] [Server thread / INFO]: [MinecraftManager]: [OreAlert]: Etzelia
|
||||
if "MinecraftManager" in line and "OreAlert" in line: # Filter out non-OreAlert log statements
|
||||
cur_time = line.split()[0].replace("[", "").replace("]", "")
|
||||
if ":" in cur_time: # Make sure we have a time.
|
||||
time_array = cur_time.split(":")
|
||||
name = line.split()[-1]
|
||||
dt = datetime.time(int(time_array[0]), int(time_array[1]), int(time_array[2]))
|
||||
p = self.Player(name, dt)
|
||||
new_player = True
|
||||
for player in self.playerList:
|
||||
if p.compare(player):
|
||||
new_player = False
|
||||
player.prev[:] = [x for x in player.prev if not self.minute_interval(x,
|
||||
dt) > self.purge] # First, purge any times older than our configured amount
|
||||
player.prev.append(dt) # Add the new time
|
||||
if len(player.prev) >= self.notify_start:
|
||||
# MCM Alert
|
||||
if len(player.prev) == self.notify_start:
|
||||
api.create_alert("OreAlert: {0}".format(player.name))
|
||||
if len(player.prev) % self.notify_each == 0:
|
||||
# In-game Notification
|
||||
api.plugin(api.PLUGIN_STAFF_CHAT,
|
||||
"OreAlert {0} has found {1} diamond ore veins within {2} minutes.".format(
|
||||
player.name, len(player.prev), self.purge))
|
||||
# Discord Notification
|
||||
ping = True if len(player.prev) % self.notify_ping == 0 else False
|
||||
api.discord_notification(
|
||||
'{0} has found {1} diamond ore veins within {2} minutes.'.format(
|
||||
player.name.replace("_", "\\_"), len(player.prev), self.purge), ping=ping)
|
||||
if new_player:
|
||||
p.prev = [p.time]
|
||||
self.playerList.append(p)
|
||||
except KeyboardInterrupt:
|
||||
api.discord_notification("OreAlert has been stopped manually.")
|
||||
except:
|
||||
api.discord_notification("OreAlert has crashed!", ping=True)
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
from django.conf.urls import url
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
import minecraft_manager.api.views as api
|
||||
|
||||
urlpatterns = [
|
||||
#API - Password protected
|
||||
url(r'^web/(?P<keyword>\w{1,20})/$', csrf_exempt(api.WebAPI.as_view()), name="api-web"),
|
||||
url(r'^plugin/(?P<keyword>\w{1,20})/$', csrf_exempt(api.PluginAPI.as_view()), name="api-plugin"),
|
||||
url(r'^form/(?P<request_model>\w{1,20})/$', csrf_exempt(api.FormAPI.as_view()), name="api-form"),
|
||||
url(r'^model/(?P<request_model>\w{1,20})/$', csrf_exempt(api.ModelAPI.as_view()), name="api-model"),
|
||||
url(r'^stats/$', csrf_exempt(api.StatsAPI.as_view()), name="api-stats"),
|
||||
]
|
|
@ -0,0 +1,423 @@
|
|||
from __future__ import absolute_import
|
||||
|
||||
import logging, random, string, discord
|
||||
from django.contrib.auth.forms import PasswordChangeForm
|
||||
from django.contrib.auth import update_session_auth_hash
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.http import JsonResponse, HttpResponse
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django.views.generic import View
|
||||
from django.forms import modelform_factory
|
||||
|
||||
import minecraft_manager.forms as MCMForms
|
||||
from minecraft_manager.models import Player, UserSettings, Application, IP, Ticket, Warning
|
||||
import minecraft_manager.api.api as mcm_api
|
||||
import minecraft_manager.utils as mcm_utils
|
||||
import minecraft_manager.external.stats as mcm_stats
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def request_allowed(request):
|
||||
is_authenticated = False
|
||||
if hasattr(request, 'user'):
|
||||
if hasattr(request.user, 'is_authenticated'):
|
||||
is_authenticated = request.user.is_authenticated
|
||||
password = getattr(settings, 'API_PASSWORD', None)
|
||||
get = request.GET
|
||||
post= request.POST
|
||||
request_password = None
|
||||
if 'api' in get:
|
||||
request_password = get['api']
|
||||
elif 'api' in post:
|
||||
request_password = post['api']
|
||||
correct_password = False
|
||||
if password and request_password:
|
||||
correct_password = request_password == password
|
||||
return is_authenticated or correct_password
|
||||
|
||||
|
||||
def clean(model, data):
|
||||
cleaned = {}
|
||||
for d in data:
|
||||
attr = d
|
||||
if '__' in d:
|
||||
attr = d.split('__')[0]
|
||||
if hasattr(model, attr):
|
||||
cleaned[d] = data[d]
|
||||
return cleaned
|
||||
|
||||
|
||||
def generate_password():
|
||||
return "".join([random.choice(string.ascii_letters + string.digits) for idx in range(0, 20)])
|
||||
|
||||
|
||||
class WebAPI(View):
|
||||
|
||||
def get(self, request, keyword):
|
||||
get = request.GET
|
||||
data = {'success': False, 'message': 'API failed'}
|
||||
if request_allowed(request):
|
||||
keyword = keyword.lower()
|
||||
if keyword == 'log':
|
||||
html_global = ""
|
||||
html_staff = ""
|
||||
chats = mcm_api.get_chats(request.user.usersettings.default_timezone)
|
||||
if chats:
|
||||
for g in chats['global']:
|
||||
g['text'] = g['text'].replace("<", "<").replace(">", ">")
|
||||
if request.user.usersettings.show_timestamp_chat:
|
||||
html_global += "<div>[{0}] {1}</div>".format(g['date'], g['text'])
|
||||
else:
|
||||
html_global += "<div data-toggle='tooltip' title='{0}' data-placement='left'>{1}</div>".format(g['date'], g['text'])
|
||||
for s in chats['staff']:
|
||||
s['text'] = s['text'].replace("<", "<").replace(">", ">")
|
||||
if request.user.usersettings.show_timestamp_chat:
|
||||
html_staff += "<div>[{0}] {1}</div>".format(s['date'], s['text'])
|
||||
else:
|
||||
html_staff += "<div data-toggle='tooltip' title='{0}' data-placement='left'>{1}</div>".format(s['date'], s['text'])
|
||||
html = {'global': html_global, 'staff': html_staff}
|
||||
data = {'chats': chats, 'html': html}
|
||||
elif keyword == 'online':
|
||||
query = mcm_api.get_query()
|
||||
html = ""
|
||||
for p in query['players']:
|
||||
html += "<div class='label label-primary'>{0}</div>".format(p)
|
||||
html += "<br/>"
|
||||
data = {'query': query, 'html': html}
|
||||
elif keyword == "coreprotect":
|
||||
if 'username' in get and 'ip' in get:
|
||||
user = User.objects.get(username=get['username'])
|
||||
if user.is_active and user.usersettings.last_ip == get['ip']:
|
||||
data = {'success': True, 'message': self.access_level(user)}
|
||||
else:
|
||||
data = {'success': False, 'message': 'Parameters not set'}
|
||||
else:
|
||||
data = {'message': 'Not Authorized', 'success': False}
|
||||
return JsonResponse(data)
|
||||
|
||||
def post(self, request, keyword):
|
||||
post = request.POST
|
||||
data = {}
|
||||
if request_allowed(request):
|
||||
keyword = keyword.lower()
|
||||
if keyword == 'settings' and request.user.usersettings:
|
||||
for s in [a for a in dir(UserSettings) if not a.startswith('__') and not callable(getattr(UserSettings,a))]:
|
||||
if s in post:
|
||||
setattr(request.user.usersettings, s, post[s])
|
||||
request.user.usersettings.save()
|
||||
data = {'success': True, 'message': 'User Settings saved'}
|
||||
elif keyword == 'password':
|
||||
form = PasswordChangeForm(request.user, post)
|
||||
if form.is_valid():
|
||||
user = form.save()
|
||||
update_session_auth_hash(request, user)
|
||||
return HttpResponse("success")
|
||||
else:
|
||||
return HttpResponse(form.as_p())
|
||||
elif keyword == 'alert':
|
||||
form = MCMForms.AlertForm(request.POST)
|
||||
if form.is_valid():
|
||||
if mcm_api.create_alert(form.cleaned_data['message']):
|
||||
data = {'success': True}
|
||||
else:
|
||||
data = {'success': False, 'message': 'Could not create Alerts'}
|
||||
else:
|
||||
data = {'success': False, 'message': 'Invalid Message'}
|
||||
elif keyword == "discord":
|
||||
if 'message' in post:
|
||||
ping = post['ping'] if 'ping' in post else False
|
||||
mcm_api.discord_notification(post['message'], ping=ping)
|
||||
data = {'success': True, 'message': "Success", 'extra': {'message': post['message'], 'ping': ping}}
|
||||
else:
|
||||
data = {'success': False, 'message': 'No message supplied'}
|
||||
else:
|
||||
data = {'success': True, 'message': 'Model set to "{0}"'.format(keyword)}
|
||||
else:
|
||||
data = {'message': 'Not Authorized', 'success': False}
|
||||
return JsonResponse(data)
|
||||
|
||||
def access_level(self, user):
|
||||
access = {'cpp': False, 'cpf': False, 'cpa': False}
|
||||
if user.has_perm('auth.coreprotect_partial'):
|
||||
access['cpp'] = True
|
||||
if user.has_perm('auth.coreprotect_full'):
|
||||
access['cpf'] = True
|
||||
if user.has_perm('auth.coreprotect_activity'):
|
||||
access['cpa'] = True
|
||||
return access
|
||||
|
||||
|
||||
class PluginAPI(View):
|
||||
|
||||
def get(self, request, keyword):
|
||||
json = {'status': True, 'message': '', 'extra': ''}
|
||||
if request_allowed(request):
|
||||
get = request.GET
|
||||
keyword = keyword.lower()
|
||||
|
||||
return JsonResponse(json)
|
||||
|
||||
def post(self, request, keyword):
|
||||
json = {'status': True, 'message': '', 'extra': ''}
|
||||
if request_allowed(request):
|
||||
post = request.POST
|
||||
keyword = keyword.lower()
|
||||
if "application" == keyword:
|
||||
if Application.objects.filter(username=post['username']).exists():
|
||||
application = Application.objects.get(username=post['username'])
|
||||
if application.accepted is not None:
|
||||
json['status'] = False
|
||||
json['message'] = "An application for {0} has already been acted on. Please contact Staff.".format(
|
||||
post['username'])
|
||||
return JsonResponse(json)
|
||||
else:
|
||||
application.accepted = None
|
||||
application.age = post['age']
|
||||
application.player_type = post['player_type']
|
||||
application.ever_banned = False if post['ever_banned'] == "no" else True
|
||||
application.ever_banned_explanation = post['ever_banned_explanation']
|
||||
application.reference = post['reference']
|
||||
application.read_rules = post['read_rules']
|
||||
else:
|
||||
application = Application(username=post['username'], age=post['age'],
|
||||
player_type=post['player_type'],
|
||||
ever_banned=False if post['ever_banned'] == "no" else True,
|
||||
ever_banned_explanation= post['ever_banned_explanation'],
|
||||
reference=post['reference'], read_rules=post['read_rules'])
|
||||
application.save()
|
||||
if Player.objects.filter(username__iexact=post['username']).exists():
|
||||
player = Player.objects.get(username__iexact=post['username'])
|
||||
player.application_id = application.id
|
||||
player.save()
|
||||
json['message'] = "{0}'s application was submitted.".format(application.username)
|
||||
json['extra'] = application.id
|
||||
msg = mcm_utils.build_application(application)
|
||||
mcm_api.discord_mcm(message='New Application!', embeds=msg)
|
||||
elif "application_action" == keyword:
|
||||
if Application.objects.filter(id=post['application_id']).exists():
|
||||
application = Application.objects.get(id=post['application_id'])
|
||||
if application.accepted is not None:
|
||||
json['status'] = False
|
||||
json['message'] = "Application was already {0}.".format(
|
||||
"accepted" if post['action'] == "True" else "denied")
|
||||
else:
|
||||
application.accepted = True if post['action'] == "True" else False
|
||||
application.save()
|
||||
json['message'] = "Application was successfully {0}.".format(
|
||||
"accepted" if post['action'] == "True" else "denied")
|
||||
mcm_api.discord_mcm("{0}'s application (#{1}) was {2} by {3}".format(application.username,
|
||||
application.id,
|
||||
"accepted" if post['action'] == "True" else "denied",
|
||||
post['username']))
|
||||
mcm_api.plugin("accept" if post['action'] == "True" else "deny", application.username)
|
||||
else:
|
||||
json['status'] = False
|
||||
json['message'] = "No application found."
|
||||
return JsonResponse(json)
|
||||
elif "application_clear" == keyword:
|
||||
if Application.objects.filter(id=post['application_id']).exists():
|
||||
application = Application.objects.get(id=post['application_id'])
|
||||
if application.accepted is True:
|
||||
json['status'] = False
|
||||
json['message'] = "An accepted application can't be cleared."
|
||||
else:
|
||||
application.accepted = None
|
||||
application.save()
|
||||
json['message'] = "Application was successfully cleared."
|
||||
else:
|
||||
json['status'] = False
|
||||
json['message'] = "No application found."
|
||||
return JsonResponse(json)
|
||||
elif "login" == keyword:
|
||||
player = Player.objects.filter(uuid=post['uuid']).exists()
|
||||
new_player = False
|
||||
if not player:
|
||||
player = Player(uuid=post['uuid'], username=post['username'])
|
||||
new_player = True
|
||||
player.first_seen = timezone.now().strftime("%Y-%m-%d")
|
||||
test_app = Application.objects.filter(username__iexact=post['username']).exists()
|
||||
if test_app:
|
||||
test_app = Application.objects.get(username__iexact=post['username'])
|
||||
player.application = test_app
|
||||
player.save()
|
||||
else:
|
||||
player = Player.objects.get(uuid=post['uuid'])
|
||||
if player.username != post['username']:
|
||||
user = User.objects.filter(username__iexact=player.username).exists()
|
||||
if user:
|
||||
user = User.objects.get(username__iexact=player.username)
|
||||
user.username = post['username'].lower()
|
||||
user.save()
|
||||
mcm_api.create_alert("Name Change: {0} to {1}.".format(player.username, post['username']))
|
||||
player.username = post['username']
|
||||
if not player.application:
|
||||
test_app = Application.objects.filter(username__iexact=post['username']).exists()
|
||||
if test_app:
|
||||
test_app = Application.objects.get(username__iexact=player.username)
|
||||
player.application = test_app
|
||||
player.save()
|
||||
test_ip = IP.objects.filter(ip=post['ip'], player=player).exists()
|
||||
if not test_ip:
|
||||
ip = IP(ip=post['ip'], player=player)
|
||||
ip.save()
|
||||
else:
|
||||
ip = IP.objects.get(ip=post['ip'], player=player)
|
||||
player = Player.objects.get(uuid=post['uuid'])
|
||||
player.last_seen = timezone.now().strftime("%Y-%m-%d")
|
||||
player.save()
|
||||
if new_player and ip.associated:
|
||||
for assoc in ip.associated:
|
||||
if assoc.uuid is not player.uuid and assoc.is_banned:
|
||||
mcm_api.plugin("staff", "Server {0}'s IP matches the banned player {1}".format(player.username, assoc.username))
|
||||
mcm_api.discord_notification("{0}'s IP matches the banned player {1}".format(player.username, assoc.username), ping=True)
|
||||
json['status'] = True
|
||||
json['message'] = "Updated {0}".format(post['username'])
|
||||
elif "register" == keyword:
|
||||
player = Player.objects.get(uuid=post['uuid'])
|
||||
if player.auth_user:
|
||||
json['status'] = False
|
||||
json['message'] = "You are already registered. To change your password, contact an Admin."
|
||||
else:
|
||||
password = generate_password()
|
||||
user = User.objects.create_user(username=player.username.lower(), password=password)
|
||||
user.save()
|
||||
player.auth_user = user
|
||||
player.save()
|
||||
json['message'] = password
|
||||
elif "ticket" == keyword:
|
||||
player = Player.objects.get(uuid=post['uuid'])
|
||||
try:
|
||||
ticket = Ticket(player=player, message=post['message'], x=post['x'], y=post['y'], z=post['z'], world=post['world'])
|
||||
ticket.save()
|
||||
json['message'] = "Ticket submitted."
|
||||
link = "{}".format(mcm_utils.url_path(settings.MCM_BASE_LINK, 'dashboard/ticket', ticket.id))
|
||||
msg = mcm_utils.build_ticket(ticket, link)
|
||||
json['extra'] = {'id': ticket.id, 'link': link}
|
||||
mcm_api.discord_mcm(embeds=msg, ping=True)
|
||||
except:
|
||||
json['status'] = False
|
||||
json['message'] = "Error while submitting ticket."
|
||||
elif "warning" == keyword:
|
||||
player = Player.objects.get(uuid=post['player'])
|
||||
staff = Player.objects.get(uuid=post['staff'])
|
||||
try:
|
||||
warning = Warning(player=player, message=post['message'], severity=post['severity'], staff=staff.auth_user)
|
||||
warning.save()
|
||||
json['message'] = "Warning issued."
|
||||
link = "{}".format(mcm_utils.url_path(settings.MCM_BASE_LINK, 'dashboard/warning', warning.id))
|
||||
msg = mcm_utils.build_warning(warning, link)
|
||||
mcm_api.discord_mcm(embeds=msg)
|
||||
except Exception as ex:
|
||||
json['status'] = False
|
||||
json['message'] = "Error while issuing warning."
|
||||
return JsonResponse(json)
|
||||
|
||||
|
||||
class FormAPI(View):
|
||||
|
||||
def get(self, request, request_model):
|
||||
html = ""
|
||||
if request_allowed(request):
|
||||
get = request.GET
|
||||
model = None
|
||||
for m in apps.get_app_config('minecraft_manager').get_models():
|
||||
if m._meta.model_name.upper() == request_model.upper():
|
||||
model = m
|
||||
break
|
||||
if model:
|
||||
form = None
|
||||
for modelform in MCMForms.__all__():
|
||||
if modelform.Meta.model == model:
|
||||
form = modelform()
|
||||
break
|
||||
if not form:
|
||||
form = modelform_factory(model, exclude=('id',))()
|
||||
if 'as' in get:
|
||||
html = getattr(form, 'as_{0}'.format(get['as']))()
|
||||
else:
|
||||
html = form.as_p()
|
||||
return HttpResponse(html)
|
||||
|
||||
def post(self, request, request_model):
|
||||
html = ""
|
||||
if request_allowed(request):
|
||||
post = request.POST
|
||||
model = None
|
||||
for m in apps.get_app_config('minecraft_manager').get_models():
|
||||
if m._meta.model_name.upper() == request_model.upper():
|
||||
model = m
|
||||
break
|
||||
if model:
|
||||
form = None
|
||||
for modelform in MCMForms.__all__():
|
||||
if modelform.Meta.model == model:
|
||||
form = modelform(post)
|
||||
break
|
||||
if not form:
|
||||
form = modelform_factory(model, exclude=('id',))(post)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
html = "Saved"
|
||||
else:
|
||||
if 'as' in post:
|
||||
html = getattr(form, 'as_{0}'.format(post['as']))()
|
||||
else:
|
||||
html = form.as_p()
|
||||
return HttpResponse(html)
|
||||
|
||||
|
||||
class ModelAPI(View):
|
||||
|
||||
def get(self, request, request_model):
|
||||
json = []
|
||||
if request_allowed(request):
|
||||
get = request.GET
|
||||
model = None
|
||||
for m in apps.get_app_config('minecraft_manager').get_models():
|
||||
if m._meta.model_name.upper() == request_model.upper():
|
||||
model = m
|
||||
break
|
||||
if model:
|
||||
keywords = clean(model, get)
|
||||
objects = model.objects.filter(**keywords).values()
|
||||
json = []
|
||||
for value in objects:
|
||||
try:
|
||||
link = "{}".format(mcm_utils.url_path(settings.MCM_BASE_LINK, 'dashboard', request_model, value['id']))
|
||||
value['link'] = link
|
||||
except:
|
||||
pass
|
||||
json.append(value)
|
||||
return JsonResponse(json, safe=False)
|
||||
|
||||
def post(self, request, request_model):
|
||||
pass
|
||||
|
||||
|
||||
class StatsAPI(View):
|
||||
|
||||
def get(self, request):
|
||||
json = []
|
||||
if request_allowed(request):
|
||||
get = request.GET
|
||||
if 'stat' in get:
|
||||
if 'uuid' in get:
|
||||
json = mcm_stats.one_single(get['uuid'], get['stat'])
|
||||
elif 'username' in get and Player.objects.filter(username__iexact=get['username']).exists():
|
||||
uuid = Player.objects.get(username__iexact=get['username']).uuid
|
||||
json = mcm_stats.one_single(uuid, get['stat'])
|
||||
else:
|
||||
json = mcm_stats.top_ten_stat(get['stat'])
|
||||
elif 'uuid' in get:
|
||||
json = mcm_stats.top_ten_player(get['uuid'])
|
||||
elif 'username' in get:
|
||||
uuid = Player.objects.get(username__iexact=get['username']).uuid
|
||||
json = mcm_stats.top_ten_player(uuid)
|
||||
return JsonResponse(json, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
pass
|
|
@ -0,0 +1,18 @@
|
|||
import os, sys, django
|
||||
|
||||
sep = os.sep
|
||||
path = os.path.dirname(os.path.abspath(__file__))
|
||||
path = path.split(sep)[:-3]
|
||||
project = path[-1]
|
||||
path = sep.join(path)
|
||||
sys.path.append(path)
|
||||
print("Setting path for {0}: {1}".format(project, path))
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{}.settings".format(project))
|
||||
django.setup()
|
||||
|
||||
from django.conf import settings
|
||||
from minecraft_manager.api.bot import Discord
|
||||
|
||||
token = getattr(settings, 'DISCORD_BOT_TOKEN', None)
|
||||
bot = Discord(token)
|
||||
bot.run_bot()
|
|
@ -0,0 +1,16 @@
|
|||
import os, sys, django
|
||||
|
||||
sep = os.sep
|
||||
path = os.path.dirname(os.path.abspath(__file__))
|
||||
path = path.split(sep)[:-3]
|
||||
project = path[-1]
|
||||
path = sep.join(path)
|
||||
sys.path.append(path)
|
||||
print("Setting path for {0}: {1}".format(project, path))
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{}.settings".format(project))
|
||||
django.setup()
|
||||
|
||||
from minecraft_manager.api.bot import OreAlert
|
||||
|
||||
bot = OreAlert()
|
||||
bot.run_bot()
|
|
@ -0,0 +1,9 @@
|
|||
You will need to get this from your CP MySQL tables
|
||||
|
||||
NOTE: CoreProtect does not match up these IDs with the block's in-game ID, so air is not 0, stone is not 1, etc.
|
||||
|
||||
1. mysql -p <coreprotect database> -e "SELECT id, material FROM co_material_map" > blocks.txt
|
||||
2. Open blocks.txt
|
||||
3. Remove the header row
|
||||
4. Remove spaces/tabs and delimit with a ','
|
||||
e.g. 1,minecraft:air
|
|
@ -0,0 +1,55 @@
|
|||
-- Alerts
|
||||
insert into minecraft_manager_alert (select * from whitelist_alert);
|
||||
|
||||
-- Applications
|
||||
insert into minecraft_manager_application (select * from whitelist_application);
|
||||
|
||||
-- Players
|
||||
insert into minecraft_manager_player (uuid, username, application_id, auth_user_id, last_seen, first_seen)
|
||||
select wp.uuid, wp.username, (
|
||||
select mma.id from minecraft_manager_application mma where mma.username = (
|
||||
select wa.username from whitelist_application wa where wp.application_id = wa.id
|
||||
)
|
||||
), wp.auth_user_id, wp.last_seen, wp.first_seen from whitelist_player wp
|
||||
;
|
||||
|
||||
-- Tickets
|
||||
insert into minecraft_manager_ticket (message, priority, resolved, world, x, y, z, date, player_id, staff_id)
|
||||
select wt.message, wt.priority, wt.resolved, wt.world, wt.x, wt.y, wt.z, wt.date, (
|
||||
select mmp.id from minecraft_manager_player mmp where mmp.uuid = (
|
||||
select wp.uuid from whitelist_player wp where wp.id = wt.player_id
|
||||
)
|
||||
), (
|
||||
select au.id from auth_user au where au.username = (
|
||||
select wp2.username from whitelist_player wp2 where wp2.id = wt.staff_id
|
||||
)
|
||||
) from whitelist_ticket wt
|
||||
;
|
||||
|
||||
-- Warnings
|
||||
insert into minecraft_manager_warning (message, severity, date, player_id, staff_id)
|
||||
select ww.message, ww.severity, ww.date, (
|
||||
select mmp.id from minecraft_manager_player mmp where mmp.uuid = (
|
||||
select wp.uuid from whitelist_player wp where wp.id = ww.player_id
|
||||
)
|
||||
), (
|
||||
select au.id from auth_user au where au.username = (
|
||||
select wp2.username from whitelist_player wp2 where wp2.id = ww.staff_id
|
||||
)
|
||||
) from whitelist_warning ww
|
||||
;
|
||||
|
||||
-- User Settings
|
||||
insert into minecraft_manager_usersettings (default_results, default_theme, default_timezone, search_player_ip, show_timestamp_chat, last_ip, auth_user_id)
|
||||
select default_results, default_theme, default_timezone, search_player_ip, show_timestamp_chat, last_ip, auth_user_id from whitelist_usersettings wu
|
||||
;
|
||||
|
||||
-- Notes (This migration ONLY works if you are using standard whitelist app, aka only Tickets had notes)
|
||||
-- The ignore is because there were some incorrectly encoded characters giving MySQL a hard time
|
||||
insert ignore into minecraft_manager_note (ref_table, ref_id, message, last_update, author_id)
|
||||
select wn.ref_table, (
|
||||
select mmt.id from minecraft_manager_ticket mmt where mmt.message = (
|
||||
select wt.message from whitelist_ticket wt where wt.id = wn.ref_id
|
||||
)
|
||||
), wn.message, wn.last_update, wn.author_id from whitelist_note wn where (select count(*) from whitelist_ticket wt2 where wt2.id = wn.ref_id) > 0
|
||||
;
|
|
@ -0,0 +1,85 @@
|
|||
import os, json
|
||||
from minecraft_manager.models import Player
|
||||
from django.conf import settings
|
||||
|
||||
stats_dir = os.path.join(settings.MINECRAFT_BASE_DIR, getattr(settings, "WORLD", "world"), "stats")
|
||||
|
||||
def get_score(data):
|
||||
return data['score']
|
||||
|
||||
|
||||
def get_stats():
|
||||
stats = {}
|
||||
for filename in os.listdir(stats_dir):
|
||||
with open(stats_dir + "/" + filename) as json_file:
|
||||
j = json.load(json_file)['stats']
|
||||
uuid = filename.replace(".json", "")
|
||||
stats[uuid] = j
|
||||
return stats
|
||||
|
||||
|
||||
def get_names(stats=None, sort=False):
|
||||
names = []
|
||||
if not stats:
|
||||
stats = get_stats()
|
||||
for uuid in stats:
|
||||
for base in stats[uuid]:
|
||||
for stat in stats[uuid][base]:
|
||||
name = "{}.{}".format(base, stat)
|
||||
if name not in names:
|
||||
names.append(name)
|
||||
if sort:
|
||||
names = sorted(names)
|
||||
return names
|
||||
|
||||
|
||||
def top_ten_stat(stat):
|
||||
stats = get_stats()
|
||||
top_ten = []
|
||||
parts = stat.split('.')
|
||||
for s in stats:
|
||||
if parts[0] in stats[s]:
|
||||
top_ten.append({'uuid': s, 'score': stats[s][parts[0]][parts[1]] if parts[1] in stats[s][parts[0]] else 0})
|
||||
top_ten = sorted(top_ten, key=get_score, reverse=True)[:10]
|
||||
for idx, tt in enumerate(top_ten):
|
||||
uuid = tt['uuid']
|
||||
try:
|
||||
top_ten[idx]['username'] = Player.objects.get(uuid=uuid).username
|
||||
except:
|
||||
top_ten[idx]['username'] = uuid
|
||||
return top_ten
|
||||
|
||||
|
||||
def top_ten_player(uuid):
|
||||
stats = get_stats()
|
||||
names = get_names(stats)
|
||||
top_ten_list = {}
|
||||
for name in names:
|
||||
top_ten_list[name] = []
|
||||
parts = name.split('.')
|
||||
for stat in stats:
|
||||
bases = stats[stat]
|
||||
if parts[0] in bases:
|
||||
top_ten_list[name].append({'uuid': stat, 'score': bases[parts[0]][parts[1]] if parts[1] in bases[parts[0]] else 0})
|
||||
top_ten_list[name] = sorted(top_ten_list[name], key=get_score, reverse=True)
|
||||
top_ten = []
|
||||
for tt in top_ten_list:
|
||||
idx = 0
|
||||
for ttl in top_ten_list[tt]:
|
||||
if idx > 10:
|
||||
break
|
||||
if ttl['uuid'] == uuid:
|
||||
ttl['rank'] = idx+1
|
||||
ttl['stat'] = tt
|
||||
top_ten.append(ttl)
|
||||
break
|
||||
idx += 1
|
||||
return top_ten
|
||||
|
||||
|
||||
def one_single(uuid, stat):
|
||||
stats = get_stats()
|
||||
if uuid in stats:
|
||||
return [{'uuid': uuid, 'stat': stat, 'score': stats[uuid][stat]}]
|
||||
return 0
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
from django.conf.urls import url
|
||||
from django.contrib.auth.decorators import login_required
|
||||
import minecraft_manager.external.views as external
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^apply/$', external.Apply.as_view(), name="external-apply"),
|
||||
url(r'^ticket/$', external.Ticket.as_view(), name="external-ticket"),
|
||||
url(r'^stats/$', external.Stats.as_view(), name="external-stats"),
|
||||
url(r'^stats/player/$', external.StatsPlayer.as_view(), name="external-stats-player"),
|
||||
]
|
|
@ -0,0 +1,158 @@
|
|||
from django.views.generic import View
|
||||
from django.shortcuts import render, reverse, redirect
|
||||
from django.conf import settings
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from minecraft_manager.forms import ApplicationForm, TicketForm
|
||||
import minecraft_manager.api.api as mcm_api
|
||||
import minecraft_manager.utils as mcm_utils
|
||||
import minecraft_manager.external.stats as mcm_stats
|
||||
from minecraft_manager.models import Player
|
||||
import random, yaml, os, json, datetime, pytz
|
||||
|
||||
|
||||
def config():
|
||||
data = {}
|
||||
data['discord_invite'] = getattr(settings, "DISCORD_INVITE", "#")
|
||||
|
||||
dynmap_url = getattr(settings, "DYNMAP_URL", "")
|
||||
data['dynmap_url'] = dynmap_url
|
||||
|
||||
dynmap_static_url = getattr(settings, "DYNMAP_STATIC_URL", dynmap_url)
|
||||
data['dynmap_static_url'] = dynmap_static_url
|
||||
|
||||
dynmap_world = getattr(settings, "WORLD", "world")
|
||||
data['dynmap_world'] = dynmap_world
|
||||
|
||||
world_border = getattr(settings, "WORLD_BORDER", 100)
|
||||
data['world_border'] = world_border
|
||||
|
||||
data['target'] = "_blank" if dynmap_url else "_self"
|
||||
|
||||
x = random.randint(-world_border, world_border)
|
||||
data['x'] = x
|
||||
|
||||
z = random.randint(-world_border, world_border)
|
||||
data['z'] = z
|
||||
|
||||
uri = "?worldname={}&mapname=surface&zoom=5&x={}&y=64&z={}".format(dynmap_world, x, z)
|
||||
data['map_url'] = "{}{}".format(dynmap_url, uri)
|
||||
data['static_url'] = "{}{}".format(dynmap_static_url, uri)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def rules():
|
||||
path = os.path.join(settings.MINECRAFT_BASE_DIR, "plugins/MinecraftManager/config.yml")
|
||||
with open(path) as config_file:
|
||||
config = yaml.load(config_file)
|
||||
data = config['rules']['rules']
|
||||
if config['rules']['application']['validate']:
|
||||
data.append("The answer to the final question is \"{}\"".format(config['rules']['application']['answer']))
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name='dispatch')
|
||||
class Apply(View):
|
||||
|
||||
def get(self, request):
|
||||
form = ApplicationForm()
|
||||
return render(request, 'minecraft_manager/external/apply.html',
|
||||
{'form': form.as_p(), 'rules': rules(), 'valid': False, 'map': config(),
|
||||
'captcha': hasattr(settings, "CAPTCHA_SECRET")})
|
||||
|
||||
def post(self, request):
|
||||
form = ApplicationForm(request.POST)
|
||||
valid_username = mcm_utils.validate_username(form.data['username'])
|
||||
captcha = mcm_utils.Captcha(request.POST)
|
||||
valid = form.is_valid()
|
||||
if valid and valid_username and captcha.success:
|
||||
app = form.save()
|
||||
msg = mcm_utils.build_application(app)
|
||||
mcm_api.discord_mcm(message='New Application!', embeds=msg)
|
||||
mcm_api.plugin("application", "{0} {1}".format(form.data['username'], app.id))
|
||||
else:
|
||||
for error in captcha.errors:
|
||||
form.add_error(None, error)
|
||||
if not valid_username:
|
||||
form.add_error(None, "That username is not a premium Minecraft account")
|
||||
return render(request, 'minecraft_manager/external/apply.html',
|
||||
{'form': form.as_p(), 'rules': rules(), 'valid': valid and valid_username and captcha.success, 'map': config(),
|
||||
'captcha': hasattr(settings, "CAPTCHA_SECRET")})
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name='dispatch')
|
||||
class Ticket(View):
|
||||
|
||||
def get(self, request):
|
||||
form = TicketForm()
|
||||
return render(request, 'minecraft_manager/external/ticket.html',
|
||||
{'form': form.as_p(), 'valid': False, 'map': config(),
|
||||
'captcha': hasattr(settings, "CAPTCHA_SECRET")})
|
||||
|
||||
def post(self, request):
|
||||
post = request.POST.copy()
|
||||
username = post['player']
|
||||
if 'player' in post and Player.objects.filter(username__iexact=post['player']).exists():
|
||||
player = Player.objects.get(username__iexact=post['player'])
|
||||
post.update({'player': player.id})
|
||||
form = TicketForm(post)
|
||||
captcha = mcm_utils.Captcha(request.POST)
|
||||
valid = form.is_valid()
|
||||
if 'player' not in post:
|
||||
# Why did I make Player nullable???
|
||||
valid = False
|
||||
form.add_error(None, "You must enter your IGN")
|
||||
if valid and captcha.success:
|
||||
ticket = form.save()
|
||||
# Create the message to send to Discord
|
||||
link = "{}".format(mcm_utils.url_path(settings.MCM_BASE_LINK, 'dashboard/ticket', ticket.id))
|
||||
msg = mcm_utils.build_ticket(ticket, link)
|
||||
mcm_api.discord_mcm(message="New Ticket", embeds=msg, ping=True)
|
||||
mcm_api.plugin("ticket", "{0} {1} {2}".format(username, ticket.id, link))
|
||||
else:
|
||||
for error in captcha.errors:
|
||||
form.add_error(None, error)
|
||||
if 'player' in form.errors:
|
||||
form.errors['player'][0] = 'You must enter your IGN.'
|
||||
form.data = form.data.copy()
|
||||
form.data['player'] = username
|
||||
return render(request, 'minecraft_manager/external/ticket.html',
|
||||
{'form': form.as_p(), 'valid': valid and captcha.success, 'map': config(),
|
||||
'captcha': hasattr(settings, "CAPTCHA_SECRET")})
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name='dispatch')
|
||||
class Stats(View):
|
||||
|
||||
def get(self, request):
|
||||
get = request.GET
|
||||
results = None
|
||||
if 'stat' in get and get['stat']:
|
||||
results = mcm_stats.top_ten_stat(get['stat'])
|
||||
return render(request, 'minecraft_manager/external/stats_all.html', {'map': config(), 'stats': mcm_stats.get_names(sort=True), 'results': results})
|
||||
|
||||
def post(self, request):
|
||||
pass
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name='dispatch')
|
||||
class StatsPlayer(View):
|
||||
|
||||
def get(self, request):
|
||||
get = request.GET
|
||||
results = None
|
||||
if 'username' in get and get['username']:
|
||||
if Player.objects.filter(username__iexact=get['username']).exists():
|
||||
uuid = Player.objects.get(username__iexact=get['username']).uuid
|
||||
results = mcm_stats.top_ten_player(uuid)
|
||||
if 'uuid' in get and get['uuid']:
|
||||
results = mcm_stats.top_ten_player(get['uuid'])
|
||||
return render(request, 'minecraft_manager/external/stats_single.html', {'map': config(), 'results': results})
|
||||
|
||||
def post(self, request):
|
||||
pass
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
from django.forms import ModelForm, Textarea, HiddenInput, TextInput
|
||||
from minecraft_manager.models import UserSettings, Application, Alert, Ticket, Warning, Note
|
||||
|
||||
|
||||
def __all__():
|
||||
return [UserSettingsForm, ApplicationForm, AlertForm, TicketForm, WarningForm, NoteForm]
|
||||
|
||||
|
||||
class UserSettingsForm(ModelForm):
|
||||
class Meta:
|
||||
model = UserSettings
|
||||
fields = ['default_results', 'default_theme', 'default_timezone', 'search_player_ip', 'show_timestamp_chat']
|
||||
|
||||
|
||||
class ApplicationForm(ModelForm):
|
||||
class Meta:
|
||||
model = Application
|
||||
fields = ['username', 'age', 'player_type', 'ever_banned', 'ever_banned_explanation', 'reference', 'read_rules']
|
||||
|
||||
|
||||
class AlertForm(ModelForm):
|
||||
class Meta:
|
||||
model = Alert
|
||||
fields = ['message']
|
||||
|
||||
|
||||
class TicketForm(ModelForm):
|
||||
class Meta:
|
||||
model = Ticket
|
||||
fields = ['player', 'message', 'priority', 'world', 'x', 'y', 'z']
|
||||
widgets = {
|
||||
'player': TextInput,
|
||||
'message': Textarea,
|
||||
}
|
||||
|
||||
|
||||
class WarningForm(ModelForm):
|
||||
class Meta:
|
||||
model = Warning
|
||||
fields = ['player', 'message', 'severity']
|
||||
widgets = {
|
||||
'message': Textarea
|
||||
}
|
||||
|
||||
|
||||
class NoteForm(ModelForm):
|
||||
class Meta:
|
||||
model = Note
|
||||
fields = ['ref_id', 'ref_table', 'message']
|
||||
widgets = {
|
||||
'ref_id': HiddenInput,
|
||||
'ref_table': HiddenInput
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
import pytz
|
||||
|
||||
from django.utils import timezone
|
||||
from django.utils.deprecation import MiddlewareMixin
|
||||
|
||||
|
||||
class TimezoneMiddleware(MiddlewareMixin):
|
||||
def process_request(self, request):
|
||||
tzname = request.session.get('django_timezone')
|
||||
if tzname:
|
||||
timezone.activate(pytz.timezone(tzname))
|
||||
else:
|
||||
try:
|
||||
timezone.activate(pytz.timezone(request.user.usersettings.default_timezone))
|
||||
except:
|
||||
timezone.deactivate()
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,21 @@
|
|||
# Generated by Django 2.0.5 on 2018-05-31 03:23
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('minecraft_manager', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='player',
|
||||
name='rank',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='ticket',
|
||||
name='type',
|
||||
),
|
||||
]
|
|
@ -0,0 +1,29 @@
|
|||
# Generated by Django 2.0.5 on 2018-05-31 03:35
|
||||
|
||||
import django.contrib.auth.models
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('auth', '0009_alter_user_last_name_max_length'),
|
||||
('minecraft_manager', '0002_auto_20180530_2223'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='MinecraftManagerUser',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'permissions': (('coreprotect_partial', 'Can use CoreProtect GUI except Command/Chat searches'), ('coreprotect_full', 'Can use full CoreProtect GUI'), ('bots', 'Can use the bot control page'), ('chat', 'Can use chat page')),
|
||||
'proxy': True,
|
||||
'indexes': [],
|
||||
},
|
||||
bases=('auth.user',),
|
||||
managers=[
|
||||
('objects', django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 2.0.5 on 2018-05-31 03:41
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('minecraft_manager', '0003_minecraftmanageruser'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='minecraftmanageruser',
|
||||
options={'permissions': (('coreprotect_partial', 'Can use CoreProtect GUI except Command/Chat searches'), ('coreprotect_full', 'Can use full CoreProtect GUI'), ('coreprotect_activity', 'Can use CoreProtect Activity Monitor'), ('bots', 'Can use the bot control page'), ('chat', 'Can use chat page'))},
|
||||
),
|
||||
]
|
|
@ -0,0 +1,20 @@
|
|||
# Generated by Django 2.0.5 on 2018-06-02 04:11
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('minecraft_manager', '0004_auto_20180530_2241'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='ticket',
|
||||
name='staff',
|
||||
field=models.ForeignKey(blank=True, limit_choices_to=models.Q(is_active=True), null=True, on_delete=django.db.models.deletion.CASCADE, related_name='ticket_staff', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,20 @@
|
|||
# Generated by Django 2.0.5 on 2018-06-02 04:12
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('minecraft_manager', '0005_auto_20180601_2311'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='warning',
|
||||
name='staff',
|
||||
field=models.ForeignKey(blank=True, limit_choices_to=models.Q(is_active=True), null=True, on_delete=django.db.models.deletion.CASCADE, related_name='warning_staff', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.0.5 on 2018-07-25 03:18
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('minecraft_manager', '0006_auto_20180601_2312'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='ticket',
|
||||
name='priority',
|
||||
field=models.CharField(blank=True, choices=[('L', 'Low'), ('M', 'Medium'), ('H', 'High')], default='L', max_length=1),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,20 @@
|
|||
# Generated by Django 2.0.5 on 2018-08-20 19:06
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('minecraft_manager', '0007_auto_20180724_2218'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='ticket',
|
||||
name='staff',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='ticket_staff', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,438 @@
|
|||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.db.models import Q
|
||||
import logging, yaml, pytz, json, os
|
||||
from django.conf import settings
|
||||
from datetime import datetime
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MinecraftManagerUser(User):
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
||||
permissions = (
|
||||
('coreprotect_partial', 'Can use CoreProtect GUI except Command/Chat searches'),
|
||||
('coreprotect_full', 'Can use full CoreProtect GUI'),
|
||||
('coreprotect_activity', 'Can use CoreProtect Activity Monitor'),
|
||||
('bots', 'Can use the bot control page'),
|
||||
('chat', 'Can use chat page'),
|
||||
)
|
||||
|
||||
|
||||
class UserSettings(models.Model):
|
||||
RESULT_OPTIONS = (
|
||||
(10, '10'),
|
||||
(25, '25'),
|
||||
(50, '50'),
|
||||
(100, '100'),
|
||||
(-1, 'All')
|
||||
)
|
||||
THEME_OPTIONS = (
|
||||
('DE', 'Default'),
|
||||
('DA', 'Dark'),
|
||||
('SO', 'Solar'),
|
||||
('SL', 'Slate')
|
||||
)
|
||||
tzs = []
|
||||
for tz in pytz.common_timezones:
|
||||
tzs.append((tz, tz))
|
||||
TIMEZONES = tuple(tzs)
|
||||
auth_user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True)
|
||||
default_results = models.SmallIntegerField("Default Results", default=10, choices=RESULT_OPTIONS)
|
||||
default_theme = models.CharField("Theme", max_length=2, default='DE', choices=THEME_OPTIONS)
|
||||
default_timezone = models.CharField("Timezone", max_length=20, default='UTC', choices=TIMEZONES)
|
||||
search_player_ip = models.BooleanField("Include IP in Player search", default=False)
|
||||
show_timestamp_chat = models.BooleanField("Show Timestamp By Chat", default=False)
|
||||
last_ip = models.CharField(max_length=30, default="127.0.0.1", editable=False)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "User Settings"
|
||||
verbose_name_plural = "User Settings"
|
||||
|
||||
def __str__(self):
|
||||
return "User Settings : %s" % self.auth_user.username
|
||||
|
||||
def __repr__(self):
|
||||
return self.auth_user.username
|
||||
|
||||
|
||||
class UnansweredManager(models.Manager):
|
||||
def get_queryset(self):
|
||||
return super(UnansweredManager, self).get_queryset().filter(accepted=None)
|
||||
|
||||
|
||||
class Application(models.Model):
|
||||
username = models.CharField("Minecraft Username", max_length=20, unique=True)
|
||||
age = models.PositiveSmallIntegerField()
|
||||
player_type = models.TextField("What type of player are you?", max_length=300)
|
||||
ever_banned = models.BooleanField("Have you ever been banned?", default=False)
|
||||
ever_banned_explanation = models.TextField("If you were previously banned, will you share why?", max_length=300, blank=True, null=True)
|
||||
reference = models.CharField("Were you referred to our server?", max_length=50, blank=True, null=True)
|
||||
read_rules = models.CharField("Have you read the rules?", max_length=10)
|
||||
accepted = models.NullBooleanField()
|
||||
date = models.DateTimeField(auto_now_add=True, blank=True, null=True)
|
||||
objects = models.Manager()
|
||||
unanswered = UnansweredManager()
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
if self.accepted is not None:
|
||||
if self.accepted is True:
|
||||
return "Accepted"
|
||||
elif self.accepted is False:
|
||||
return "Denied"
|
||||
else:
|
||||
return "Unknown"
|
||||
else:
|
||||
return "Unanswered"
|
||||
|
||||
@property
|
||||
def date_display(self):
|
||||
return str(self.date).split(".")[0]
|
||||
|
||||
@property
|
||||
def json(self):
|
||||
return {'id': self.id, 'username': self.username, 'age': self.age, 'player_type': self.player_type,
|
||||
'ever_banned': self.ever_banned, 'ever_banned_explanation': self.ever_banned_explanation,
|
||||
'reference': self.reference, 'read_rules': self.read_rules, 'status': self.status,
|
||||
'date': self.date_display}
|
||||
|
||||
@property
|
||||
def skript(self):
|
||||
return "{0}<:>{1}<:>{2}<:>{3}<:>{4}<:>{5}<:>{6}<:>{7}<:>{8}<:>{9}<:>{10}".format(
|
||||
"success", self.id, self.username, self.age, self.player_type, self.ever_banned,
|
||||
self.ever_banned_explanation, self.reference, self.read_rules, self.status, self.date_display
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return "Application: %s" % self.username
|
||||
|
||||
def __repr__(self):
|
||||
return self.username
|
||||
|
||||
|
||||
class Player(models.Model):
|
||||
auth_user = models.OneToOneField(User, on_delete=models.DO_NOTHING, null=True, blank=True)
|
||||
uuid = models.CharField(max_length=36, unique=True)
|
||||
username = models.CharField(max_length=20)
|
||||
application = models.ForeignKey(Application, on_delete=models.CASCADE, null=True, blank=True)
|
||||
first_seen = models.DateField(null=True, blank=True)
|
||||
last_seen = models.DateField(null=True, blank=True)
|
||||
|
||||
|
||||
@property
|
||||
def is_banned(self):
|
||||
ban_file = os.path.join(settings.MINECRAFT_BASE_DIR, 'banned-players.json')
|
||||
with open(ban_file, encoding='utf-8') as f:
|
||||
bans = json.load(f)
|
||||
if bans:
|
||||
for ban in bans:
|
||||
if self.uuid == ban['uuid']:
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
@property
|
||||
def donor_status(self):
|
||||
|
||||
def format_time(timestamp):
|
||||
return datetime.utcfromtimestamp(timestamp).strftime('%m/%d/%Y')
|
||||
|
||||
try:
|
||||
from django_luckperms.models import LuckPermsPlayerPermissions as lppp
|
||||
donator = lppp.objects.filter(permission='group.donator', uuid=self.uuid)
|
||||
if donator.exists():
|
||||
expiry = donator.first().expiry
|
||||
if expiry > 0:
|
||||
return "Until {}".format(format_time(expiry))
|
||||
else:
|
||||
return "Permanent"
|
||||
else:
|
||||
return "None"
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
pex_file = os.path.join(getattr(settings, 'MINECRAFT_BASE_DIR', ''), 'plugins/PermissionsEx/permissions.yml')
|
||||
with open(pex_file) as f:
|
||||
yml = yaml.load(f)
|
||||
for user in yml['users']:
|
||||
if user == self.uuid:
|
||||
if 'donator' in yml['users'][user]['group']:
|
||||
u = yml['users'][user]
|
||||
if 'group-donator-until' in u['options']:
|
||||
unix_time = float(u['options']['group-donator-until'])
|
||||
return 'Until {0}'.format(format_time(unix_time))
|
||||
else:
|
||||
return 'Permanent'
|
||||
else:
|
||||
return 'None'
|
||||
except:
|
||||
pass
|
||||
return "N/A"
|
||||
|
||||
@property
|
||||
def ips(self):
|
||||
ips = []
|
||||
query = IP.objects.filter(player=self)
|
||||
for q in query:
|
||||
ips.append(q.ip)
|
||||
return " ".join(ips)
|
||||
|
||||
def __str__(self):
|
||||
return self.username + " (" + self.uuid + ")"
|
||||
|
||||
|
||||
class UnclaimedManager(models.Manager):
|
||||
def get_queryset(self):
|
||||
return super(UnclaimedManager, self).get_queryset().filter(staff=None, resolved=False)
|
||||
|
||||
|
||||
class ClaimedManager(models.Manager):
|
||||
def get_queryset(self):
|
||||
return super(ClaimedManager, self).get_queryset().filter(staff__isnull=False, resolved=False)
|
||||
|
||||
|
||||
class Ticket(models.Model):
|
||||
PRIORITY = (
|
||||
('L', 'Low'),
|
||||
('M', 'Medium'),
|
||||
('H', 'High')
|
||||
)
|
||||
WORLDS = (
|
||||
('O', 'Overworld'),
|
||||
('N', 'Nether'),
|
||||
('E', 'The End')
|
||||
)
|
||||
player = models.ForeignKey(Player, related_name="ticket_player", on_delete=models.CASCADE, blank=True, null=True)
|
||||
message = models.CharField(max_length=500)
|
||||
priority = models.CharField(max_length=1, choices=PRIORITY, blank=True, default='L')
|
||||
staff = models.ForeignKey(User, related_name="ticket_staff", on_delete=models.CASCADE, null=True, blank=True)
|
||||
resolved = models.BooleanField(default=False)
|
||||
world = models.CharField(max_length=1, choices=WORLDS, blank=True, null=True)
|
||||
x = models.CharField(max_length=20, blank=True, null=True)
|
||||
y = models.CharField(max_length=20, blank=True, null=True)
|
||||
z = models.CharField(max_length=20, blank=True, null=True)
|
||||
date = models.DateTimeField(auto_now_add=True, null=True, blank=True)
|
||||
objects = models.Manager()
|
||||
unclaimed = UnclaimedManager()
|
||||
claimed = ClaimedManager()
|
||||
|
||||
@property
|
||||
def location(self):
|
||||
return "{0}, {1}, {2} in {3}".format(self.x, self.y, self.z, self.world_label)
|
||||
|
||||
@property
|
||||
def world_label(self):
|
||||
for W in self.WORLDS:
|
||||
if W[0] == self.world:
|
||||
return W[1]
|
||||
return ""
|
||||
|
||||
@property
|
||||
def resolved_label(self):
|
||||
if self.resolved:
|
||||
return "Resolved"
|
||||
else:
|
||||
return "Unresolved"
|
||||
|
||||
@property
|
||||
def snippet(self):
|
||||
if len(self.message) > 50:
|
||||
return self.message[:50] + "..."
|
||||
else:
|
||||
return self.message
|
||||
|
||||
@property
|
||||
def issuer(self):
|
||||
if self.player:
|
||||
pl = Player.objects.get(id=self.player_id)
|
||||
return pl.username
|
||||
else:
|
||||
return "Unknown"
|
||||
|
||||
@property
|
||||
def claimed_by(self):
|
||||
if self.staff:
|
||||
pl = User.objects.get(id=self.staff_id)
|
||||
return pl.username
|
||||
else:
|
||||
return "Unclaimed"
|
||||
|
||||
@property
|
||||
def is_claimed(self):
|
||||
if self.staff:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@property
|
||||
def priority_display(self):
|
||||
for P in self.PRIORITY:
|
||||
if P[0] == self.priority:
|
||||
return P[1]
|
||||
return "Unknown"
|
||||
|
||||
@staticmethod
|
||||
def priority_code_to_display(piority_code):
|
||||
for P in Ticket.PRIORITY:
|
||||
if P[0] == piority_code:
|
||||
return P[1]
|
||||
return "Unknown"
|
||||
|
||||
@property
|
||||
def date_display(self):
|
||||
return str(self.date).split(".")[0]
|
||||
|
||||
def __str__(self):
|
||||
username = "Unknown"
|
||||
try:
|
||||
pl = Player.objects.get(id=self.player_id)
|
||||
username = pl.username
|
||||
except:
|
||||
pass
|
||||
return "Ticket from %s" % username
|
||||
|
||||
|
||||
class Warning(models.Model):
|
||||
SEVERITY = (
|
||||
('L', 'Low'),
|
||||
('M', 'Medium'),
|
||||
('H', 'High')
|
||||
)
|
||||
player = models.ForeignKey(Player, related_name="warning_player", on_delete=models.CASCADE)
|
||||
message = models.CharField(max_length=200)
|
||||
severity = models.CharField(max_length=1, choices=SEVERITY)
|
||||
staff = models.ForeignKey(User, related_name="warning_staff", on_delete=models.CASCADE, null=True, blank=True, limit_choices_to=Q(is_active=True))
|
||||
date = models.DateTimeField(auto_now_add=True, blank=True, null=True)
|
||||
|
||||
@property
|
||||
def snippet(self):
|
||||
if len(self.message) > 50:
|
||||
return self.message[:50] + "..."
|
||||
else:
|
||||
return self.message
|
||||
|
||||
@property
|
||||
def severity_display(self):
|
||||
for S in self.SEVERITY:
|
||||
if S[0] == self.severity:
|
||||
return S[1]
|
||||
return "Unknown"
|
||||
|
||||
@staticmethod
|
||||
def severity_code_to_display(severity_code):
|
||||
for S in Warning.SEVERITY:
|
||||
if S[0] == severity_code:
|
||||
return S[1]
|
||||
return "Unknown"
|
||||
|
||||
@staticmethod
|
||||
def resolve(severity):
|
||||
for S in Warning.SEVERITY:
|
||||
if severity.lower() in (S[0].lower(), S[1].lower()):
|
||||
return S[0]
|
||||
return 'L'
|
||||
|
||||
@property
|
||||
def issuer(self):
|
||||
if self.staff:
|
||||
pl = User.objects.get(id=self.staff_id)
|
||||
return pl.username
|
||||
else:
|
||||
return "System"
|
||||
|
||||
@property
|
||||
def issuee(self):
|
||||
if self.player:
|
||||
pl = Player.objects.get(id=self.player_id)
|
||||
return pl.username
|
||||
else:
|
||||
return "Unknown"
|
||||
|
||||
@property
|
||||
def date_display(self):
|
||||
return str(self.date).split(".")[0]
|
||||
|
||||
def __str__(self):
|
||||
username = "Unknown"
|
||||
try:
|
||||
pl = Player.objects.get(id=self.player_id)
|
||||
username = pl.username
|
||||
except:
|
||||
pass
|
||||
return "Warning for %s" % username
|
||||
|
||||
|
||||
class IP(models.Model):
|
||||
player = models.ForeignKey(Player, on_delete=models.CASCADE)
|
||||
ip = models.CharField(max_length=30)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "IP"
|
||||
verbose_name_plural = "IPs"
|
||||
|
||||
@property
|
||||
def associated(self):
|
||||
ips = IP.objects.filter(ip=self.ip)
|
||||
players = []
|
||||
for ip in ips:
|
||||
if self.player != ip.player:
|
||||
players.append(ip.player)
|
||||
if players:
|
||||
return players
|
||||
else:
|
||||
return None
|
||||
|
||||
def __str__(self):
|
||||
player = Player.objects.get(id=self.player_id)
|
||||
return player.username + " - " + self.ip
|
||||
|
||||
|
||||
class Alert(models.Model):
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
message = models.TextField(max_length=1000)
|
||||
seen = models.BooleanField(default=False)
|
||||
date = models.DateTimeField(auto_now_add=True, blank=True, null=True)
|
||||
|
||||
@property
|
||||
def snippet(self):
|
||||
if len(self.message) > 50:
|
||||
return self.message[:50] + "..."
|
||||
else:
|
||||
return self.message
|
||||
|
||||
def __str__(self):
|
||||
return "Alert for %s" % self.user.username
|
||||
|
||||
|
||||
class Note(models.Model):
|
||||
NOTABLE = (
|
||||
('PL', 'Player'),
|
||||
('TI', 'Ticket'),
|
||||
('WA', 'Warning')
|
||||
)
|
||||
author = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
ref_table = models.CharField(max_length=2, choices=NOTABLE)
|
||||
ref_id = models.CharField(max_length=4)
|
||||
message = models.TextField(max_length=1000)
|
||||
last_update = models.DateTimeField(auto_now_add=True, blank=True, null=True)
|
||||
date = models.DateTimeField(auto_now_add=True, blank=True, null=True)
|
||||
|
||||
@property
|
||||
def ref_model(self):
|
||||
if self.ref_table == 'PL':
|
||||
return Player
|
||||
elif self.ref_table == 'TI':
|
||||
return Ticket
|
||||
elif self.ref_table == 'WA':
|
||||
return Warning
|
||||
|
||||
def __str__(self):
|
||||
ref = self.ref_model.objects.get(id=self.ref_id)
|
||||
return "Note: {0}".format(ref)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
jQuery.fn.filterByText = function(textbox) {
|
||||
return this.each(function() {
|
||||
var select = this;
|
||||
var options = [];
|
||||
$(select).find('option').each(function() {
|
||||
options.push({value: $(this).val(), text: $(this).text()});
|
||||
});
|
||||
$(select).data('options', options);
|
||||
|
||||
$(textbox).bind('change keyup', function() {
|
||||
var options = $(select).empty().data('options');
|
||||
var search = $.trim($(this).val());
|
||||
var regex = new RegExp(search,"gi");
|
||||
|
||||
$.each(options, function(i) {
|
||||
var option = options[i];
|
||||
if(option.text.match(regex) !== null) {
|
||||
$(select).append(
|
||||
$('<option>').text(option.text).val(option.value)
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
Binary file not shown.
After Width: | Height: | Size: 802 KiB |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Base structure
|
||||
*/
|
||||
|
||||
/* Move down content because we have a fixed navbar that is 50px tall */
|
||||
body {
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
.popup {
|
||||
opacity: .9;
|
||||
position: absolute;
|
||||
right: 3em;
|
||||
width: 20em;
|
||||
}
|
||||
|
||||
/*
|
||||
* Global add-ons
|
||||
*/
|
||||
|
||||
.sub-header {
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
/*
|
||||
* Top navigation
|
||||
* Hide default border to remove 1px line.
|
||||
*/
|
||||
.navbar-fixed-top {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
cursor: default !important;
|
||||
}
|
||||
.navbar-brand:hover {
|
||||
cursor: default !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sidebar
|
||||
*/
|
||||
|
||||
/* Hide for mobile, show later */
|
||||
.sidebar {
|
||||
display: none;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 51px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
display: block;
|
||||
padding: 20px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
|
||||
background-color: #222222;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sidebar navigation */
|
||||
.nav-sidebar {
|
||||
margin-right: -21px; /* 20px padding + 1px border */
|
||||
margin-bottom: 20px;
|
||||
margin-left: -20px;
|
||||
}
|
||||
.nav-sidebar > li > a {
|
||||
padding-right: 20px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.nav-sidebar > .active > a,
|
||||
.nav-sidebar > .active > a:hover,
|
||||
.nav-sidebar > .active > a:focus {
|
||||
color: #fff;
|
||||
background-color: #428bca;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Main content
|
||||
*/
|
||||
|
||||
.main {
|
||||
padding: 20px;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.main {
|
||||
padding-right: 40px;
|
||||
padding-left: 40px;
|
||||
}
|
||||
}
|
||||
.main .page-header {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
|
||||
tr[data-id] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*
|
||||
* Placeholder dashboard ideas
|
||||
*/
|
||||
|
||||
.placeholders {
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
.placeholders h4 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.placeholder {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.placeholder img {
|
||||
display: inline-block;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
select {
|
||||
background-color: grey;
|
||||
}
|
||||
|
||||
option {
|
||||
background-color: grey;
|
||||
}
|
||||
|
||||
.well {
|
||||
color: black;
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Base structure
|
||||
*/
|
||||
|
||||
/* Move down content because we have a fixed navbar that is 50px tall */
|
||||
body {
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
.popup {
|
||||
opacity: .9;
|
||||
position: absolute;
|
||||
right: 3em;
|
||||
width: 20em;
|
||||
}
|
||||
|
||||
/*
|
||||
* Global add-ons
|
||||
*/
|
||||
|
||||
.sub-header {
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
/*
|
||||
* Top navigation
|
||||
* Hide default border to remove 1px line.
|
||||
*/
|
||||
.navbar-fixed-top {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
cursor: default !important;
|
||||
}
|
||||
.navbar-brand:hover {
|
||||
cursor: default !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sidebar
|
||||
*/
|
||||
|
||||
/* Hide for mobile, show later */
|
||||
.sidebar {
|
||||
display: none;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 51px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
display: block;
|
||||
padding: 20px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
|
||||
background-color: #272b30;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sidebar navigation */
|
||||
.nav-sidebar {
|
||||
margin-right: -21px; /* 20px padding + 1px border */
|
||||
margin-bottom: 20px;
|
||||
margin-left: -20px;
|
||||
}
|
||||
.nav-sidebar > li > a {
|
||||
padding-right: 20px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.nav-sidebar > .active > a,
|
||||
.nav-sidebar > .active > a:hover,
|
||||
.nav-sidebar > .active > a:focus {
|
||||
color: #fff;
|
||||
background-color: #428bca;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Main content
|
||||
*/
|
||||
|
||||
.main {
|
||||
padding: 20px;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.main {
|
||||
padding-right: 40px;
|
||||
padding-left: 40px;
|
||||
}
|
||||
}
|
||||
.main .page-header {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
|
||||
tr[data-id] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*
|
||||
* Placeholder dashboard ideas
|
||||
*/
|
||||
|
||||
.placeholders {
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
.placeholders h4 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.placeholder {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.placeholder img {
|
||||
display: inline-block;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
select {
|
||||
background-color: grey;
|
||||
}
|
||||
|
||||
option {
|
||||
background-color: grey;
|
||||
}
|
||||
|
||||
.well {
|
||||
color: black;
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Base structure
|
||||
*/
|
||||
|
||||
/* Move down content because we have a fixed navbar that is 50px tall */
|
||||
body {
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
.popup {
|
||||
opacity: .9;
|
||||
position: absolute;
|
||||
right: 3em;
|
||||
width: 20em;
|
||||
}
|
||||
|
||||
/*
|
||||
* Global add-ons
|
||||
*/
|
||||
|
||||
.sub-header {
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
/*
|
||||
* Top navigation
|
||||
* Hide default border to remove 1px line.
|
||||
*/
|
||||
.navbar-fixed-top {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
cursor: default !important;
|
||||
}
|
||||
.navbar-brand:hover {
|
||||
cursor: default !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sidebar
|
||||
*/
|
||||
|
||||
/* Hide for mobile, show later */
|
||||
.sidebar {
|
||||
display: none;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 51px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
display: block;
|
||||
padding: 20px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
|
||||
background-color: #002b36;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sidebar navigation */
|
||||
.nav-sidebar {
|
||||
margin-right: -21px; /* 20px padding + 1px border */
|
||||
margin-bottom: 20px;
|
||||
margin-left: -20px;
|
||||
}
|
||||
.nav-sidebar > li > a {
|
||||
padding-right: 20px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.nav-sidebar > .active > a,
|
||||
.nav-sidebar > .active > a:hover,
|
||||
.nav-sidebar > .active > a:focus {
|
||||
color: #fff;
|
||||
background-color: #428bca;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Main content
|
||||
*/
|
||||
|
||||
.main {
|
||||
padding: 20px;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.main {
|
||||
padding-right: 40px;
|
||||
padding-left: 40px;
|
||||
}
|
||||
}
|
||||
.main .page-header {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
|
||||
tr[data-id] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*
|
||||
* Placeholder dashboard ideas
|
||||
*/
|
||||
|
||||
.placeholders {
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
.placeholders h4 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.placeholder {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.placeholder img {
|
||||
display: inline-block;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.well {
|
||||
color: black;
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Base structure
|
||||
*/
|
||||
|
||||
/* Move down content because we have a fixed navbar that is 50px tall */
|
||||
body {
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
.popup {
|
||||
opacity: .9;
|
||||
position: absolute;
|
||||
right: 3em;
|
||||
width: 20em;
|
||||
}
|
||||
|
||||
/*
|
||||
* Global add-ons
|
||||
*/
|
||||
|
||||
.sub-header {
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
/*
|
||||
* Top navigation
|
||||
* Hide default border to remove 1px line.
|
||||
*/
|
||||
.navbar-fixed-top {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
cursor: default !important;
|
||||
}
|
||||
.navbar-brand:hover {
|
||||
cursor: default !important;
|
||||
color: #9d9d9d !important;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sidebar
|
||||
*/
|
||||
|
||||
/* Hide for mobile, show later */
|
||||
.sidebar {
|
||||
display: none;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 51px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
display: block;
|
||||
padding: 20px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
|
||||
background-color: #f5f5f5;
|
||||
border-right: 1px solid #eee;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sidebar navigation */
|
||||
.nav-sidebar {
|
||||
margin-right: -21px; /* 20px padding + 1px border */
|
||||
margin-bottom: 20px;
|
||||
margin-left: -20px;
|
||||
}
|
||||
.nav-sidebar > li > a {
|
||||
padding-right: 20px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.nav-sidebar > .active > a,
|
||||
.nav-sidebar > .active > a:hover,
|
||||
.nav-sidebar > .active > a:focus {
|
||||
color: #fff;
|
||||
background-color: #428bca;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Main content
|
||||
*/
|
||||
|
||||
.main {
|
||||
padding: 20px;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.main {
|
||||
padding-right: 40px;
|
||||
padding-left: 40px;
|
||||
}
|
||||
}
|
||||
.main .page-header {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
|
||||
tr[data-id] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*
|
||||
* Placeholder dashboard ideas
|
||||
*/
|
||||
|
||||
.placeholders {
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
.placeholders h4 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.placeholder {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.placeholder img {
|
||||
display: inline-block;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
#bg {
|
||||
width:100%;
|
||||
min-height:100%;
|
||||
height:auto!important;
|
||||
position:fixed;
|
||||
top:0px;
|
||||
left:0px;
|
||||
overflow:hidden;
|
||||
border:0px;
|
||||
z-index:-9;
|
||||
float:left;
|
||||
}
|
||||
|
||||
#form {
|
||||
background: rgba(255, 255, 255, .9);
|
||||
margin: auto;
|
||||
display: block;
|
||||
width: 35%;
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
padding-left: 5em;
|
||||
border-radius: 1em;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
.dataTables_wrapper {
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
#appFormOld {
|
||||
margin: auto;
|
||||
display: block;
|
||||
width: 40%;
|
||||
height: 98%;
|
||||
border-radius: 1em;
|
||||
}
|
||||
|
||||
#viewMap {
|
||||
float: right;
|
||||
border: 0px;
|
||||
margin: 5px 5px 0 0;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 21px;
|
||||
}
|
||||
|
||||
#goMap {
|
||||
float: right;
|
||||
border: 0px;
|
||||
margin: 5px 5px 0 0;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
#display {
|
||||
color: white;
|
||||
background: rgba(0,0,0,.5);
|
||||
border-radius: 1em;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
#display input {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.rule {
|
||||
margin-bottom: .5em;
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><svg width='200px' height='200px' xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="uil-gears"><rect x="0" y="0" width="100" height="100" fill="none" class="bk"></rect><g transform="translate(-20,-20)"><path d="M79.9,52.6C80,51.8,80,50.9,80,50s0-1.8-0.1-2.6l-5.1-0.4c-0.3-2.4-0.9-4.6-1.8-6.7l4.2-2.9c-0.7-1.6-1.6-3.1-2.6-4.5 L70,35c-1.4-1.9-3.1-3.5-4.9-4.9l2.2-4.6c-1.4-1-2.9-1.9-4.5-2.6L59.8,27c-2.1-0.9-4.4-1.5-6.7-1.8l-0.4-5.1C51.8,20,50.9,20,50,20 s-1.8,0-2.6,0.1l-0.4,5.1c-2.4,0.3-4.6,0.9-6.7,1.8l-2.9-4.1c-1.6,0.7-3.1,1.6-4.5,2.6l2.1,4.6c-1.9,1.4-3.5,3.1-5,4.9l-4.5-2.1 c-1,1.4-1.9,2.9-2.6,4.5l4.1,2.9c-0.9,2.1-1.5,4.4-1.8,6.8l-5,0.4C20,48.2,20,49.1,20,50s0,1.8,0.1,2.6l5,0.4 c0.3,2.4,0.9,4.7,1.8,6.8l-4.1,2.9c0.7,1.6,1.6,3.1,2.6,4.5l4.5-2.1c1.4,1.9,3.1,3.5,5,4.9l-2.1,4.6c1.4,1,2.9,1.9,4.5,2.6l2.9-4.1 c2.1,0.9,4.4,1.5,6.7,1.8l0.4,5.1C48.2,80,49.1,80,50,80s1.8,0,2.6-0.1l0.4-5.1c2.3-0.3,4.6-0.9,6.7-1.8l2.9,4.2 c1.6-0.7,3.1-1.6,4.5-2.6L65,69.9c1.9-1.4,3.5-3,4.9-4.9l4.6,2.2c1-1.4,1.9-2.9,2.6-4.5L73,59.8c0.9-2.1,1.5-4.4,1.8-6.7L79.9,52.6 z M50,65c-8.3,0-15-6.7-15-15c0-8.3,6.7-15,15-15s15,6.7,15,15C65,58.3,58.3,65,50,65z" fill="#ffd444"><animateTransform attributeName="transform" type="rotate" from="90 50 50" to="0 50 50" dur="1s" repeatCount="indefinite"></animateTransform></path></g><g transform="translate(20,20) rotate(15 50 50)"><path d="M79.9,52.6C80,51.8,80,50.9,80,50s0-1.8-0.1-2.6l-5.1-0.4c-0.3-2.4-0.9-4.6-1.8-6.7l4.2-2.9c-0.7-1.6-1.6-3.1-2.6-4.5 L70,35c-1.4-1.9-3.1-3.5-4.9-4.9l2.2-4.6c-1.4-1-2.9-1.9-4.5-2.6L59.8,27c-2.1-0.9-4.4-1.5-6.7-1.8l-0.4-5.1C51.8,20,50.9,20,50,20 s-1.8,0-2.6,0.1l-0.4,5.1c-2.4,0.3-4.6,0.9-6.7,1.8l-2.9-4.1c-1.6,0.7-3.1,1.6-4.5,2.6l2.1,4.6c-1.9,1.4-3.5,3.1-5,4.9l-4.5-2.1 c-1,1.4-1.9,2.9-2.6,4.5l4.1,2.9c-0.9,2.1-1.5,4.4-1.8,6.8l-5,0.4C20,48.2,20,49.1,20,50s0,1.8,0.1,2.6l5,0.4 c0.3,2.4,0.9,4.7,1.8,6.8l-4.1,2.9c0.7,1.6,1.6,3.1,2.6,4.5l4.5-2.1c1.4,1.9,3.1,3.5,5,4.9l-2.1,4.6c1.4,1,2.9,1.9,4.5,2.6l2.9-4.1 c2.1,0.9,4.4,1.5,6.7,1.8l0.4,5.1C48.2,80,49.1,80,50,80s1.8,0,2.6-0.1l0.4-5.1c2.3-0.3,4.6-0.9,6.7-1.8l2.9,4.2 c1.6-0.7,3.1-1.6,4.5-2.6L65,69.9c1.9-1.4,3.5-3,4.9-4.9l4.6,2.2c1-1.4,1.9-2.9,2.6-4.5L73,59.8c0.9-2.1,1.5-4.4,1.8-6.7L79.9,52.6 z M50,65c-8.3,0-15-6.7-15-15c0-8.3,6.7-15,15-15s15,6.7,15,15C65,58.3,58.3,65,50,65z" fill="#3771a2"><animateTransform attributeName="transform" type="rotate" from="0 50 50" to="90 50 50" dur="1s" repeatCount="indefinite"></animateTransform></path></g></svg>
|
After Width: | Height: | Size: 2.5 KiB |
|
@ -0,0 +1,48 @@
|
|||
body {
|
||||
padding-top: 40px;
|
||||
padding-bottom: 40px;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.form-signin {
|
||||
max-width: 330px;
|
||||
padding: 15px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.form-signin .form-signin-heading,
|
||||
.form-signin .checkbox {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.form-signin .checkbox {
|
||||
font-weight: normal;
|
||||
}
|
||||
.form-signin .form-control {
|
||||
position: relative;
|
||||
height: auto;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.form-signin .form-control:focus {
|
||||
z-index: 2;
|
||||
}
|
||||
.form-control-top {
|
||||
margin-bottom: -1px;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
.form-control-middle {
|
||||
border-radius: 0;
|
||||
}
|
||||
.form-control-bottom {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
.form-control-alone {
|
||||
border-radius: 10px;
|
||||
}
|
||||
.form-control-button {
|
||||
margin-top: 10px;
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 923 B |
Binary file not shown.
After Width: | Height: | Size: 944 B |
|
@ -0,0 +1,11 @@
|
|||
{% extends "minecraft_manager/dashboard.html" %}
|
||||
{% load template_settings %}
|
||||
{% block section %}
|
||||
<a href="{% template_settings 'COREPROTECT_ACTIVITY_URL' %}?username={{ user.username }}" target="_blank">Fullscreen</a>
|
||||
<iframe id="cpgui" width="100%" height="750px" src="{% template_settings 'COREPROTECT_ACTIVITY_URL' %}?username={{ user.username }}">Loading...</iframe>
|
||||
|
||||
<script>
|
||||
var height = $(window).height();
|
||||
//$("#cpgui").height(height * .8);
|
||||
</script>
|
||||
{% endblock section %}
|
|
@ -0,0 +1,45 @@
|
|||
{% extends "minecraft_manager/dashboard.html" %}
|
||||
{% load csrf_html %}
|
||||
{% load static %}
|
||||
{% block section %}
|
||||
<div id="content" hidden="hidden">
|
||||
<table id="model-table" class="table table-hover link-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Message</th>
|
||||
<th>Seen</th>
|
||||
<th>Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for alert in alerts %}
|
||||
<tr {% if alert.seen is True %}class="success"{% endif %} data-id="{{ alert.id }}">
|
||||
<td>{{ alert.snippet }}</td>
|
||||
<td>{{ alert.seen }}</td>
|
||||
<td>{{ alert.date }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if unseen > 0 %}
|
||||
<form method="post">{% autoescape off %}{% get_csrf_html request %}{% endautoescape %}
|
||||
<button type="submit" class="btn btn-primary" name="action" value="mar">Mark all as read</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#model-table").DataTable({
|
||||
'lengthMenu': [ [ 10, 25, 50, 100, -1 ], [ 10, 25, 50, 100, "All" ] ],
|
||||
'initComplete': function(settings, json) {
|
||||
$("#content").show();
|
||||
},
|
||||
'order': [],
|
||||
{% if user.usersettings %}
|
||||
'pageLength': {{ user.usersettings.default_results }},
|
||||
{% endif %}
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock section %}
|
|
@ -0,0 +1,23 @@
|
|||
{% extends "minecraft_manager/dashboard.html" %}
|
||||
{% load csrf_html %}
|
||||
{% block section %}
|
||||
<div id="content">
|
||||
<h2 class="sub-header">Alert Info</h2>
|
||||
<div class="row">
|
||||
<div class="col-xs-6 col-md-4">
|
||||
<p>Message: </p>
|
||||
<p class="well">{{ alert.message }}</p>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-8">
|
||||
<p>Date: {{ alert.date }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% if alert.seen is True %}
|
||||
<div class="row">
|
||||
<form method="post">{% autoescape off %}{% get_csrf_html request %}{% endautoescape %}
|
||||
<button type="submit" class="btn btn-primary" name="action" value="mu">Mark Unread</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock section %}
|
|
@ -0,0 +1,46 @@
|
|||
{% extends "minecraft_manager/dashboard.html" %}
|
||||
{% load static %}
|
||||
{% block section %}
|
||||
<div id="content" hidden="hidden">
|
||||
<table id="model-table" class="table table-hover link-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>App ID</th>
|
||||
<th>Username</th>
|
||||
<th>Age</th>
|
||||
<th>Past Ban</th>
|
||||
<th>Status</th>
|
||||
<th>Date Applied</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for app in applications %}
|
||||
<tr {% if app.accepted is True %}class="success"{% endif %}{% if app.accepted is False %}class="danger"{% endif %} data-id="{{ app.id }}">
|
||||
<td>{{ app.id }}</td>
|
||||
<td>{{ app.username }}</td>
|
||||
<td>{{ app.age }}</td>
|
||||
<td>{{ app.ever_banned }}</td>
|
||||
<td>{{ app.status }}</td>
|
||||
<td>{{ app.date }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<a href="{% url 'reference' %}">Reference Report</a>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#model-table").DataTable({
|
||||
'lengthMenu': [ [ 10, 25, 50, 100, -1 ], [ 10, 25, 50, 100, "All" ] ],
|
||||
'initComplete': function(settings, json) {
|
||||
$("#content").show();
|
||||
},
|
||||
'order': [],
|
||||
{% if request.user.usersettings %}
|
||||
'pageLength': {{ request.user.usersettings.default_results }},
|
||||
{% endif %}
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock section %}
|
|
@ -0,0 +1,43 @@
|
|||
{% extends "minecraft_manager/dashboard.html" %}
|
||||
{% load csrf_html %}
|
||||
{% block section %}
|
||||
<div id="content">
|
||||
<h2 class="sub-header">Viewing application for {{ application.username }}</h2>
|
||||
<div class="row">
|
||||
<div class="col-xs-6 col-md-4">
|
||||
<!-- <p>Username: {{ application.username }}</p> -->
|
||||
<p>Age: {{ application.age }}</p>
|
||||
<p>Application Date: {{ application.date }}</p>
|
||||
<p>Type Of Player:</p>
|
||||
<p class="well">{{ application.player_type }}</p>
|
||||
{% if application.accepted is not null %}
|
||||
<p>Status: {{ application.status }}</p>
|
||||
{% else %}
|
||||
<form action="" method="post">{% autoescape off %}{% get_csrf_html request %}{% endautoescape %}
|
||||
<button class="btn btn-primary" type="submit" name="accept" value="accept">Accept</button>
|
||||
<button class="btn btn-danger" type="submit" name="accept" value="deny">Deny</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-8">
|
||||
<p>Ever Been Banned: {{ application.ever_banned }}</p>
|
||||
{% if application.ever_banned %}
|
||||
<p>Explanation: {{ application.ever_banned_explanation }}</p>
|
||||
{% endif %}
|
||||
{% if application.reference %}
|
||||
<p>Reference: {{ application.reference }}</p>
|
||||
{% endif %}
|
||||
<p>Read The Rules: {{ application.read_rules }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-6 col-md-4">
|
||||
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-8">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock section %}
|
|
@ -0,0 +1,45 @@
|
|||
{% extends "minecraft_manager/dashboard.html" %}
|
||||
{% block section %}
|
||||
<div id="content" hidden="hidden">
|
||||
<table id="model-table" class="table table-hover link-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Player</th>
|
||||
<th>Message</th>
|
||||
<td>Issued By</td>
|
||||
<th>Expires</th>
|
||||
<th>Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for ban in bans %}
|
||||
<tr {% if ban.id > 0 %} data-url="{% url 'player' %}" data-id="{{ ban.id }}" {% endif %}>
|
||||
<!-- <td>{{ ban.id }}</td> -->
|
||||
<td>{{ ban.name }}</td>
|
||||
<td>{{ ban.reason }}</td>
|
||||
<td>{{ ban.source }}</td>
|
||||
<td>{{ ban.expires }}</td>
|
||||
<td>{{ ban.created }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#model-table").DataTable({
|
||||
'lengthMenu': [ [ 10, 25, 50, 100, -1 ], [ 10, 25, 50, 100, "All" ] ],
|
||||
'initComplete': function(settings, json) {
|
||||
$("#content").show();
|
||||
$(this).css('width', '');
|
||||
},
|
||||
'order': [],
|
||||
{% if request.user.usersettings %}
|
||||
'pageLength': {{ request.user.usersettings.default_results }},
|
||||
{% endif %}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock section %}
|
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE html>
|
||||
{% load static %}
|
||||
{% load template_settings %}
|
||||
<html>
|
||||
<head>
|
||||
<!-- favicon -->
|
||||
<link rel="shortcut icon" type="image/png" href="{% static 'favicon.png' %}"/>
|
||||
|
||||
<!-- Latest compiled and minified JQuery -->
|
||||
<script src="https://code.jquery.com/jquery-3.1.1.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script>
|
||||
|
||||
<!-- Optional theme -->
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
|
||||
|
||||
<!-- Latest compiled and minified CSS -->
|
||||
{% block bootstrap %}{% endblock bootstrap %}
|
||||
|
||||
<!-- Latest compiled and minified JavaScript -->
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
|
||||
|
||||
<!-- DataTables -->
|
||||
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.13/css/dataTables.bootstrap.min.css"></link>
|
||||
|
||||
<script src="https://cdn.datatables.net/1.10.13/js/jquery.dataTables.min.js"></script>
|
||||
|
||||
<script src="{% static "FilterByText.js" %}"></script>
|
||||
|
||||
{% block head %}{% endblock head %}
|
||||
{% template_settings 'WINDOW_TITLE' as WINDOW_TITLE %}
|
||||
<title>{% if WINDOW_TITLE %}{{ WINDOW_TITLE }}{% else %}MCM{% endif %}</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{% block content %}{% endblock content %}
|
||||
</body>
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
});
|
||||
String.prototype.htmlEscape = function() {
|
||||
return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
||||
}
|
||||
</script>
|
||||
</html>
|
|
@ -0,0 +1,17 @@
|
|||
{% extends "minecraft_manager/dashboard.html" %}
|
||||
{% load csrf_html %}
|
||||
{% load getattribute %}
|
||||
{% block section %}
|
||||
<div id="content">
|
||||
<!-- {{ form.status }} -->
|
||||
{% for bot in form.bots %}
|
||||
<p>{{ bot.name }}: <span class="label {% if form|getattribute:bot.name is True %}label-success{% else %}label-danger{% endif %}">{% if form|getattribute:bot.name is True %}Started{% else %}Stopped{% endif %}</span></p>
|
||||
<form action="" method="post">{% autoescape off %}{% get_csrf_html request %}{% endautoescape %}
|
||||
<button class="btn btn-primary{% if form|getattribute:bot.name is True %} disabled{% endif %}" type="submit" name="{{ bot.name }}" value="start" {% if form|getattribute:bot.name is True %} disabled="disabled"{% endif %}>Start</button>
|
||||
<button class="btn btn-primary{% if form|getattribute:bot.name is False %} disabled{% endif %}" type="submit" name="{{ bot.name }}" value="stop" {% if form|getattribute:bot.name is False %}disabled="disabled"{% endif %}>Stop</button>
|
||||
<button class="btn btn-primary{% if form|getattribute:bot.name is False %} disabled{% endif %}" type="submit" name="{{ bot.name }}" value="restart" {% if form|getattribute:bot.name is False %}disabled="disabled"{% endif %}>Restart</button>
|
||||
</form>
|
||||
<br/>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock section %}
|
|
@ -0,0 +1,121 @@
|
|||
{% extends "minecraft_manager/dashboard.html" %}
|
||||
{% load csrf_html %}
|
||||
{% block section %}
|
||||
<div id="content">
|
||||
<div class="row">
|
||||
<div class="col-xs-8 col-md-5">
|
||||
<h3>Global Chat</h3>
|
||||
<div id="global_chat" style="overflow:scroll; height:40em;">
|
||||
Loading...
|
||||
</div>
|
||||
<br/>
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">Auto-Scroll</span>
|
||||
<span class="input-group-addon">
|
||||
<input id="global_scroll" type="checkbox" aria-label="Auto-Scroll" checked="checked">
|
||||
</span>
|
||||
<input id="global_input" type="text" class="form-control" placeholder="Global Chat...">
|
||||
<span class="input-group-btn">
|
||||
<button id="global_button" onClick="chat('global');" class="btn btn-secondary" type="button">Send</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-2 col-md-2">
|
||||
<h3>Players <span id="player_online">N</span>/<span id="player_max">A</span></h3>
|
||||
<div id="player_list">
|
||||
<div>Loading...</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if staff %}
|
||||
<div class="col-xs-8 col-md-5">
|
||||
<h3>Staff Chat</h3>
|
||||
<div id="staff_chat" style="overflow:scroll; height:40em;">
|
||||
Loading...
|
||||
</div>
|
||||
<br/>
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">Auto-Scroll</span>
|
||||
<span class="input-group-addon">
|
||||
<input id="staff_scroll" type="checkbox" aria-label="Auto-Scroll" checked="checked">
|
||||
</span>
|
||||
<input id="staff_input" type="text" class="form-control" placeholder="Staff Chat...">
|
||||
<span class="input-group-btn">
|
||||
<button id="staff_button" onClick="chat('staff');" class="btn btn-secondary" type="button">Send</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#global_input").keypress(function(e) {
|
||||
if (e.keyCode === 13) {
|
||||
$("#global_button").click();
|
||||
}
|
||||
});
|
||||
scrollGlobal();
|
||||
|
||||
{% if staff %}
|
||||
$("#staff_input").keypress(function(e) {
|
||||
if (e.keyCode === 13) {
|
||||
$("#staff_button").click();
|
||||
}
|
||||
});
|
||||
scrollStaff();
|
||||
{% endif %}
|
||||
|
||||
setInterval(function() {
|
||||
$.ajax({
|
||||
url: "{% url "api-web" 'log' %}?api={{ api }}",
|
||||
success: function(res, status, xhr) {
|
||||
$("#global_chat").html(res.html.global);
|
||||
if ($("#global_scroll").prop("checked")) {
|
||||
scrollGlobal();
|
||||
}
|
||||
|
||||
{% if staff %}
|
||||
$("#staff_chat").html(res.html.staff);
|
||||
if ($("#staff_scroll").prop("checked")) {
|
||||
scrollStaff();
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
url: "{% url "api-web" 'online' %}?api={{ api }}",
|
||||
success: function(res, status, xhr) {
|
||||
$("#player_list").html(res.html);
|
||||
$("#player_online").html(res.query.online);
|
||||
$("#player_max").html(res.query.max);
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
function chat(type) {
|
||||
var msg = $("#" + type + "_input").val();
|
||||
if (msg != null && msg != "") {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
data: {'csrfmiddlewaretoken': '{% get_csrf_token request %}','chat': type, 'message': msg}
|
||||
});
|
||||
$("#" + type + "_input").val('');
|
||||
}
|
||||
}
|
||||
|
||||
function scrollGlobal() {
|
||||
var global_chat = document.getElementById("global_chat");
|
||||
global_chat.scrollTop = global_chat.scrollHeight;
|
||||
}
|
||||
|
||||
{% if staff %}
|
||||
function scrollStaff() {
|
||||
var staff_chat = document.getElementById("staff_chat");
|
||||
staff_chat.scrollTop = staff_chat.scrollHeight;
|
||||
}
|
||||
{% endif %}
|
||||
</script>
|
||||
{% endblock section %}
|
|
@ -0,0 +1,11 @@
|
|||
{% extends "minecraft_manager/dashboard.html" %}
|
||||
{% load template_settings %}
|
||||
{% block section %}
|
||||
<a href="{% template_settings 'COREPROTECT_WEB_URL' %}?username={{ user.username }}" target="_blank">Fullscreen</a>
|
||||
<iframe id="cpgui" width="100%" height="750px" src="{% template_settings 'COREPROTECT_WEB_URL' %}?username={{ user.username }}">Loading...</iframe>
|
||||
|
||||
<script>
|
||||
var height = $(window).height();
|
||||
//$("#cpgui").height(height * .8);
|
||||
</script>
|
||||
{% endblock section %}
|
|
@ -0,0 +1,214 @@
|
|||
{% extends "minecraft_manager/base.html" %}
|
||||
{% load static %}
|
||||
{% load csrf_html %}
|
||||
{% load sidebar %}
|
||||
{% load template_settings %}
|
||||
{% block bootstrap %}
|
||||
{% if user.usersettings.default_theme == 'DA' %}
|
||||
<link rel="stylesheet" href="{% static "bootstrap-dark.css" %}">
|
||||
{% elif user.usersettings.default_theme == 'SO' %}
|
||||
<link rel="stylesheet" href="{% static "bootstrap-solar.css" %}">
|
||||
{% elif user.usersettings.default_theme == 'SL' %}
|
||||
<link rel="stylesheet" href="{% static "bootstrap-slate.css" %}">
|
||||
{% else %}
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
||||
{% endif %}
|
||||
{% endblock bootstrap %}
|
||||
{% block head %}
|
||||
{% if user.usersettings.default_theme == 'DA' %}
|
||||
<link rel="stylesheet" href="{% static "dashboard-dark.css" %}">
|
||||
{% elif user.usersettings.default_theme == 'SO' %}
|
||||
<link rel="stylesheet" href="{% static "dashboard-solar.css" %}">
|
||||
{% elif user.usersettings.default_theme == 'SL' %}
|
||||
<link rel="stylesheet" href="{% static "dashboard-slate.css" %}">
|
||||
{% else %}
|
||||
<link rel="stylesheet" href="{% static "dashboard.css" %}">
|
||||
{% endif %}
|
||||
<script src="{% static "chart.min.js" %}"></script>
|
||||
{% endblock head %}
|
||||
{% block content %}
|
||||
<nav class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
{% template_settings 'DASHBOARD_TITLE' as DASHBOARD_TITLE %}
|
||||
<a class="navbar-brand">{% if DASHBOARD_TITLE %}{{ DASHBOARD_TITLE }}{% else %}Minecraft Manager{% endif %}</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
{% template_settings 'COREPROTECT_WEB_URL' as CPW %}
|
||||
{% template_settings 'COREPROTECT_ACTIVITY_URL' as CPA %}
|
||||
{% if CPW %}
|
||||
{% if perms.auth.coreprotect_activity and CPA %}
|
||||
<li class="dropdown">
|
||||
<a class="dropdown-toggle " href="#" role="button" data-toggle="dropdown" id="cpDropdown">CoreProtect</a>
|
||||
<ul class="dropdown-menu" aria-labelledby="cpDropdown">
|
||||
<li><a href="{% url 'coreprotect' %}">Web GUI</a></li>
|
||||
<li><a href="{% url 'activity' %}">Activity Monitor</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
{% elif perms.auth.coreprotect_partial or perms.auth.coreprotect_full %}
|
||||
<li><a class="dropdown-item btn" href="{% url 'coreprotect' %}">CoreProtect</a></li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<li class="dropdown">
|
||||
<a class="dropdown-toggle " href="#" role="button" data-toggle="dropdown" id="accountDropdown">{{ user.username }}</a>
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="accountDropdown">
|
||||
<li><a style="cursor:pointer;" data-toggle="modal" data-target="#settingsModal">Settings</a></li>
|
||||
<li><a style="cursor:pointer;" data-toggle="modal" data-target="#passwordModal">Change Password</a></li>
|
||||
<li><a href="{% url "logout" %}">Logout</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-3 col-md-2 sidebar">
|
||||
<ul class="nav nav-sidebar">
|
||||
{% autoescape off %}{% get_sidebar current_app|safe request %}{% endautoescape %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
|
||||
<h1 class="page-header">{{ current_app|capfirst }} {% block after_h1 %}{% endblock %}</h1>
|
||||
<div id="alert" class="popup"></div>
|
||||
{% block section %}{% endblock section %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="settingsModal" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
|
||||
<!-- Modal content-->
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="modal-title">User Settings</h4>
|
||||
</div>
|
||||
<form id="settingsForm" method="POST" action="{% url 'api-web' keyword='settings' %}">{% autoescape off %}{% get_csrf_html request %}{% endautoescape %}
|
||||
<div class="modal-body">
|
||||
{% get_form 'usersettings' request %}
|
||||
{% if user.is_staff %}
|
||||
<br/><a target="_blank" href="{% url "admin:index" %}">Admin Site</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary" onclick="saveSettings();">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div id="passwordModal" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
|
||||
<!-- Modal content-->
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="modal-title">Change Password</h4>
|
||||
</div>
|
||||
<form id="passwordForm" method="POST" action="{% url 'api-web' keyword='password' %}">{% autoescape off %}{% get_csrf_html request %}{% endautoescape %}
|
||||
<div id="passwordDiv" class="modal-body">
|
||||
{% get_form 'password' request %}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary" onclick="savePassword();">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
|
||||
$('.table.link-table > tbody > tr').click(function() {
|
||||
var base_url = $(location).attr('href').split("?")[0];
|
||||
var id = $(this).attr('data-id');
|
||||
var url = $(this).attr('data-url');
|
||||
if (url != null && id != null) {
|
||||
if (url.endsWith("/")) {
|
||||
window.location.href = url + id
|
||||
} else {
|
||||
window.location.href = url + "/" + id
|
||||
}
|
||||
} else if (id != null) {
|
||||
if (base_url.endsWith("/")) {
|
||||
window.location.href = base_url + id
|
||||
} else {
|
||||
window.location.href = base_url + "/" + id
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function showAlert(message, flavor) {
|
||||
var $alert = $("#alert");
|
||||
$alert.hide();
|
||||
$alert.html(message);
|
||||
$alert.removeClass();
|
||||
$alert.addClass("popup alert alert-" + flavor);
|
||||
$alert.fadeIn().delay(1000).fadeOut();
|
||||
}
|
||||
|
||||
function saveSettings() {
|
||||
var form = $("#settingsForm").serialize();
|
||||
form = form.replace("search_player_ip=on", "search_player_ip=True");
|
||||
if (!form.includes("search_player_ip")) {
|
||||
form += "&search_player_ip=False";
|
||||
}
|
||||
form = form.replace("show_timestamp_chat=on", "show_timestamp_chat=True");
|
||||
if (!form.includes("show_timestamp_chat")) {
|
||||
form += "&show_timestamp_chat=False";
|
||||
}
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '{% url 'api-web' keyword='settings' %}',
|
||||
data: form,
|
||||
success: function() {
|
||||
location.reload()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function savePassword() {
|
||||
var form = $("#passwordForm").serialize();
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '{% url 'api-web' keyword='password' %}',
|
||||
data: form,
|
||||
success: function(data) {
|
||||
if (data === "success") {
|
||||
alert("Password Changed!");
|
||||
location.reload();
|
||||
} else {
|
||||
$("#passwordDiv").html(data);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$(".paginate_button").each(function() {
|
||||
$(this).addClass("btn");
|
||||
});
|
||||
|
||||
$("#model-table").on('draw.dt', function() {
|
||||
$(".paginate_button").each(function() {
|
||||
$(this).addClass("btn");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock content %}
|
|
@ -0,0 +1,25 @@
|
|||
{% extends 'minecraft_manager/external/base.html' %}
|
||||
|
||||
{% block title %}Application Form{% endblock %}
|
||||
|
||||
{% block h1 %}Application{% endblock %}
|
||||
|
||||
{% block form_top %}
|
||||
<h3>Rules</h3>
|
||||
{% for rule in rules %}
|
||||
<div class="rule">{{ rule }}</div>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
{% if valid is True %}
|
||||
{% block valid %}
|
||||
<h2>Thanks for applying!
|
||||
<br/>
|
||||
We will get back to you soon.
|
||||
<br/>
|
||||
Consider joining our <a href="https://discord.gg/{{ map.discord_invite }}">Discord</a></h2>
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
|
||||
{% block submit %}Apply{% endblock %}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
{% load static %}
|
||||
<html>
|
||||
<head>
|
||||
<title>{% block title %}{% endblock %}</title>
|
||||
<link rel="shortcut icon" type="image/png" href="{% static 'favicon.png' %}"/>
|
||||
<script src="{% static 'jquery-3.3.1.min.js' %}"></script>
|
||||
<script src="{% static 'FilterByText.js' %}"></script>
|
||||
<script src="//cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>
|
||||
{% if captcha %}
|
||||
<script src='https://www.google.com/recaptcha/api.js'></script>
|
||||
{% endif %}
|
||||
<link rel="stylesheet" href="{% static 'external.css' %}">
|
||||
<link rel="stylesheet" href="//cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css">
|
||||
</head>
|
||||
<body>
|
||||
{% include 'minecraft_manager/external/map.html' %}
|
||||
<div id="form"><h1>{% block h1 %}{% endblock %}</h1>
|
||||
{% block form_top %}{% endblock %}
|
||||
{% if valid is True %}
|
||||
{% block valid %}{% endblock %}
|
||||
{% else %}
|
||||
<form method="{% block method %}POST{% endblock %}">
|
||||
{% block form %}{{ form }}{% endblock %}<br/><br/>
|
||||
{% if captcha %}<div class="g-recaptcha" data-sitekey="6LeLcGEUAAAAAMMpHR-7VL6Q3nNV5v03S5sq1Ddz"></div>{% endif %}
|
||||
<button type="submit">{% block submit %}Submit{% endblock %}</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% block form_bottom %}{% endblock %}
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
var $dataTable = $("#dataTable");
|
||||
if ($dataTable.length) {
|
||||
$dataTable.hide();
|
||||
$dataTable.DataTable({
|
||||
'initComplete': function(settings, json) {
|
||||
$dataTable.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var $filter = $("#filter");
|
||||
if ($filter.length) {
|
||||
$("#stats").filterByText($filter);
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
</html>
|
|
@ -0,0 +1,26 @@
|
|||
{% load static %}
|
||||
{% if map.dynmap_static_url %}
|
||||
<iframe id='bg' scrolling='no' allowtransparency='true' src='{{ map.static_url }}' frameborder='0'>Loading...</iframe>
|
||||
{% elif map.dynmap_url %}
|
||||
<iframe id='bg' scrolling='no' allowtransparency='true' src='{{ map.map_url }}' frameborder='0'>Loading...</iframe>
|
||||
{% else %}
|
||||
<img id='bg' src="{% static 'background.png' %}" />
|
||||
{% endif %}
|
||||
|
||||
{% if map.dynmap_static_url or map.dynmap_url %}
|
||||
<!-- Images -->
|
||||
<a id='viewMap' href='javascript:;' hidden><img src="{% static 'world.png' %}" /></a>
|
||||
<a id='goMap' href='{% if map.dynmap_url %}{{ map.map_url }}{% else %}#{% endif %}' target='{{ map.target }}' hidden><img src="{% static 'world_go.png' %}" /></a>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
var $viewMap = $("#viewMap");
|
||||
$viewMap.show();
|
||||
$("#goMap").show();
|
||||
|
||||
$viewMap.click(function() {
|
||||
$("#form").toggle();
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
|
@ -0,0 +1,46 @@
|
|||
{% extends 'minecraft_manager/external/base.html' %}
|
||||
|
||||
{% load pretty_print %}
|
||||
|
||||
{% block title %}Stats Search{% endblock %}
|
||||
|
||||
{% block h1 %}Stats{% endblock %}
|
||||
|
||||
{% block method %}GET{% endblock %}
|
||||
|
||||
{% block form %}
|
||||
<label>Filter: <input id="filter" /></label>
|
||||
<br/>
|
||||
<label>Stat: <br/>
|
||||
<select id="stats" name="stat">
|
||||
{% for stat in stats %}
|
||||
<option {% if request.GET.stat == stat %}selected{% endif %} value="{{ stat }}">{{ stat|pretty_stat }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
{% endblock %}
|
||||
|
||||
{% block submit %}Search{% endblock %}
|
||||
|
||||
{% block form_bottom %}
|
||||
{% if results %}
|
||||
<table id="dataTable" class="stripe row-border compact">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Rank</th>
|
||||
<th>Username</th>
|
||||
<th>Score</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for result in results %}
|
||||
<tr>
|
||||
<td>{{ forloop.counter }}</td>
|
||||
<td><a href="{% url 'external-stats-player' %}?uuid={{ result.uuid }}">{{ result.username }}</a></td>
|
||||
<td>{{ result.score|pretty_score }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,44 @@
|
|||
{% extends 'minecraft_manager/external/base.html' %}
|
||||
|
||||
{% load pretty_print %}
|
||||
|
||||
{% block title %}Stats Search{% endblock %}
|
||||
|
||||
{% block h1 %}Stats{% endblock %}
|
||||
|
||||
{% block method %}GET{% endblock %}
|
||||
|
||||
{% block form %}
|
||||
<label data-toggle="tooltip" title="If you recently updated your username, try searching via UUID">Username:
|
||||
<input type="text" name="username" value="{{ request.GET.username }}" />
|
||||
</label>
|
||||
<br/><br/>----- or -----<br/><br/>
|
||||
<label>UUID:
|
||||
<input type="text" name="uuid" size="36" value="{{ request.GET.uuid }}" />
|
||||
</label>
|
||||
{% endblock %}
|
||||
|
||||
{% block submit %}Search{% endblock %}
|
||||
|
||||
{% block form_bottom %}
|
||||
{% if results %}
|
||||
<table id="dataTable" class="stripe row-border compact">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Stat</th>
|
||||
<th>Rank</th>
|
||||
<th>Score</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for result in results %}
|
||||
<tr>
|
||||
<td><a href="{% url 'external-stats' %}?stat={{ result.stat }}">{{ result.stat|pretty_stat }}</a></td>
|
||||
<td>{{ result.rank }}</td>
|
||||
<td>{{ result.score|pretty_score }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,13 @@
|
|||
{% extends 'minecraft_manager/external/base.html' %}
|
||||
|
||||
{% block title %}Ticket Form{% endblock %}
|
||||
|
||||
{% block h1 %}Ticket Form{% endblock %}
|
||||
|
||||
{% if valid is True %}
|
||||
{% block valid %}
|
||||
<h2>Ticket Submitted
|
||||
<br/>
|
||||
We will get back to you soon.</h2>
|
||||
{% endblock %}
|
||||
{% endif %}
|
|
@ -0,0 +1,9 @@
|
|||
{% extends "minecraft_manager/base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>Logged out</h1>
|
||||
<p>You have logged out 24CarrotCraft.</p><p>
|
||||
</p><p><a href="{% url 'login' %}">Log back in</a>. </p>
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,31 @@
|
|||
{% extends "minecraft_manager/base.html" %}
|
||||
{% load static %}
|
||||
{% block bootstrap %}
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
||||
{% endblock bootstrap %}
|
||||
{% block head %}
|
||||
<link rel="stylesheet" href="{% static "signin.css" %}">
|
||||
{% endblock head %}
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
|
||||
<form class="form-signin" method="post" action="{% url 'login' %}">{% csrf_token %}
|
||||
{% if form.errors %}
|
||||
<p class="alert alert-error">Username and Password are invalid. Please try again.</p>
|
||||
{% endif %}
|
||||
<label for="inputUsername" class="sr-only">Username</label>
|
||||
<input type="text" id="inputUsername" class="form-control form-control-top" name="username" placeholder="Username" required autofocus>
|
||||
<label for="inputPassword" class="sr-only">Password</label>
|
||||
<input type="password" id="inputPassword" class="form-control form-control-bottom" name="password" placeholder="Password" required>
|
||||
<button class="btn btn-lg btn-primary btn-block form-control-button" type="submit">Sign in</button>
|
||||
<input type="hidden" name="next" value="{{ next }}" />
|
||||
</form>
|
||||
|
||||
</div> <!-- /container -->
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
{% endblock content %}
|
|
@ -0,0 +1,141 @@
|
|||
{% extends "minecraft_manager/dashboard.html" %}
|
||||
{% block section %}
|
||||
<div id="content">
|
||||
<div class="row">
|
||||
<div class="col-xs-9 col-md-6">
|
||||
<h3 class="center">Applications{% if form.apps.unanswered > 0 %} <a href="{% url "application" %}?accepted=">({{ form.apps.unanswered }} Unanswered)</a>{% endif %}</h3>
|
||||
<canvas id="appChart" style="width:30em;height:15em" ></canvas>
|
||||
</div>
|
||||
<div class="col-xs-9 col-md-6">
|
||||
<h3 class="center">Tickets{% if form.tickets.unclaimed > 0 %} <a href="{% url "ticket" %}?claimed=false">({{ form.tickets.unclaimed }} Unclaimed)</a>{% endif %}</h3>
|
||||
<h4></h4>
|
||||
<canvas id="ticketChart" style="width:30em;height:15em" ></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-18 col-md-12">
|
||||
<h3 class="center">Totals</h3>
|
||||
<canvas id="totalChart" style="width:30em;height:15em" ></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
var app_data = {
|
||||
labels: [
|
||||
"Unanswered",
|
||||
"Denied",
|
||||
"Accepted"
|
||||
],
|
||||
datasets: [
|
||||
{
|
||||
data: [{{ form.apps.unanswered }}, {{ form.apps.denied }}, {{ form.apps.accepted }}],
|
||||
backgroundColor: [
|
||||
"#8080ff",
|
||||
"#ff3333",
|
||||
"#33cc33"
|
||||
],
|
||||
hoverBackgroundColor: [
|
||||
"#8080ff",
|
||||
"#ff3333",
|
||||
"#33cc33"
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
var app_options = {
|
||||
animation: {
|
||||
animateRotate: true
|
||||
}
|
||||
}
|
||||
var app_ctx = $("#appChart");
|
||||
var app_chart = new Chart(app_ctx,
|
||||
{
|
||||
type: 'pie',
|
||||
data: app_data,
|
||||
options: app_options
|
||||
}
|
||||
);
|
||||
|
||||
var ticket_data = {
|
||||
labels: [
|
||||
"Unclaimed",
|
||||
"Claimed",
|
||||
"Resolved"
|
||||
],
|
||||
datasets: [
|
||||
{
|
||||
data: [{{ form.tickets.unclaimed }}, {{ form.tickets.claimed }}, {{ form.tickets.resolved }}],
|
||||
backgroundColor: [
|
||||
"#8080ff",
|
||||
"#ff3333",
|
||||
"#33cc33"
|
||||
],
|
||||
hoverBackgroundColor: [
|
||||
"#8080ff",
|
||||
"#ff3333",
|
||||
"#33cc33"
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
var ticket_options = {
|
||||
animation: {
|
||||
animateRotate: true
|
||||
}
|
||||
}
|
||||
var ticket_ctx = $("#ticketChart");
|
||||
var ticket_chart = new Chart(ticket_ctx,
|
||||
{
|
||||
type: 'pie',
|
||||
data: ticket_data,
|
||||
options: ticket_options
|
||||
}
|
||||
);
|
||||
|
||||
var total_data = {
|
||||
labels: [
|
||||
"Applications",
|
||||
"Players",
|
||||
"IPs",
|
||||
"Tickets",
|
||||
"Warnings"
|
||||
],
|
||||
datasets: [
|
||||
{
|
||||
label: "Totals",
|
||||
data: [
|
||||
{{ form.counts.applications }},
|
||||
{{ form.counts.players }},
|
||||
{{ form.counts.ips }},
|
||||
{{ form.counts.tickets }},
|
||||
{{ form.counts.warnings }}
|
||||
],
|
||||
backgroundColor: [
|
||||
'rgba(255, 99, 132, 0.2)',
|
||||
'rgba(54, 162, 235, 0.2)',
|
||||
'rgba(255, 206, 86, 0.2)',
|
||||
'rgba(75, 192, 192, 0.2)',
|
||||
'rgba(153, 102, 255, 0.2)'
|
||||
],
|
||||
borderColor: [
|
||||
'rgba(255,99,132,1)',
|
||||
'rgba(54, 162, 235, 1)',
|
||||
'rgba(255, 206, 86, 1)',
|
||||
'rgba(75, 192, 192, 1)',
|
||||
'rgba(153, 102, 255, 1)'
|
||||
],
|
||||
borderWidth: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
var total_ctx = $("#totalChart");
|
||||
var total_chart = new Chart(total_ctx,
|
||||
{
|
||||
type: 'bar',
|
||||
data: total_data
|
||||
}
|
||||
);
|
||||
});
|
||||
</script>
|
||||
{% endblock section %}
|
|
@ -0,0 +1,40 @@
|
|||
{% extends "minecraft_manager/dashboard.html" %}
|
||||
{% block section %}
|
||||
<div id="content" hidden="hidden">
|
||||
<table id="model-table" class="table table-hover link-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Username</th>
|
||||
<th>UUID</th>
|
||||
<th>Banned</th>
|
||||
<th>Last Seen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for player in players %}
|
||||
<tr {% if player.uuid in bans %}class="danger"{% endif %}{% if player.uuid not in bans %}class="success"{% endif %} data-id="{{ player.id }}">
|
||||
<td>{{ player.username }}{% if user.usersettings.search_player_ip is True %}<span hidden="hidden">{{ player.ips }}</span>{% endif %}</td>
|
||||
<td>{{ player.uuid }}</td>
|
||||
<td>{% if player.uuid in bans %}True {% else %}False{% endif %}</td>
|
||||
<td>{{ player.last_seen }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#model-table").DataTable({
|
||||
'lengthMenu': [ [ 10, 25, 50, 100, -1 ], [ 10, 25, 50, 100, "All" ] ],
|
||||
'initComplete': function(settings, json) {
|
||||
$("#content").show();
|
||||
},
|
||||
'order': [],
|
||||
{% if user.usersettings %}
|
||||
'pageLength': {{ user.usersettings.default_results }},
|
||||
{% endif %}
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock section %}
|
|
@ -0,0 +1,90 @@
|
|||
{% extends "minecraft_manager/dashboard.html" %}
|
||||
{% load csrf_html %}
|
||||
{% block section %}
|
||||
<div id="content">
|
||||
<h2 class="sub-header">Player Info ({{ player.username }})</h2>
|
||||
<div class="row">
|
||||
<div class="col-xs-9 col-md-6">
|
||||
<p>UUID: {{ player.uuid }}</p>
|
||||
{% if player.auth_user %}
|
||||
<p>Connected User: {{ player.auth_user.username }}</p>
|
||||
{% endif %}
|
||||
{% if player.application %}
|
||||
<p>Application: <a href="{% url "application" %}{{ player.application.id }}">Here</a></p>
|
||||
{% endif %}
|
||||
<p>Donor Status: {{ player.donor_status }}</p>
|
||||
<p>First Seen: {{ player.first_seen }}</p>
|
||||
<p>Last Seen: {{ player.last_seen }}</p>
|
||||
<p>Banned: {% if player.is_banned %}<span class="label label-danger">Yes</span>{% else %}<span class="label label-success">No</span>{% endif %}</p>
|
||||
</div>
|
||||
<div class="col-xs-9 col-md-6">
|
||||
<h4>Tickets</h4>
|
||||
<table class="table table-hover link-table">
|
||||
<tbody>
|
||||
{% if form.tickets %}
|
||||
{% for ticket in form.tickets %}
|
||||
<tr {% if ticket.resolved is True %}class="success"{% else %}class="danger"{% endif %} data-id="{{ ticket.id }}" data-url="{% url "ticket" %}">
|
||||
<td>{{ ticket.id }}</td>
|
||||
<td>{{ ticket.snippet }}</td>
|
||||
<td>{{ ticket.resolved }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr><td colspan="3">No Tickets Found</td></tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
<br/>
|
||||
<table class="table table-hover link-table">
|
||||
<h4>Warnings</h4>
|
||||
<tbody>
|
||||
{% if form.warnings %}
|
||||
{% for warning in form.warnings %}
|
||||
<tr {% if warning.severity == 'L' %}class="info"{% endif %}{% if warning.severity == 'M' %}class="warning"{% endif %}{% if warning.severity == 'H' %}class="danger"{% endif %} data-id="{{ warning.id }}" data-url="{% url "warning" %}">
|
||||
<!-- {{ warning.id }} -->
|
||||
<td>{{ warning.snippet }}</td>
|
||||
<td>{{ warning.severity_display }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr><td colspan="3">No Warnings Found</td></tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
<br/>
|
||||
<table class="table table-striped">
|
||||
<h4>IPs</h4>
|
||||
<tbody>
|
||||
{% if form.ips %}
|
||||
{% for ip in form.ips %}
|
||||
<tr class="default">
|
||||
<!-- {{ ip.id }} -->
|
||||
<td>{{ ip.ip }}</td>
|
||||
{% if ip.associated %}
|
||||
<td>
|
||||
{% for assoc in ip.associated %}
|
||||
<a href="{% url "player" %}{{ assoc.id }}">{{ assoc.username }}</a>{% if assoc.is_banned %} <span class="label label-danger">Banned</span>{% endif %}<br/>
|
||||
{% endfor %}
|
||||
</td>
|
||||
{% else %}
|
||||
<td>None</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr><td colspan="3">No IPs Found</td></tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-6 col-md-4">
|
||||
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-8">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock section %}
|
|
@ -0,0 +1,24 @@
|
|||
{% extends "minecraft_manager/dashboard.html" %}
|
||||
{% load static %}
|
||||
{% block section %}
|
||||
<div id="content">
|
||||
<table id="model-table" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Username</th>
|
||||
<th>Age</th>
|
||||
<th>Reference</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for app in applications %}
|
||||
<tr>
|
||||
<td>{{ app.username }}</td>
|
||||
<td>{{ app.age }}</td>
|
||||
<td>{{ app.reference }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock section %}
|
|
@ -0,0 +1,84 @@
|
|||
{% extends "minecraft_manager/dashboard.html" %}
|
||||
|
||||
{% block after_h1 %}{% if timestamp %}- {{ timestamp }}{% endif %}{% endblock %}
|
||||
|
||||
{% block section %}
|
||||
<form>
|
||||
Report: <br/>
|
||||
<label for="player">
|
||||
<input type="radio" name="report" id="player" value="players" {% if report == 'players' %}checked{% endif %}>Players
|
||||
</label>
|
||||
<label for="entity">
|
||||
<input type="radio" name="report" id="entity" value="entities" {% if report == 'entities' %}checked{% endif %}>Entities
|
||||
</label>
|
||||
<label for="count">
|
||||
<input type="radio" name="report" id="count" value="counts" {% if report == 'counts' %}checked{% endif %}>Counts
|
||||
</label>
|
||||
<br/><br/>
|
||||
World: <br/>
|
||||
<label for="overworld">
|
||||
<input type="radio" name="world" id="overworld" value="overworld" {% if world == 'overworld' %}checked{% endif %}>Overworld
|
||||
</label>
|
||||
<label for="nether">
|
||||
<input type="radio" name="world" id="nether" value="nether" {% if world == 'nether' %}checked{% endif %}>Nether
|
||||
</label>
|
||||
<label for="end">
|
||||
<input type="radio" name="world" id="end" value="end" {% if world == 'end' %}checked{% endif %}>The End
|
||||
</label>
|
||||
<br/><br/>
|
||||
<button type="submit" class="btn btn-primary">Load</button>
|
||||
</form>
|
||||
<br/>
|
||||
{% if results %}
|
||||
{% if results == 'NONE' %}
|
||||
<h2>No Report Found. Use "/mcm report" in-game to generate one.</h2>
|
||||
{% elif results == 'BOTH' %}
|
||||
<h2>Choose One Of Each Option</h2>
|
||||
{% elif results == 'EMPTY' %}
|
||||
<h2>No Data</h2>
|
||||
{% else %}
|
||||
<table id="dataTable" class="table table-hover link-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% if report == 'players' %}Name{% else %}Entity{% endif %}</th>
|
||||
<th>{% if report == 'counts' %}Count{% else %}X{% endif %}</th>
|
||||
{% if report != 'counts' %}<th>Y</th>{% endif %}
|
||||
{% if report != 'counts' %}<th>Z</th>{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in results %}
|
||||
<tr>
|
||||
<td>{{ row.name }}</td>
|
||||
<td>{% if report == 'counts' %}{{ row.count }}{% else %}{{ row.x }}{% endif %}</td>
|
||||
{% if report != 'counts' %}<td>{{ row.y }}</td>{% endif %}
|
||||
{% if report != 'counts' %}<td>{{ row.z }}</td>{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#dataTable").DataTable({
|
||||
'lengthMenu': [ [ 10, 25, 50, 100, -1 ], [ 10, 25, 50, 100, "All" ] ],
|
||||
'initComplete': function(settings, json) {
|
||||
$("#content").show();
|
||||
$(this).css('width', '');
|
||||
},
|
||||
'order': [],
|
||||
{% if user.usersettings %}
|
||||
'pageLength': {{ user.usersettings.default_results }},
|
||||
{% endif %}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block form_bottom %}
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,46 @@
|
|||
{% extends "minecraft_manager/dashboard.html" %}
|
||||
{% block section %}
|
||||
<div id="content" hidden="hidden">
|
||||
<table id="model-table" class="table table-hover link-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Player</th>
|
||||
<th>Message</th>
|
||||
<th>Priority</th>
|
||||
<th>Claimed</th>
|
||||
<th>Resolved</th>
|
||||
<th>Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for ticket in tickets %}
|
||||
<tr {% if ticket.resolved is True %}class="info"{% elif ticket.priority == 'L' %}class="success"{% elif ticket.priority == 'M' %}class="warning"{% elif ticket.priority == 'H' %}class="danger"{% endif %} data-id="{{ ticket.id }}">
|
||||
<td>{{ ticket.id }}</td>
|
||||
<td>{{ ticket.issuer }}</td>
|
||||
<td>{{ ticket.snippet }}</td>
|
||||
<td>{{ ticket.priority_display }}</td>
|
||||
<td>{{ ticket.claimed_by }}</td>
|
||||
<td>{{ ticket.resolved }}</td>
|
||||
<td>{{ ticket.date }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#model-table").DataTable({
|
||||
'lengthMenu': [ [ 10, 25, 50, 100, -1 ], [ 10, 25, 50, 100, "All" ] ],
|
||||
'initComplete': function(settings, json) {
|
||||
$("#content").show();
|
||||
},
|
||||
'order': [],
|
||||
{% if user.usersettings %}
|
||||
'pageLength': {{ user.usersettings.default_results }},
|
||||
{% endif %}
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock section %}
|
|
@ -0,0 +1,129 @@
|
|||
{% extends "minecraft_manager/dashboard.html" %}
|
||||
{% load csrf_html %}
|
||||
{% block section %}
|
||||
<div id="content">
|
||||
<h2 class="sub-header">Ticket Info ({{ ticket.issuer }}) <span {% if ticket.resolved is True %}class="label label-success"{% else %}class="label label-danger"{% endif %}>{{ ticket.resolved_label }}</span></h2>
|
||||
<div class="row">
|
||||
<div class="col-xs-6 col-md-4">
|
||||
<p>Message: </p>
|
||||
<p class="well">{{ ticket.message }}</p>
|
||||
<p>Location: {{ ticket.location }}</p>
|
||||
<p>Sent In Date: {{ ticket.date }}</p>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-8">
|
||||
{% if ticket.resolved is False %}
|
||||
<form action="" method="post">{% autoescape off %}{% get_csrf_html request %}{% endautoescape %}
|
||||
<p>Claimed:
|
||||
{% if user.is_staff %}
|
||||
<select id="ticketStaff" name="staff">
|
||||
<option value=""></option>
|
||||
{% if form.active_staff %}
|
||||
<optgroup label="Active">
|
||||
{% for staff in form.active_staff %}
|
||||
<option value="{{ staff.id }}" {% if staff == ticket.staff %}selected="selected"{% endif %}>{{ staff.username }}</option>
|
||||
{% endfor %}
|
||||
</optgroup>
|
||||
{% endif %}
|
||||
{% if form.inactive_staff %}
|
||||
<optgroup label="Inactive">
|
||||
{% for staff in form.inactive_staff %}
|
||||
<option value="{{ staff.id }}" {% if staff == ticket.staff %}selected="selected"{% endif %}>{{ staff.username }}</option>
|
||||
{% endfor %}
|
||||
</optgroup>
|
||||
{% endif %}
|
||||
</select>
|
||||
{% elif ticket.staff %}
|
||||
{{ ticket.staff.username }}
|
||||
{% else %}
|
||||
<button type="submit" name="staff" value="{{ user.id}}">Claim</button>
|
||||
{% endif %}
|
||||
</p>
|
||||
<p>Priority:
|
||||
{% if user.is_staff or user == ticket.staff %}
|
||||
<select id="ticketPriority" name="priority">
|
||||
{% for p in form.priority %}
|
||||
<option value="{{ p.0 }}" {% if ticket.priority == p.0 %}selected="selected"{% endif %}>{{ p.1 }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% else %}
|
||||
{{ ticket.priority_display }}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% if user.is_staff or ticket.staff and user == ticket.staff %}
|
||||
<button id="resolveButton" class="btn btn-primary" type="submit" name="resolved" value="1">Set as Resolved</button>
|
||||
<button id="saveButton" class="btn btn-primary" type="submit">Save</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% else %}
|
||||
<p>Claimed: {{ ticket.staff.username }}</p>
|
||||
<p>Priority: {{ ticket.priority_display }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="row">
|
||||
{% for note in form.notes %}
|
||||
<div class="col-xs-9 col-md-6">
|
||||
<h3>Note by {{ note.author.username }}</h3>
|
||||
<p>Message:</p>
|
||||
<p class="well">{{ note.message }}</p>
|
||||
{% if note.author == user %}
|
||||
<button id="editBtn" class="btn btn-primary" onClick="showNote();">Edit</button>
|
||||
{% endif %}
|
||||
<p>Created: {{ note.date }}</p>
|
||||
<p>Last Update: {{ note.last_update }}</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if not form.has_note and not form.show_note %}
|
||||
<div id="createDiv" class="row">
|
||||
<button class="btn btn-primary" onClick="showNote()">Create Note</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div id="noteDiv" class="row" {% if not form.show_note %}style="display: none;"{% endif %}>
|
||||
<br/>
|
||||
<h3>Note</h3>
|
||||
<form action="" method="POST">{% autoescape off %}{% get_csrf_html request %}{% endautoescape %}
|
||||
{{ form.note_form }}
|
||||
<br/>
|
||||
<button type="submit" class="btn btn-primary" name="note" value="{% if form.has_note %}edit{% else %}create{% endif %}">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script>
|
||||
$("#saveButton").hide();
|
||||
$("#ticketStaff").change(function() {
|
||||
var $priority = $("#ticketPriority");
|
||||
if ($(this).val() != "{{ ticket.staff.id }}" || $priority.val() != "{{ ticket.priority }}") {
|
||||
toggleSave(true);
|
||||
} else {
|
||||
toggleSave(false);
|
||||
}
|
||||
});
|
||||
$("#ticketPriority").change(function() {
|
||||
var $staff = $("#ticketStaff");
|
||||
if ($(this).val() != "{{ ticket.priority }}" || $staff.val() != "{{ ticket.staff.id }}") {
|
||||
toggleSave(true);
|
||||
} else {
|
||||
toggleSave(false);
|
||||
}
|
||||
});
|
||||
|
||||
function toggleSave(toggle) {
|
||||
if (toggle == true) {
|
||||
$("#resolveButton").hide();
|
||||
$("#saveButton").show();
|
||||
} else if (toggle == false) {
|
||||
$("#resolveButton").show();
|
||||
$("#saveButton").hide();
|
||||
}
|
||||
}
|
||||
|
||||
function showNote() {
|
||||
$("#noteDiv").show();
|
||||
$("#createDiv").hide();
|
||||
$("#editBtn").hide();
|
||||
}
|
||||
</script>
|
||||
{% endblock section %}
|
|
@ -0,0 +1,46 @@
|
|||
{% extends "minecraft_manager/dashboard.html" %}
|
||||
{% block section %}
|
||||
<div id="content" hidden="hidden">
|
||||
<table id="model-table" class="table table-hover link-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Player</th>
|
||||
<th>Message</th>
|
||||
<th>Severity</th>
|
||||
<td>Issued By</td>
|
||||
<th>Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for warning in warnings %}
|
||||
<tr {% if warning.severity == 'L' %}class="info"{% endif %}{% if warning.severity == 'M' %}class="warning"{% endif %}{% if warning.severity == 'H' %}class="danger"{% endif %} data-id="{{ warning.id }}">
|
||||
<!-- <td>{{ warning.id }}</td> -->
|
||||
<td>{{ warning.issuee }}</td>
|
||||
<td>{{ warning.snippet }}</td>
|
||||
<td>{{ warning.severity_display }}</td>
|
||||
<td>{{ warning.issuer }}</td>
|
||||
<td>{{ warning.date }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<a class="btn btn-primary" href="{% url "warning_add" %}">Add Warning</a>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#model-table").DataTable({
|
||||
'lengthMenu': [ [ 10, 25, 50, 100, -1 ], [ 10, 25, 50, 100, "All" ] ],
|
||||
'initComplete': function(settings, json) {
|
||||
$("#content").show();
|
||||
$(this).css('width', '');
|
||||
},
|
||||
'order': [],
|
||||
{% if user.usersettings %}
|
||||
'pageLength': {{ user.usersettings.default_results }},
|
||||
{% endif %}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock section %}
|
|
@ -0,0 +1,29 @@
|
|||
{% extends "minecraft_manager/dashboard.html" %}
|
||||
{% load csrf_html %}
|
||||
{% block section %}
|
||||
<div id="content">
|
||||
<h2 class="sub-header">New Warning</h2>
|
||||
<div class="row">
|
||||
<div class="col-xs-18 col-md-12">
|
||||
<form action="" method="post">{% autoescape off %}{% get_csrf_html request %}{% endautoescape %}
|
||||
<p><label for="">Player Filter:</label> <input id="id_filter" type="text"/></p>
|
||||
{{ form }}
|
||||
<button id="saveButton" class="btn btn-primary" type="submit">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-6 col-md-4">
|
||||
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-8">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#id_player").filterByText($("#id_filter"));
|
||||
});
|
||||
</script>
|
||||
{% endblock section %}
|
|
@ -0,0 +1,52 @@
|
|||
{% extends "minecraft_manager/dashboard.html" %}
|
||||
{% load csrf_html %}
|
||||
{% block section %}
|
||||
<div id="content">
|
||||
<h2 class="sub-header">Warning Info ({{ warning.player.username }})</h2>
|
||||
<div class="row">
|
||||
<div class="col-xs-6 col-md-4">
|
||||
<!-- <p>Username: {{ application.username }}</p> -->
|
||||
<p>Message: </p>
|
||||
<p class="well">{{ warning.message }}</p>
|
||||
<p>Warning Date: {{ warning.date }}</p>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-8">
|
||||
<form action="" method="post">{% autoescape off %}{% get_csrf_html request %}{% endautoescape %}
|
||||
<p>Issuer: {{ warning.staff.username }} </p>
|
||||
<p>Severity:
|
||||
{% if user.is_staff or user == warning.staff %}
|
||||
<select id="warningSeverity" name="severity">
|
||||
{% for p in form.severity %}
|
||||
<option value="{{ p.0 }}" {% if warning.severity == p.0 %}selected="selected"{% endif %}>{{ p.1 }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% else %}
|
||||
{{ warning.severity_display }}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% if user.is_staff or warning.staff and user == warning.staff %}
|
||||
<button id="saveButton" class="btn btn-primary" type="submit">Save</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-6 col-md-4">
|
||||
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-8">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$("#saveButton").hide();
|
||||
$("#warningSeverity").change(function() {
|
||||
if (("{{ user.username }}" == "{{ warning.staff.username }}" || "{{ user.is_staff }}" == "True") && $(this).val() != "{{ warning.severity }}") {
|
||||
$("#saveButton").show();
|
||||
} else {
|
||||
$("#saveButton").hide();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock section %}
|
|
@ -0,0 +1,36 @@
|
|||
from django.template import Library
|
||||
|
||||
from minecraft_manager.models import UserSettings
|
||||
from minecraft_manager.forms import UserSettingsForm
|
||||
from django.contrib.auth.forms import PasswordChangeForm
|
||||
|
||||
register = Library()
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def get_csrf_html(request):
|
||||
if request.COOKIES and request.COOKIES['csrftoken']:
|
||||
return "<input type='hidden' name='csrfmiddlewaretoken' value='%s' />" % request.COOKIES['csrftoken']
|
||||
return ""
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def get_csrf_token(request):
|
||||
if request.COOKIES and request.COOKIES['csrftoken']:
|
||||
return request.COOKIES['csrftoken']
|
||||
return ""
|
||||
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def get_form(form, request):
|
||||
|
||||
if form.lower() == 'usersettings' and request:
|
||||
try:
|
||||
return UserSettingsForm(instance=request.user.usersettings).as_p()
|
||||
except UserSettings.DoesNotExist:
|
||||
user_settings = UserSettings(auth_user=request.user)
|
||||
user_settings.save()
|
||||
return UserSettingsForm(instance=request.user.usersettings).as_p()
|
||||
elif form.lower() == "password":
|
||||
return PasswordChangeForm(request.user).as_p()
|
|
@ -0,0 +1,22 @@
|
|||
import re
|
||||
from django import template
|
||||
from django.conf import settings
|
||||
|
||||
numeric_test = re.compile("^\d+$")
|
||||
register = template.Library()
|
||||
|
||||
def getattribute(value, arg):
|
||||
"""Gets an attribute of an object dynamically from a string name"""
|
||||
|
||||
if hasattr(value, str(arg)):
|
||||
return getattr(value, arg)
|
||||
elif hasattr(value, 'has_key') and value.has_key(arg):
|
||||
return value[arg]
|
||||
elif arg in value:
|
||||
return value[arg]
|
||||
elif numeric_test.match(str(arg)) and len(value) > int(arg):
|
||||
return value[int(arg)]
|
||||
else:
|
||||
return settings.TEMPLATE_STRING_IF_INVALID
|
||||
|
||||
register.filter('getattribute', getattribute)
|
|
@ -0,0 +1,23 @@
|
|||
from django import template
|
||||
from django.template.defaultfilters import stringfilter
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.filter
|
||||
@stringfilter
|
||||
def pretty_stat(value):
|
||||
stat = value.split('.')
|
||||
parts = []
|
||||
for s in stat:
|
||||
s = s.replace('minecraft:', '')
|
||||
for part in s.split('_'):
|
||||
if part.lower() == 'custom':
|
||||
continue
|
||||
parts.append(part.title())
|
||||
return ' '.join(parts)
|
||||
|
||||
|
||||
@register.filter
|
||||
def pretty_score(value):
|
||||
return '{:,}'.format(value)
|
|
@ -0,0 +1,69 @@
|
|||
from django.template import Library
|
||||
from django.urls import reverse
|
||||
from minecraft_manager.models import Alert
|
||||
from django.conf import settings
|
||||
|
||||
register = Library()
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def get_sidebar(current_app, request):
|
||||
|
||||
# Get unseen Alerts
|
||||
unseen_alerts = Alert.objects.filter(user=request.user, seen=False)
|
||||
unseen_html = ""
|
||||
if len(unseen_alerts) > 0:
|
||||
unseen_html = " <span class='badge badge-light'>" + str(len(unseen_alerts)) + "</span>"
|
||||
|
||||
|
||||
ret = ""
|
||||
if current_app == 'overview':
|
||||
ret += "<li class=\"active\"><a href=\"" + reverse('overview') + "\">Overview</a></li>"
|
||||
else:
|
||||
ret += "<li><a href=\"" + reverse('overview') + "\">Overview</a></li>"
|
||||
if current_app == 'ban':
|
||||
ret += "<li class=\"active\"><a href=\"" + reverse('ban') + "\">Bans</a></li>"
|
||||
else:
|
||||
ret += "<li><a href=\"" + reverse('ban') + "\">Bans</a></li>"
|
||||
if current_app == 'alert':
|
||||
ret += "<li class=\"active\"><a href=\"" + reverse('alert') + "\">Alerts{0}</a></li>".format(unseen_html)
|
||||
else:
|
||||
ret += "<li><a href=\"" + reverse('alert') + "\">Alerts{0}</a></li>".format(unseen_html)
|
||||
|
||||
# Models
|
||||
if current_app == 'application':
|
||||
ret += "<li class=\"active\"><a href=\"" + reverse('application') + "\">Applications</a></li>"
|
||||
else:
|
||||
ret += "<li><a href=\"" + reverse('application') + "\">Applications</a></li>"
|
||||
if current_app == 'player':
|
||||
ret += "<li class=\"active\"><a href=\"" + reverse('player') + "\">Players</a></li>"
|
||||
else:
|
||||
ret += "<li><a href=\"" + reverse('player') + "\">Players</a></li>"
|
||||
if current_app == 'ticket':
|
||||
ret += "<li class=\"active\"><a href=\"" + reverse('ticket') + "\">Tickets</a></li>"
|
||||
else:
|
||||
ret += "<li><a href=\"" + reverse('ticket') + "\">Tickets</a></li>"
|
||||
if current_app == 'warning':
|
||||
ret += "<li class=\"active\"><a href=\"" + reverse('warning') + "\">Warnings</a></li>"
|
||||
else:
|
||||
ret += "<li><a href=\"" + reverse('warning') + "\">Warnings</a></li>"
|
||||
|
||||
# Split up MCM and "other"
|
||||
ret += "<hr/>"
|
||||
|
||||
if current_app == 'report':
|
||||
ret += "<li class=\"active\"><a href=\"" + reverse("report") + "\">Report</a></li>"
|
||||
else:
|
||||
ret += "<li><a href=\"" + reverse("report") + "\">Report</a></li>"
|
||||
show_chat = True if getattr(settings, 'GLOBAL_LOG', None) is not None else False
|
||||
if show_chat and request.user.has_perm('auth.chat'):
|
||||
if current_app == 'chat':
|
||||
ret += "<li class=\"active\"><a href=\"" + reverse("chat") + "\">Chat</a></li>"
|
||||
else:
|
||||
ret += "<li><a href=\"" + reverse("chat") + "\">Chat</a></li>"
|
||||
if request.user.has_perm('auth.bots'):
|
||||
if current_app == 'bots':
|
||||
ret += "<li class=\"active\"><a href=\"" + reverse("bots") + "\">Bots</a></li>"
|
||||
else:
|
||||
ret += "<li><a href=\"" + reverse("bots") + "\">Bots</a></li>"
|
||||
return ret
|
|
@ -0,0 +1,10 @@
|
|||
from django.template import Library
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
register = Library()
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def template_settings(prop):
|
||||
return getattr(settings, prop, "")
|
|
@ -0,0 +1,72 @@
|
|||
from django.conf.urls import url
|
||||
from django.views.generic import RedirectView
|
||||
from django.contrib.auth.decorators import login_required
|
||||
import minecraft_manager.views as mcm
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', RedirectView.as_view(pattern_name='overview')),
|
||||
#Dashboard
|
||||
url(r'^dashboard/overview/$', login_required(mcm.Overview.as_view()), name="overview"),
|
||||
url(r'^dashboard/ban/$', login_required(mcm.Ban.as_view()), name="ban"),
|
||||
#CoreProtect
|
||||
url(r'^dashboard/coreprotect/$', login_required(mcm.CoreProtect.as_view()), name="coreprotect"),
|
||||
url(r'^dashboard/activity/$', login_required(mcm.Activity.as_view()), name="activity"),
|
||||
#Alerts
|
||||
url(r'^dashboard/alert/$', login_required(mcm.Alert.as_view()), name="alert"),
|
||||
url(r'^dashboard/alert/(?P<alert_id>[0-9]{1,5})/$', login_required(mcm.AlertInfo.as_view())),
|
||||
#Applications
|
||||
url(r'^dashboard/application/$', login_required(mcm.Application.as_view()), name="application"),
|
||||
url(r'^dashboard/reference/$', login_required(mcm.Reference.as_view()), name="reference"),
|
||||
url(r'^dashboard/application/(?P<application_id>[0-9]{1,5})/$', login_required(mcm.ApplicationInfo.as_view())),
|
||||
#Players
|
||||
url(r'^dashboard/player/$', login_required(mcm.Player.as_view()), name="player"),
|
||||
url(r'^dashboard/player/(?P<player_id>[0-9]{1,5})/$', login_required(mcm.PlayerInfo.as_view())),
|
||||
#Tickets
|
||||
url(r'^dashboard/ticket/$', login_required(mcm.Ticket.as_view()), name="ticket"),
|
||||
url(r'^dashboard/ticket/(?P<ticket_id>[0-9]{1,5})/$', login_required(mcm.TicketInfo.as_view())),
|
||||
#Warnings
|
||||
url(r'^dashboard/warning/$', login_required(mcm.Warning.as_view()), name="warning"),
|
||||
url(r'^dashboard/warning/(?P<warning_id>[0-9]{1,5})/$', login_required(mcm.WarningInfo.as_view())),
|
||||
url(r'^dashboard/warning/add$', login_required(mcm.WarningAdd.as_view()), name="warning_add"),
|
||||
#Report
|
||||
url(r'^report/$', login_required(mcm.Report.as_view()), name="report"),
|
||||
#Chat
|
||||
url(r'^dashboard/chat/$', login_required(mcm.Chat.as_view()), name="chat"),
|
||||
#Bots
|
||||
url(r'^dashboard/bots/$', login_required(mcm.Bots.as_view()), name="bots"),
|
||||
]
|
||||
|
||||
|
||||
|
||||
# Possible future feature
|
||||
# from django.conf import settings
|
||||
#
|
||||
# LOGIN_REQUIRED = settings.LOGIN_REQUIRED if hasattr(settings, 'LOGIN_REQUIRED') else False
|
||||
#
|
||||
# urlpatterns = [
|
||||
# #Dashboard
|
||||
# url(r'^dashboard/overview/$', login_required(mcm.Overview.as_view()) if LOGIN_REQUIRED else mcm.Overview.as_view(), name="overview"),
|
||||
# url(r'^dashboard/coreprotect/$', login_required(mcm.CoreProtect.as_view()) if LOGIN_REQUIRED else mcm.CoreProtect.as_view(), name="coreprotect"),
|
||||
# url(r'^dashboard/activity/$', login_required(mcm.Activity.as_view()) if LOGIN_REQUIRED else mcm.Activity.as_view(), name="activity"),
|
||||
# url(r'^dashboard/ban/$', login_required(mcm.Ban.as_view()) if LOGIN_REQUIRED else mcm.Ban.as_view(), name="ban"),
|
||||
# #Alerts
|
||||
# url(r'^dashboard/alert/$', login_required(mcm.Alert.as_view()) if LOGIN_REQUIRED else mcm.Alert.as_view(), name="alert"),
|
||||
# url(r'^dashboard/alert/(?P<alert_id>[0-9]{1,5})/$', login_required(mcm.AlertInfo.as_view()) if LOGIN_REQUIRED else mcm.AlertInfo.as_view()),
|
||||
# #Applications
|
||||
# url(r'^dashboard/application/$', login_required(mcm.Application.as_view()) if LOGIN_REQUIRED else mcm.Application.as_view(), name="application"),
|
||||
# url(r'^dashboard/application/(?P<application_id>[0-9]{1,5})/$', login_required(mcm.ApplicationInfo.as_view()) if LOGIN_REQUIRED else mcm.ApplicationInfo.as_view()),
|
||||
# #Players
|
||||
# url(r'^dashboard/player/$', login_required(mcm.Player.as_view()) if LOGIN_REQUIRED else mcm.Player.as_view(), name="player"),
|
||||
# url(r'^dashboard/player/(?P<player_id>[0-9]{1,5})/$', login_required(mcm.PlayerInfo.as_view()) if LOGIN_REQUIRED else mcm.PlayerInfo.as_view()),
|
||||
# #Tickets
|
||||
# url(r'^dashboard/ticket/$', login_required(mcm.Ticket.as_view()) if LOGIN_REQUIRED else mcm.Ticket.as_view(), name="ticket"),
|
||||
# url(r'^dashboard/ticket/(?P<ticket_id>[0-9]{1,5})/$', login_required(mcm.TicketInfo.as_view()) if LOGIN_REQUIRED else mcm.TicketInfo.as_view()),
|
||||
# #Warnings
|
||||
# url(r'^dashboard/warning/$', login_required(mcm.Warning.as_view()) if LOGIN_REQUIRED else mcm.Warning.as_view(), name="warning"),
|
||||
# url(r'^dashboard/warning/(?P<warning_id>[0-9]{1,5})/$', login_required(mcm.WarningInfo.as_view()) if LOGIN_REQUIRED else mcm.WarningInfo.as_view()),
|
||||
# url(r'^dashboard/warning/add$', login_required(mcm.WarningAdd.as_view()) if LOGIN_REQUIRED else mcm.WarningAdd.as_view(), name="warning_add"),
|
||||
# #Chat
|
||||
# url(r'^dashboard/chat/$', login_required(mcm.Chat.as_view()) if LOGIN_REQUIRED else mcm.Chat.as_view(), name="chat"),
|
||||
# #Bots
|
||||
# url(r'^dashboard/bots/$', login_required(mcm.Bots.as_view()) if LOGIN_REQUIRED else mcm.Bots.as_view(), name="bots"),
|
||||
# ]
|
|
@ -0,0 +1,103 @@
|
|||
import discord, requests
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
def build_application(application):
|
||||
embed = discord.Embed(colour=discord.Colour(0x417505))
|
||||
embed.title = "Application"
|
||||
embed.set_thumbnail(
|
||||
url="https://minotar.net/helm/{0}/100.png".format(application.username))
|
||||
embed.add_field(name="Application ID", value=application.id)
|
||||
embed.add_field(name="Username", value=application.username.replace("_", "\\_"))
|
||||
embed.add_field(name="Age", value=application.age)
|
||||
embed.add_field(name="Type of Player", value=application.player_type)
|
||||
embed.add_field(name="Ever been banned", value=application.ever_banned)
|
||||
if application.ever_banned:
|
||||
embed.add_field(name="Reason for being banned", value=application.ever_banned_explanation)
|
||||
embed.add_field(name="Reference", value=application.reference)
|
||||
embed.add_field(name="Read the Rules", value=application.read_rules)
|
||||
embed.add_field(name="Date", value=application.date_display)
|
||||
embed.add_field(name="Status", value=application.status)
|
||||
return [embed.to_dict()]
|
||||
|
||||
|
||||
def build_ticket(ticket, link):
|
||||
embed = discord.Embed(colour=discord.Colour(0x417505))
|
||||
embed.title = "Ticket"
|
||||
embed.set_thumbnail(
|
||||
url="https://cdn.discordapp.com/avatars/454457830918062081/b5792489bc43d9e17b8f657880a17dd4.png")
|
||||
embed.add_field(name="Date", value=ticket.date_display)
|
||||
embed.add_field(name="Player", value=ticket.player.username.replace("_", "\\_"))
|
||||
embed.add_field(name="Priority", value=ticket.priority_display)
|
||||
if ticket.x and ticket.y and ticket.z and ticket.world:
|
||||
embed.add_field(name="Location", value=ticket.location)
|
||||
embed.add_field(name="Message", value=ticket.message)
|
||||
embed.add_field(name="Link", value=link)
|
||||
return [embed.to_dict()]
|
||||
|
||||
|
||||
def build_warning(warning, link):
|
||||
embed = discord.Embed(colour=discord.Colour(0x417505))
|
||||
embed.title = "Warning"
|
||||
embed.set_thumbnail(
|
||||
url="https://cdn.discordapp.com/avatars/454457830918062081/b5792489bc43d9e17b8f657880a17dd4.png")
|
||||
embed.add_field(name="Date", value=warning.date_display)
|
||||
embed.add_field(name="Player", value=warning.player.username.replace("_", "\\_"))
|
||||
embed.add_field(name="Severity", value=warning.severity_display)
|
||||
embed.add_field(name="Message", value=warning.message)
|
||||
embed.add_field(name="Link", value=link)
|
||||
return [embed.to_dict()]
|
||||
|
||||
|
||||
def validate_username(username):
|
||||
response = requests.get("https://api.mojang.com/users/profiles/minecraft/{}".format(username))
|
||||
if response.status_code == 200:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def url_path(*args):
|
||||
value = []
|
||||
for arg in args:
|
||||
arg = str(arg)
|
||||
if arg.startswith('/'):
|
||||
arg = arg[1:]
|
||||
if arg.endswith('/'):
|
||||
arg = arg[:-1]
|
||||
value.append(arg)
|
||||
return '/'.join(value)
|
||||
|
||||
|
||||
class Captcha:
|
||||
success = False
|
||||
challenge_ts = None
|
||||
hostname = None
|
||||
errors = []
|
||||
|
||||
def __init__(self, post):
|
||||
if not hasattr(settings, 'CAPTCHA_SECRET'):
|
||||
self.success = True
|
||||
if 'g-recaptcha-response' in post:
|
||||
response = requests.post("https://www.google.com/recaptcha/api/siteverify",
|
||||
{"secret": settings.CAPTCHA_SECRET, "response": post['g-recaptcha-response']})
|
||||
json = response.json()
|
||||
self.success = json['success']
|
||||
self.challenge_ts = json['challenge_ts'] if 'challenge_ts' in json else None
|
||||
self.hostname = json['hostname'] if 'hostname' in json else None
|
||||
|
||||
if 'error-codes' in json:
|
||||
codes = json['error-codes']
|
||||
if 'missing-input-secret' in codes:
|
||||
self.add_error('The secret parameter is missing.')
|
||||
if 'invalid-input-secret' in codes:
|
||||
self.add_error('The secret parameter is invalid or malformed.')
|
||||
if 'missing-input-response' in codes:
|
||||
self.add_error('The reCAPTCHA must be filled out.')
|
||||
if 'invalid-input-response' in codes:
|
||||
self.add_error('The response parameter is invalid or malformed.')
|
||||
if 'bad-request' in codes:
|
||||
self.add_error('The request is invalid or malformed.')
|
||||
|
||||
def add_error(self, error):
|
||||
if error not in self.errors:
|
||||
self.errors.append(error)
|
|
@ -0,0 +1,497 @@
|
|||
#create your views here.
|
||||
# https://api.mojang.com/users/profiles/minecraft/<username>
|
||||
from __future__ import absolute_import
|
||||
|
||||
import json, datetime, pytz, os, sys
|
||||
from django.utils import timezone
|
||||
from itertools import chain
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import render, reverse, redirect
|
||||
from django.views.decorators.csrf import csrf_protect
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.conf import settings
|
||||
from django.views.generic import View
|
||||
from django.contrib.auth.models import User
|
||||
from minecraft_manager.models import Application as AppModel, Player as PlayerModel, Ticket as TicketModel, Warning as WarningModel, IP as IPModel, Alert as AlertModel, Note as NoteModel, UserSettings as UserSettingsModel
|
||||
from minecraft_manager.forms import WarningForm, NoteForm
|
||||
import minecraft_manager.api.api as API
|
||||
|
||||
import subprocess
|
||||
|
||||
|
||||
class Overview(View):
|
||||
|
||||
def get(self, request):
|
||||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||
if x_forwarded_for:
|
||||
user_ip = x_forwarded_for.split(',')[0]
|
||||
else:
|
||||
user_ip = request.META.get('REMOTE_ADDR')
|
||||
try:
|
||||
request.user.usersettings.last_ip = user_ip
|
||||
except:
|
||||
request.user.usersettings = UserSettingsModel(auth_user=request.user)
|
||||
request.user.usersettings.last_ip = user_ip
|
||||
request.user.usersettings.save()
|
||||
unanswered_apps = AppModel.unanswered.count()
|
||||
accepted_apps = AppModel.objects.filter(accepted=True).count()
|
||||
denied_apps = AppModel.objects.filter(accepted=False).count()
|
||||
unclaimed_tickets = TicketModel.unclaimed.count()
|
||||
claimed_tickets = TicketModel.claimed.count()
|
||||
resolved_tickets = TicketModel.objects.filter(resolved=True).count()
|
||||
counts = {'applications': AppModel.objects.count(), 'players': PlayerModel.objects.count(),
|
||||
"ips": IPModel.objects.count(), "tickets": TicketModel.objects.count(),
|
||||
"warnings": WarningModel.objects.count()}
|
||||
form = {'apps': {'unanswered': unanswered_apps, 'accepted': accepted_apps, 'denied': denied_apps},
|
||||
'tickets': {'unclaimed': unclaimed_tickets, 'claimed': claimed_tickets, 'resolved': resolved_tickets},
|
||||
'counts': counts}
|
||||
return render(request, 'minecraft_manager/overview.html', {'current_app': 'overview', 'form': form})
|
||||
|
||||
class CoreProtect(View):
|
||||
|
||||
def get(self, request):
|
||||
#http://www.24carrotcraft.com/assets/cp/index.php?username=etzelia
|
||||
return render(request, 'minecraft_manager/coreprotect.html', {'current_app': 'coreprotect'})
|
||||
|
||||
class Activity(View):
|
||||
|
||||
def get(self, request):
|
||||
#http://www.24carrotcraft.com/assets/cp/activity.php?username=etzelia
|
||||
return render(request, 'minecraft_manager/activity.html', {'current_app': 'activity'})
|
||||
|
||||
class Ban(View):
|
||||
|
||||
def get(self, request):
|
||||
ban_file = os.path.join(settings.MINECRAFT_BASE_DIR, 'banned-players.json')
|
||||
with open(ban_file, encoding='utf-8') as f:
|
||||
bans = json.load(f)
|
||||
for idx, ban in enumerate(bans):
|
||||
ban['source'] = API.strip_format(ban['source'])
|
||||
if PlayerModel.objects.filter(uuid=ban['uuid']).exists():
|
||||
ban['id'] = PlayerModel.objects.get(uuid=ban['uuid']).id
|
||||
else:
|
||||
ban['id'] = 0
|
||||
ban['source'] = API.strip_format(ban['source'])
|
||||
if ban['expires'] == "forever":
|
||||
ban['expires'] = "Permanent"
|
||||
else:
|
||||
dt = datetime.datetime.strptime(ban['expires'], "%Y-%m-%d %H:%M:%S %z")
|
||||
dt = dt.astimezone(pytz.timezone(request.user.usersettings.default_timezone))
|
||||
ban['expires'] = dt.strftime("%m/%d/%y %I:%M %p")
|
||||
dt = datetime.datetime.strptime(ban['created'], "%Y-%m-%d %H:%M:%S %z")
|
||||
dt = dt.astimezone(pytz.timezone(request.user.usersettings.default_timezone))
|
||||
ban['created'] = dt.strftime("%m/%d/%y %I:%M %p")
|
||||
bans[idx] = ban
|
||||
bans = sorted(bans, key=lambda ban: ban['created'], reverse=True)
|
||||
return render(request, 'minecraft_manager/ban.html', {'current_app': 'ban', 'bans': bans})
|
||||
|
||||
|
||||
class Alert(View):
|
||||
|
||||
def get(self, request):
|
||||
alerts = AlertModel.objects.filter(user=request.user).order_by('seen', '-id')
|
||||
unseen_alerts = AlertModel.objects.filter(user=request.user, seen=False)
|
||||
return render(request, 'minecraft_manager/alert.html',
|
||||
{'current_app': 'alert', 'alerts': alerts, 'unseen': len(unseen_alerts)})
|
||||
|
||||
def post(self, request):
|
||||
post = request.POST
|
||||
if 'action' in post:
|
||||
if post['action'] == 'mar':
|
||||
AlertModel.objects.filter(user=request.user).update(seen=True)
|
||||
alerts = AlertModel.objects.filter(user=request.user).order_by('seen', '-id')
|
||||
return render(request, 'minecraft_manager/alert.html',
|
||||
{'current_app': 'alert', 'alerts': alerts, 'unseen': 0})
|
||||
|
||||
|
||||
class AlertInfo(View):
|
||||
|
||||
def get(self, request, alert_id):
|
||||
alert = AlertModel.objects.get(id=alert_id)
|
||||
alert.seen = True
|
||||
alert.save()
|
||||
return render(request, 'minecraft_manager/alert_info.html',
|
||||
{'current_app': 'alert', 'alert': alert})
|
||||
|
||||
def post(self, request, alert_id):
|
||||
post = request.POST
|
||||
alert = AlertModel.objects.get(id=alert_id)
|
||||
if 'action' in post:
|
||||
if post['action'] == 'mu':
|
||||
alert.seen = False
|
||||
alert.save()
|
||||
return render(request, 'minecraft_manager/alert_info.html',
|
||||
{'current_app': 'alert', 'alert': alert})
|
||||
|
||||
|
||||
class Application(View):
|
||||
|
||||
def get(self, request):
|
||||
get = request.GET
|
||||
if 'accepted' in get:
|
||||
if get['accepted'].lower() == 'true':
|
||||
applications = AppModel.objects.filter(accepted=True)
|
||||
elif get['accepted'].lower() == 'false':
|
||||
applications = AppModel.objects.filter(accepted=False)
|
||||
else:
|
||||
applications = AppModel.objects.filter(accepted__isnull=True)
|
||||
else:
|
||||
applications1 = AppModel.objects.filter(accepted__isnull=True)
|
||||
applications2 = AppModel.objects.filter(accepted__isnull=False)
|
||||
applications = list(chain(applications1, applications2))
|
||||
return render(request, 'minecraft_manager/application.html', {'current_app': 'application', 'applications': applications})
|
||||
|
||||
|
||||
class Reference(View):
|
||||
|
||||
def get(self, request):
|
||||
get = request.GET
|
||||
applications = AppModel.objects.all()
|
||||
return render(request, 'minecraft_manager/reference.html', {'current_app': 'application', 'applications': applications})
|
||||
|
||||
|
||||
class ApplicationInfo(View):
|
||||
|
||||
@method_decorator(csrf_protect)
|
||||
def get(self, request, application_id):
|
||||
application = AppModel.objects.get(id=application_id)
|
||||
return render(request, 'minecraft_manager/application_info.html', {'current_app': 'application', 'application': application})
|
||||
|
||||
def post(self, request, application_id):
|
||||
post = request.POST
|
||||
application = AppModel.objects.get(id=application_id)
|
||||
if post['accept']:
|
||||
if post['accept'] == 'accept':
|
||||
application.accepted = True
|
||||
if post['accept'] == 'deny':
|
||||
application.accepted = False
|
||||
application.save()
|
||||
API.plugin(post['accept'], application.username)
|
||||
API.discord_mcm("Application #**{0}** was **{1}** by **{2}**".format(application.id, "Accepted" if application.accepted else "Denied", request.user.player.username))
|
||||
return render(request, 'minecraft_manager/application_info.html',
|
||||
{'current_app': 'application', 'application': application})
|
||||
|
||||
|
||||
|
||||
class Player(View):
|
||||
|
||||
def get(self, request):
|
||||
players = PlayerModel.objects.all()
|
||||
ban_file = os.path.join(settings.MINECRAFT_BASE_DIR, 'banned-players.json')
|
||||
with open(ban_file, encoding='utf-8') as f:
|
||||
ban_list = json.load(f)
|
||||
bans = []
|
||||
for ban in ban_list:
|
||||
bans.append(ban['uuid'])
|
||||
return render(request, 'minecraft_manager/player.html', {'current_app': 'player', 'players': players, 'bans': bans})
|
||||
|
||||
|
||||
class PlayerInfo(View):
|
||||
|
||||
def get(self, request, player_id):
|
||||
player = PlayerModel.objects.get(id=player_id)
|
||||
ips = IPModel.objects.filter(player=player)
|
||||
tickets = TicketModel.objects.filter(player=player)
|
||||
warnings = WarningModel.objects.filter(player=player)
|
||||
form = {'ips': ips, 'tickets': tickets, 'warnings': warnings}
|
||||
return render(request, 'minecraft_manager/player_info.html', {'current_app': 'player', 'player': player, 'form': form})
|
||||
|
||||
def post(self, request, player_id):
|
||||
player = PlayerModel.objects.get(id=player_id)
|
||||
return render(request, 'minecraft_manager/player_info.html', {'current_app': 'player', 'player': player})
|
||||
|
||||
|
||||
class Ticket(View):
|
||||
|
||||
def get(self, request):
|
||||
get = request.GET
|
||||
if 'claimed' in get:
|
||||
if get['claimed'].lower() == 'true':
|
||||
tickets = TicketModel.claimed.all()
|
||||
elif get['claimed'].lower() == 'false':
|
||||
tickets = TicketModel.unclaimed.all()
|
||||
else:
|
||||
tickets = TicketModel.objects.filter(resolved=True)
|
||||
else:
|
||||
tickets1 = TicketModel.objects.filter(resolved=False).order_by('-id')
|
||||
tickets2 = TicketModel.objects.filter(resolved=True).order_by('-id')
|
||||
tickets = list(chain(tickets1, tickets2))
|
||||
return render(request, 'minecraft_manager/ticket.html', {'current_app': 'ticket', 'tickets': tickets})
|
||||
|
||||
|
||||
class TicketInfo(View):
|
||||
|
||||
@method_decorator(csrf_protect)
|
||||
def get(self, request, ticket_id):
|
||||
ticket = TicketModel.objects.get(id=ticket_id)
|
||||
active_staff = User.objects.filter(is_active=True)
|
||||
inactive_staff = User.objects.filter(is_active=False)
|
||||
notes = NoteModel.objects.filter(ref_id=ticket_id, ref_table='TI')
|
||||
note_form = NoteForm(instance=request.user)
|
||||
has_note = False
|
||||
for note in notes:
|
||||
if note.author == request.user:
|
||||
note_form = NoteForm(instance=note)
|
||||
has_note = True
|
||||
if not has_note:
|
||||
note_form.fields['ref_table'].initial = 'TI'
|
||||
note_form.fields['ref_id'].initial = ticket_id
|
||||
form = {'active_staff': active_staff, 'inactive_staff': inactive_staff, 'priority': TicketModel.PRIORITY,
|
||||
'resolved': ticket.resolved, 'note_form': note_form.as_p(), 'notes': notes, 'has_note': has_note,
|
||||
'show_note': False}
|
||||
return render(request, 'minecraft_manager/ticket_info.html', {'current_app': 'ticket', 'ticket': ticket, 'form': form})
|
||||
|
||||
def post(self, request, ticket_id):
|
||||
post = request.POST
|
||||
ticket = TicketModel.objects.get(id=ticket_id)
|
||||
if 'priority' in post:
|
||||
if post['priority'] != ticket.priority:
|
||||
API.discord_mcm(
|
||||
"Ticket #**{0}**'s priority was changed from **{1}** to **{2}** by **{3}**".format(ticket.id,
|
||||
ticket.priority_display,
|
||||
TicketModel.priority_code_to_display(
|
||||
post['priority']),
|
||||
request.user.username))
|
||||
ticket.priority = post['priority']
|
||||
if 'staff' in post and 'resolved' not in post:
|
||||
if post['staff']:
|
||||
staff = User.objects.get(id=post['staff'])
|
||||
if post['staff'] != str(getattr(ticket.staff, 'id', '-1')):
|
||||
if post['staff'] == str(request.user.id):
|
||||
API.discord_mcm(
|
||||
"Ticket #**{0}** was claimed by **{1}**".format(ticket.id, request.user.username))
|
||||
else:
|
||||
API.discord_mcm(
|
||||
"Ticket #**{0}** was given to **{1}** by **{2}**".format(ticket.id, staff.username, request.user.username))
|
||||
else:
|
||||
staff = None
|
||||
API.discord_mcm(
|
||||
"Ticket #**{0}** was unclaimed by **{1}**".format(ticket.id, request.user.username))
|
||||
ticket.staff = staff
|
||||
if 'resolved' in post:
|
||||
API.discord_mcm("Ticket #**{0}** was resolved by **{1}**".format(ticket.id, request.user.username))
|
||||
ticket.resolved = True
|
||||
ticket.save()
|
||||
|
||||
show_note = False
|
||||
if 'note' in post:
|
||||
note_form = NoteForm(post)
|
||||
if note_form.is_valid():
|
||||
n = note_form.save(commit=False)
|
||||
if post['note'] == 'create':
|
||||
n.author = request.user
|
||||
n.save()
|
||||
elif post['note'] == 'edit':
|
||||
db = NoteModel.objects.get(ref_id=ticket_id, ref_table='TI', author=request.user)
|
||||
db.message = n.message
|
||||
db.last_update = timezone.now()
|
||||
db.save()
|
||||
else:
|
||||
show_note = True
|
||||
else:
|
||||
note_form = NoteForm(instance=request.user)
|
||||
notes = NoteModel.objects.filter(ref_id=ticket_id, ref_table='TI')
|
||||
has_note = False
|
||||
for note in notes:
|
||||
if note.author == request.user:
|
||||
note_form = NoteForm(instance=note)
|
||||
has_note = True
|
||||
if not has_note:
|
||||
note_form.fields['ref_table'].initial = 'TI'
|
||||
note_form.fields['ref_id'].initial = ticket_id
|
||||
active_staff = User.objects.filter(is_active=True)
|
||||
inactive_staff = User.objects.filter(is_active=False)
|
||||
form = {'active_staff': active_staff, 'inactive_staff': inactive_staff, 'priority': TicketModel.PRIORITY,
|
||||
'resolved': ticket.resolved, 'note_form': note_form.as_p(), 'notes': notes, 'has_note': has_note,
|
||||
'show_note': show_note}
|
||||
return render(request, 'minecraft_manager/ticket_info.html',
|
||||
{'current_app': 'ticket', 'ticket': ticket, 'form': form})
|
||||
|
||||
|
||||
class Warning(View):
|
||||
|
||||
def get(self, request):
|
||||
warnings = WarningModel.objects.order_by('-id')
|
||||
return render(request, 'minecraft_manager/warning.html', {'current_app': 'warning', 'warnings': warnings})
|
||||
|
||||
|
||||
class WarningInfo(View):
|
||||
|
||||
@method_decorator(csrf_protect)
|
||||
def get(self, request, warning_id):
|
||||
warning = WarningModel.objects.get(id=warning_id)
|
||||
form = {'severity': WarningModel.SEVERITY}
|
||||
return render(request, 'minecraft_manager/warning_info.html', {'current_app': 'warning', 'form': form, 'warning': warning})
|
||||
|
||||
def post(self, request, warning_id):
|
||||
post = request.POST
|
||||
warning = WarningModel.objects.get(id=warning_id)
|
||||
if 'severity' in post:
|
||||
API.discord_mcm("Warning #**{0}**'s severity was changed from {1} to {2} by {3}".format(warning.id,
|
||||
warning.severity_display,
|
||||
WarningModel.severity_code_to_display(
|
||||
post[
|
||||
'severity']),
|
||||
request.user.player.username))
|
||||
warning.severity = post['severity']
|
||||
warning.save()
|
||||
form = {'severity': WarningModel.SEVERITY}
|
||||
return render(request, 'minecraft_manager/warning_info.html',
|
||||
{'current_app': 'warning', 'form': form, 'warning': warning})
|
||||
|
||||
|
||||
class WarningAdd(View):
|
||||
|
||||
@method_decorator(csrf_protect)
|
||||
def get(self, request):
|
||||
form = WarningForm().as_p()
|
||||
return render(request, 'minecraft_manager/warning_add.html',
|
||||
{'current_app': 'warning', 'form': form})
|
||||
|
||||
def post(self, request):
|
||||
post = request.POST
|
||||
form = WarningForm(post)
|
||||
if form.is_valid():
|
||||
warning = form.save()
|
||||
warning.staff = request.user
|
||||
warning.save()
|
||||
API.discord_mcm(
|
||||
"**{0}** issued a **{1}** severity warning to **{2}**\nPreview: {3}".format(warning.staff.player.username,
|
||||
warning.severity_display,
|
||||
warning.player.username,
|
||||
warning.snippet))
|
||||
return redirect("{0}{1}".format(reverse('warning'), warning.id))
|
||||
else:
|
||||
return render(request, 'minecraft_manager/warning_add.html',
|
||||
{'current_app': 'warning', 'form': form})
|
||||
|
||||
|
||||
class Report(View):
|
||||
|
||||
def get(self, request):
|
||||
get = request.GET
|
||||
results = []
|
||||
timestamp = None
|
||||
report = get.get('report')
|
||||
world = get.get('world')
|
||||
if report and world:
|
||||
# Get some results
|
||||
report_dir = os.path.join(settings.MINECRAFT_BASE_DIR, 'plugins', 'MinecraftManager', 'report.json')
|
||||
if os.path.exists(report_dir):
|
||||
with open(report_dir) as rpt:
|
||||
raw = json.load(rpt)
|
||||
time = raw.get('time')
|
||||
timestamp = datetime.datetime.utcfromtimestamp(float(time))
|
||||
timestamp = pytz.timezone('UTC').localize(timestamp)
|
||||
timestamp = timestamp.astimezone(pytz.timezone(request.user.usersettings.default_timezone))
|
||||
for w in raw:
|
||||
if w.endswith('_nether') and world == 'nether':
|
||||
world_raw = raw[w]
|
||||
elif w.endswith('_the_end') and world == 'end':
|
||||
world_raw = raw[w]
|
||||
elif w != 'time' and not (w.endswith('_nether') or w.endswith('_the_end')) and world == 'overworld': # Need to think of a way to clean this up...
|
||||
world_raw = raw[w]
|
||||
break
|
||||
results_raw = world_raw.get(report)
|
||||
for result in results_raw:
|
||||
if report == 'players':
|
||||
results.append({'name': result.get('name'), 'x': result.get('x'), 'y': result.get('y'),
|
||||
'z': result.get('z')})
|
||||
else:
|
||||
type_formatted = ' '.join([part.title() for part in result.split('_')])
|
||||
if report == 'entities':
|
||||
for location in results_raw[result]:
|
||||
results.append({'name': type_formatted, 'x': location.get('x'), 'y': location.get('y'),
|
||||
'z': location.get('z')})
|
||||
else:
|
||||
results.append({'name': type_formatted, 'count': results_raw.get(result)})
|
||||
|
||||
else:
|
||||
results = 'NONE'
|
||||
elif report or world:
|
||||
results = 'BOTH'
|
||||
if len(results) == 0:
|
||||
results = 'EMPTY'
|
||||
return render(request, 'minecraft_manager/report.html', {'current_app': 'report', 'report': report, 'world': world, 'timestamp': timestamp, 'results': results})
|
||||
|
||||
def post(self, request):
|
||||
pass
|
||||
|
||||
|
||||
class Chat(View):
|
||||
|
||||
|
||||
@staticmethod
|
||||
def replace_ascii(message):
|
||||
return message.replace(" ", "\\040").replace("\"", "\\042").replace("#", "\\043").replace("$", "\\044")\
|
||||
.replace("%", "\\045").replace("&", "\\046").replace("(", "\\050").replace(")", "\\051").replace("*", "\\052")\
|
||||
.replace("+", "\\053").replace("-", "\\055")
|
||||
|
||||
def get(self, request):
|
||||
staff = hasattr(settings, 'STAFF_LOG')
|
||||
return render(request, 'minecraft_manager/chat.html', {'current_app': 'chat', 'staff': staff})
|
||||
|
||||
def post(self, request):
|
||||
post = request.POST
|
||||
if 'chat' in post and 'message' in post:
|
||||
API.plugin(post['chat'], "{0} {1}".format(request.user.player.username, post['message']))
|
||||
data = {'success': True, 'message': 'Message sent successfully.'}
|
||||
else:
|
||||
data = {'success': False, 'message': 'No chat type or message set.'}
|
||||
return JsonResponse(data)
|
||||
|
||||
|
||||
class Bots(View):
|
||||
|
||||
def assets(self):
|
||||
path = os.path.abspath(os.path.dirname(__file__))
|
||||
bot_dir = os.path.join(path, 'assets/bots')
|
||||
return bot_dir
|
||||
|
||||
def get_bots(self):
|
||||
bot_dir = getattr(settings, 'BOT_DIR', None)
|
||||
bots = []
|
||||
if bot_dir:
|
||||
for file in os.listdir(bot_dir):
|
||||
if file.endswith('.bot.py'):
|
||||
ve = file.replace('.bot.py', '')
|
||||
py = os.path.join(bot_dir, ve, 'bin/python')
|
||||
if os.path.isfile(py):
|
||||
bots.append({'name': file.replace('.bot.py', ''), 'asset': False, 'executable': py})
|
||||
else:
|
||||
bots.append({'name': file.replace('.bot.py', ''), 'asset': False, 'executable': sys.executable})
|
||||
# Also get packaged MCM bots
|
||||
for file in os.listdir(self.assets()):
|
||||
if file.endswith('.bot.py'):
|
||||
bots.append({'name': file.replace('.bot.py', ''), 'asset': True, 'executable': sys.executable})
|
||||
return bots
|
||||
|
||||
def get_form(self):
|
||||
bots = self.get_bots()
|
||||
plugin_port = getattr(settings, 'PLUGIN_PORT', None)
|
||||
screens = subprocess.getoutput("screen -ls")
|
||||
form = {'screens': screens, 'bots': bots}
|
||||
for bot in bots:
|
||||
form[bot['name']] = True if "{0}_{1}".format(plugin_port, bot['name']) in screens else False
|
||||
return form
|
||||
|
||||
def get(self, request):
|
||||
return render(request, 'minecraft_manager/bots.html', {'current_app': 'bots', 'form': self.get_form()})
|
||||
|
||||
def post(self, request):
|
||||
post = request.POST
|
||||
plugin_port = getattr(settings, 'PLUGIN_PORT', None)
|
||||
for bot in self.get_bots():
|
||||
if bot['name'] in post:
|
||||
if post[bot['name']] == "stop":
|
||||
subprocess.run('screen -X -S "{0}_{1}" quit'.format(plugin_port, bot['name']), shell=True)
|
||||
elif post[bot['name']] in ('start', 'restart'):
|
||||
subprocess.run('screen -X -S "{0}_{1}" quit'.format(plugin_port, bot['name']), shell=True)
|
||||
path = self.assets() if bot['asset'] else getattr(settings, 'BOT_DIR', "")
|
||||
if not path.endswith("/"):
|
||||
path += "/"
|
||||
print('screen -S {0}_{1} -d -m {2} {3}{1}.bot.py'.format(plugin_port, bot['name'], bot['executable'], path))
|
||||
subprocess.run(
|
||||
'screen -S {0}_{1} -d -m {2} {3}{1}.bot.py'.format(plugin_port, bot['name'], bot['executable'], path),
|
||||
shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
return render(request, 'minecraft_manager/bots.html', {'current_app': 'bots', 'form': self.get_form()})
|
Loading…
Reference in New Issue