from __future__ import absolute_import import json, datetime, pytz, os from django.utils import timezone from itertools import chain from django.http import JsonResponse, HttpResponse 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, TicketNote as TicketNoteModel, Note as NoteModel, IP as IPModel, Alert as AlertModel, UserSettings as UserSettingsModel, Attachment as AttachmentModel, RefModels from minecraft_manager.forms import TicketNoteForm, NoteForm from minecraft_manager.overview import overview_data from minecraft_manager.utils import resolve_player, build_note, full_reverse import minecraft_manager.api.api as API 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() return render(request, 'minecraft_manager/overview.html', {'current_app': 'overview', 'data': overview_data()}) class CoreProtect(View): def get(self, request): return render(request, 'minecraft_manager/coreprotect.html', {'current_app': 'coreprotect'}) class Activity(View): def get(self, request): 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): 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) link = full_reverse('application_info', application_id) API.discord_mcm("[Application #**{0}**]({3}) was **{1}** by **{2}**".format(application.id, "Accepted" if application.accepted else "Denied", request.user.player.username, link)) return render(request, 'minecraft_manager/application_info.html', {'current_app': 'application', 'application': application}) class Player(View): def get(self, request): get = request.GET if 'search' in get: search = get['search'] player = resolve_player(search) if player: return redirect('{}{}/'.format(reverse('player'), player.id)) else: players = PlayerModel.objects.filter(username__icontains=search) else: 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 _info(self, request, player_id): player = PlayerModel.objects.get(id=player_id) ips = IPModel.api.filter(player=player) tickets = TicketModel.objects.filter(player=player) notes = NoteModel.objects.filter(player=player) form = {'ips': ips, 'tickets': tickets, 'notes': notes} return render(request, 'minecraft_manager/player_info.html', {'current_app': 'player', 'player': player, 'form': form}) def get(self, request, player_id): return self._info(request, player_id) def post(self, request, player_id): return self._info(request, player_id) class Ticket(View): def get(self, request): 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) ticket_notes = TicketNoteModel.objects.filter(ticket=ticket) ticket_note_form = TicketNoteForm(instance=request.user) has_ticket_note = False for ticket_note in ticket_notes: if ticket_note.author == request.user: ticket_note_form = TicketNoteForm(instance=ticket_note) has_ticket_note = True if not has_ticket_note: ticket_note_form.fields['ticket'].initial = ticket_id form = {'active_staff': active_staff, 'inactive_staff': inactive_staff, 'priority': TicketModel.PRIORITY, 'resolved': ticket.resolved, 'ticket_note_form': ticket_note_form.as_p(), 'ticket_notes': ticket_notes, 'has_ticket_note': has_ticket_note, 'show_ticket_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) link = full_reverse('ticket_info', ticket_id) if 'priority' in post: if post['priority'] != ticket.priority: API.discord_mcm( "[Ticket #**{0}**]({4})'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, link) ) ticket.priority = post['priority'] if 'staff' in post and 'resolved' not in post: if not ticket.staff or request.user.is_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}**]({2}) was claimed by **{1}**".format(ticket.id, request.user.username, link)) else: API.discord_mcm( "[Ticket #**{0}**]({3}) was given to **{1}** by **{2}**".format(ticket.id, staff.username, request.user.username, link)) ticket.staff = staff if 'resolved' in post: API.discord_mcm("[Ticket #**{0}**]({2}) was resolved by **{1}**".format(ticket.id, request.user.username, link)) ticket.resolved = True ticket.save() show_ticket_note = False if 'ticket_note' in post: ticket_note_form = TicketNoteForm(post) if ticket_note_form.is_valid(): n = ticket_note_form.save(commit=False) if post['ticket_note'] == 'create': n.author = request.user n.save() elif post['ticket_note'] == 'edit': db = TicketNoteModel.objects.get(ticket=ticket, author=request.user) db.message = n.message db.last_update = timezone.now() db.save() # Refresh to get the ID for attachments note = TicketNoteModel.objects.get(ticket=ticket, author=request.user) for file in request.FILES.getlist('attachments', []): attachment = AttachmentModel(ref_model=RefModels.TICKET_NOTE[0], ref_id=note.id, file=file) attachment.save() else: show_ticket_note = True else: ticket_note_form = TicketNoteForm(instance=request.user) ticket_notes = TicketNoteModel.objects.filter(ticket=ticket) has_ticket_note = False for ticket_note in ticket_notes: if ticket_note.author == request.user: ticket_note_form = TicketNoteForm(instance=ticket_note) has_ticket_note = True if not has_ticket_note: ticket_note_form.fields['ticket'].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, 'ticket_note_form': ticket_note_form.as_p(), 'ticket_notes': ticket_notes, 'has_ticket_note': has_ticket_note, 'show_ticket_note': show_ticket_note} return render(request, 'minecraft_manager/ticket_info.html', {'current_app': 'ticket', 'ticket': ticket, 'form': form}) class Note(View): def get(self, request): notes = NoteModel.objects.order_by('-id') return render(request, 'minecraft_manager/note.html', {'current_app': 'note', 'notes': notes}) class NoteInfo(View): @method_decorator(csrf_protect) def get(self, request, note_id): note = NoteModel.objects.get(id=note_id) form = {'importance': NoteModel.IMPORTANCE} return render(request, 'minecraft_manager/note_info.html', { 'current_app': 'note', 'form': form, 'note': note }) def post(self, request, note_id): post = request.POST note = NoteModel.objects.get(id=note_id) if 'importance' in post: API.discord_mcm("[Note #**{0}**]({4})'s importance was changed from **{1}** to **{2}** by **{3}**".format( note.id, note.importance_display, NoteModel.importance_code_to_display(post['importance']), request.user.username, full_reverse('note_info', note_id)) ) note.importance = post['importance'] note.save() form = {'importance': NoteModel.IMPORTANCE} return render(request, 'minecraft_manager/note_info.html', context={ 'current_app': 'note', 'form': form, 'note': note }) class NoteAdd(View): @method_decorator(csrf_protect) def get(self, request): get = request.GET form = NoteForm() if 'player' in get: form.initial = {'player': get['player']} return render(request, 'minecraft_manager/note_add.html', context={'current_app': 'note', 'form': form.as_p()}) def post(self, request): post = request.POST form = NoteForm(post) if form.is_valid(): note = form.save() note.staff = request.user note.save() API.discord_mcm(embed=build_note(note, full_reverse('note_info', note.id))) for file in request.FILES.getlist('attachments', []): attachment = AttachmentModel(ref_model=RefModels.NOTE[0], ref_id=note.id, file=file) attachment.save() return redirect("{0}{1}".format(full_reverse('note'), note.id)) else: return render(request, 'minecraft_manager/note_add.html', context={'current_app': 'note', 'form': form}) class Attachment(View): def get(self, request, attachment_id): attachment = AttachmentModel.objects.get(id=attachment_id) resp = HttpResponse(attachment.file) resp['Content-Disposition'] = f"attachment; filename={attachment.file_name}" return resp def delete(self, request, attachment_id): attachment = AttachmentModel.objects.get(id=attachment_id) attachment.delete() return HttpResponse(status=204) class AddAttachment(View): def post(self, request, ref_model, ref_id): for file in request.FILES.getlist('attachments', []): attachment = AttachmentModel(ref_model=ref_model, ref_id=ref_id, file=file) attachment.save() return redirect(request.POST.get('next', reverse('overview'))) class IP(View): def get(self, request, ip_id): ip = IPModel.api.get(id=ip_id) ips = IPModel.api.filter(ip=ip.ip) return render(request, 'minecraft_manager/ip.html', {'ip': ip, 'ips': ips}) def post(self, request, ip_id): pass 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)