Add attachments, clean up UI, etc.
Signed-off-by: Etzelia <etzelia@hotmail.com>pull/2/head
parent
a40966aa0e
commit
cd369ca1a3
|
@ -0,0 +1 @@
|
||||||
|
default_app_config = 'minecraft_manager.apps.MinecraftManagerAppConfig'
|
11
admin.py
11
admin.py
|
@ -4,7 +4,7 @@ from django.contrib import admin
|
||||||
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from minecraft_manager.models import Application, Note, Ticket, TicketNote, Player, IP, UserSettings, Alert
|
from minecraft_manager.models import Application, Note, Ticket, TicketNote, Player, IP, UserSettings, Alert, Attachment
|
||||||
from minecraft_manager.api.admin import register as api_register
|
from minecraft_manager.api.admin import register as api_register
|
||||||
|
|
||||||
|
|
||||||
|
@ -90,6 +90,14 @@ class IPAdmin(admin.ModelAdmin):
|
||||||
search_fields = ["player__username", "ip"]
|
search_fields = ["player__username", "ip"]
|
||||||
|
|
||||||
|
|
||||||
|
class AttachmentAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ["file_name", "model_name"]
|
||||||
|
|
||||||
|
def model_name(self, attachment):
|
||||||
|
return attachment.ref_name
|
||||||
|
model_name.short_description = "Model"
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
admin.site.unregister(User)
|
admin.site.unregister(User)
|
||||||
admin.site.register(User, UserAdmin)
|
admin.site.register(User, UserAdmin)
|
||||||
|
@ -101,6 +109,7 @@ try:
|
||||||
admin.site.register(Player, PlayerAdmin)
|
admin.site.register(Player, PlayerAdmin)
|
||||||
admin.site.register(IP, IPAdmin)
|
admin.site.register(IP, IPAdmin)
|
||||||
admin.site.register(Alert)
|
admin.site.register(Alert)
|
||||||
|
admin.site.register(Attachment, AttachmentAdmin)
|
||||||
api_register()
|
api_register()
|
||||||
except admin.sites.AlreadyRegistered:
|
except admin.sites.AlreadyRegistered:
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
from django.db.models.signals import pre_delete
|
||||||
|
from minecraft_manager.signals.pre_delete import attachment_delete
|
||||||
|
|
||||||
|
|
||||||
|
class MinecraftManagerAppConfig(AppConfig):
|
||||||
|
name = 'minecraft_manager'
|
||||||
|
verbose_name = "Minecraft Manager"
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
pre_delete.connect(attachment_delete)
|
||||||
|
|
|
@ -153,6 +153,29 @@ class Commands(commands.Cog):
|
||||||
if not api.plugin(api.PLUGIN_DENY, application.username):
|
if not api.plugin(api.PLUGIN_DENY, application.username):
|
||||||
await self.bot.discord_message(ctx.message.channel, "Could not deny in-game, is the server running?")
|
await self.bot.discord_message(ctx.message.channel, "Could not deny in-game, is the server running?")
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
async def clear(self, ctx, app_id: int):
|
||||||
|
await self._clear(ctx, app_id)
|
||||||
|
|
||||||
|
@app.command("clear")
|
||||||
|
async def app_clear(self, ctx, app_id: int):
|
||||||
|
await self._clear(ctx, app_id)
|
||||||
|
|
||||||
|
async def _clear(self, ctx, app_id: int):
|
||||||
|
application = get_application(app_id)
|
||||||
|
if not application:
|
||||||
|
await self.bot.discord_message(ctx.message.channel, "An Application with that ID doesn't exist.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not application.accepted:
|
||||||
|
application.accepted = None
|
||||||
|
application.save()
|
||||||
|
if Player.objects.filter(username__iexact=application.username).exists():
|
||||||
|
player = Player.objects.get(username__iexact=application.username)
|
||||||
|
player.application_id = application.id
|
||||||
|
player.save()
|
||||||
|
await self.bot.discord_message(ctx.message.channel, "App ID **{0}** was successfully cleared.".format(app_id))
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def search(self, ctx, search: str):
|
async def search(self, ctx, search: str):
|
||||||
await self._search(ctx, search)
|
await self._search(ctx, search)
|
||||||
|
|
4
forms.py
4
forms.py
|
@ -30,7 +30,7 @@ class TicketForm(ModelForm):
|
||||||
fields = ['player', 'message', 'priority', 'world', 'x', 'y', 'z']
|
fields = ['player', 'message', 'priority', 'world', 'x', 'y', 'z']
|
||||||
widgets = {
|
widgets = {
|
||||||
'player': TextInput,
|
'player': TextInput,
|
||||||
'message': Textarea,
|
'message': Textarea(attrs={'style': 'display: block;'}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ class NoteForm(ModelForm):
|
||||||
model = Note
|
model = Note
|
||||||
fields = ['player', 'message', 'importance']
|
fields = ['player', 'message', 'importance']
|
||||||
widgets = {
|
widgets = {
|
||||||
'message': Textarea
|
'message': Textarea(attrs={'style': 'display: block;'})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Generated by Django 2.2.3 on 2021-02-06 21:40
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import minecraft_manager.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('minecraft_manager', '0014_auto_20190930_1103'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Attachment',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('ref_model', models.CharField(choices=[('T', 'Ticket Note'), ('N', 'Note')], max_length=1)),
|
||||||
|
('ref_id', models.IntegerField()),
|
||||||
|
('file', models.FileField(upload_to=minecraft_manager.models._attachment_upload_to)),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='application',
|
||||||
|
name='reference',
|
||||||
|
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='How did you find out about our server?'),
|
||||||
|
),
|
||||||
|
]
|
49
models.py
49
models.py
|
@ -1,13 +1,13 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
from os.path import basename
|
||||||
import logging, yaml, pytz, json, os
|
import logging, yaml, pytz, json, os
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class MinecraftManagerUser(User):
|
class MinecraftManagerUser(User):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -252,6 +252,10 @@ class Ticket(models.Model):
|
||||||
def date_display(self):
|
def date_display(self):
|
||||||
return str(self.date).split(".")[0]
|
return str(self.date).split(".")[0]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def notes(self):
|
||||||
|
return TicketNote.objects.filter(ticket=self)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{}: {}".format(self.issuer, self.snippet)
|
return "{}: {}".format(self.issuer, self.snippet)
|
||||||
|
|
||||||
|
@ -263,6 +267,10 @@ class TicketNote(models.Model):
|
||||||
last_update = models.DateTimeField(auto_now_add=True, blank=True, null=True)
|
last_update = models.DateTimeField(auto_now_add=True, blank=True, null=True)
|
||||||
date = models.DateTimeField(auto_now_add=True, blank=True, null=True)
|
date = models.DateTimeField(auto_now_add=True, blank=True, null=True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def attachments(self):
|
||||||
|
return Attachment.objects.filter(ref_model=RefModels.TICKET_NOTE[0], ref_id=self.id)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "Ticket Note"
|
verbose_name = "Ticket Note"
|
||||||
verbose_name_plural = "Ticket Notes"
|
verbose_name_plural = "Ticket Notes"
|
||||||
|
@ -324,10 +332,49 @@ class Note(models.Model):
|
||||||
def date_display(self):
|
def date_display(self):
|
||||||
return str(self.date).split(".")[0]
|
return str(self.date).split(".")[0]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def attachments(self):
|
||||||
|
return Attachment.objects.filter(ref_model=RefModels.NOTE[0], ref_id=self.id)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{}: {}".format(self.issuee, self.snippet)
|
return "{}: {}".format(self.issuee, self.snippet)
|
||||||
|
|
||||||
|
|
||||||
|
class RefModels:
|
||||||
|
TICKET_NOTE = 'T', 'Ticket Note'
|
||||||
|
NOTE = 'N', 'Note'
|
||||||
|
choices = TICKET_NOTE, NOTE
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def label(ref: str):
|
||||||
|
for c in RefModels.choices:
|
||||||
|
if c[0] == ref:
|
||||||
|
return c[1]
|
||||||
|
return "None"
|
||||||
|
|
||||||
|
|
||||||
|
def _attachment_upload_to(instance, filename):
|
||||||
|
return f"attachments/{instance.ref_name.lower().replace(' ', '_')}s/{instance.ref_id}/{filename}"
|
||||||
|
|
||||||
|
|
||||||
|
class Attachment(models.Model):
|
||||||
|
ref_model = models.CharField(max_length=1, choices=RefModels.choices)
|
||||||
|
ref_id = models.IntegerField()
|
||||||
|
file = models.FileField(upload_to=_attachment_upload_to)
|
||||||
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ref_name(self):
|
||||||
|
return RefModels.label(self.ref_model)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def file_name(self):
|
||||||
|
return basename(self.file.name)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.file.name
|
||||||
|
|
||||||
|
|
||||||
class IPManager(models.Manager):
|
class IPManager(models.Manager):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
users = User.objects.filter(is_active=True)
|
users = User.objects.filter(is_active=True)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
def attachment_delete(sender, instance, **kwargs):
|
||||||
|
instance.file.delete(False)
|
|
@ -1,4 +1,9 @@
|
||||||
@import url("https://fonts.googleapis.com/css?family=Lato:400,700,400italic");
|
@import url("https://fonts.googleapis.com/css?family=Lato:400,700,400italic");
|
||||||
|
/* Custom CSS */
|
||||||
|
.nav-sidebar {
|
||||||
|
border-right: 1px solid #464545;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* bootswatch v3.3.7
|
* bootswatch v3.3.7
|
||||||
* Homepage: http://bootswatch.com
|
* Homepage: http://bootswatch.com
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
@import url("https://fonts.googleapis.com/css?family=Lato:400,700,400italic");
|
@import url("https://fonts.googleapis.com/css?family=Lato:400,700,400italic");
|
||||||
|
/* Custom CSS */
|
||||||
|
.nav-sidebar {
|
||||||
|
border-right: 1px solid #ecf0f1;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* bootswatch v3.3.7
|
* bootswatch v3.3.7
|
||||||
* Homepage: http://bootswatch.com
|
* Homepage: http://bootswatch.com
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
@import url("https://fonts.googleapis.com/css?family=Roboto:400,700");
|
||||||
|
/* Custom CSS */
|
||||||
|
.nav-sidebar {
|
||||||
|
border-right: 1px solid #1c1e22;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* bootswatch v3.3.7
|
* bootswatch v3.3.7
|
||||||
* Homepage: http://bootswatch.com
|
* Homepage: http://bootswatch.com
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
@import url("https://fonts.googleapis.com/css?family=Roboto:400,700");
|
@import url("https://fonts.googleapis.com/css?family=Roboto:400,700");
|
||||||
|
/* Custom CSS */
|
||||||
|
.nav-sidebar {
|
||||||
|
border-right: 1px solid #282828;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* bootswatch v3.3.7
|
* bootswatch v3.3.7
|
||||||
* Homepage: http://bootswatch.com
|
* Homepage: http://bootswatch.com
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
{% block title %}Alerts{% endblock %}
|
{% block title %}Alerts{% endblock %}
|
||||||
{% block section %}
|
{% block section %}
|
||||||
<div id="content" hidden="hidden">
|
<div id="content" hidden="hidden">
|
||||||
<table id="model-table" class="table table-hover link-table">
|
<table id="model-table" class="table table-striped table-hover link-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Message</th>
|
<th>Message</th>
|
||||||
|
@ -14,9 +14,15 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for alert in alerts %}
|
{% for alert in alerts %}
|
||||||
<tr {% if alert.seen is True %}class="success"{% endif %} data-id="{{ alert.id }}">
|
<tr data-id="{{ alert.id }}">
|
||||||
<td>{{ alert.snippet }}</td>
|
<td>{{ alert.snippet }}</td>
|
||||||
<td>{{ alert.seen }}</td>
|
<td>
|
||||||
|
{% if alert.seen %}
|
||||||
|
<span><i class="glyphicon glyphicon-ok-circle text-success"></i></span>
|
||||||
|
{% else %}
|
||||||
|
<span><i class="glyphicon glyphicon-remove-circle text-danger"></i></span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
<td>{{ alert.date }}</td>
|
<td>{{ alert.date }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
{% block title %}Applications{% endblock %}
|
{% block title %}Applications{% endblock %}
|
||||||
{% block section %}
|
{% block section %}
|
||||||
<div id="content" hidden="hidden">
|
<div id="content" hidden="hidden">
|
||||||
<table id="model-table" class="table table-hover link-table">
|
<table id="model-table" class="table table-striped table-hover link-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>App ID</th>
|
<th>App ID</th>
|
||||||
|
@ -16,12 +16,19 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for app in applications %}
|
{% 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 }}">
|
<tr data-id="{{ app.id }}">
|
||||||
<td>{{ app.id }}</td>
|
<td>{{ app.id }}</td>
|
||||||
<td>{{ app.username }}</td>
|
<td>{{ app.username }}</td>
|
||||||
<td>{{ app.age }}</td>
|
<td>{{ app.age }}</td>
|
||||||
<td>{{ app.ever_banned }}</td>
|
<td>{{ app.ever_banned }}</td>
|
||||||
<td>{{ app.status }}</td>
|
<td>
|
||||||
|
{{ app.status }}
|
||||||
|
{% if app.accepted is True %}
|
||||||
|
<span><i class="glyphicon glyphicon-ok-circle text-success"></i></span>
|
||||||
|
{% elif app.accepted is False %}
|
||||||
|
<span><i class="glyphicon glyphicon-remove-circle text-danger"></i></span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
<td>{{ app.date }}</td>
|
<td>{{ app.date }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
{% block title %}Bans{% endblock %}
|
{% block title %}Bans{% endblock %}
|
||||||
{% block section %}
|
{% block section %}
|
||||||
<div id="content" hidden="hidden">
|
<div id="content" hidden="hidden">
|
||||||
<table id="model-table" class="table table-hover link-table">
|
<table id="model-table" class="table table-striped table-hover link-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Player</th>
|
<th>Player</th>
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
{% extends "minecraft_manager/dashboard.html" %}
|
|
||||||
{% load csrf_html %}
|
|
||||||
{% block title %}Bots{% endblock %}
|
|
||||||
{% block section %}
|
|
||||||
<div id="content">
|
|
||||||
{% for bot in bots %}
|
|
||||||
<p>{{ bot.name }}: <span class="label {% if bot.status is True %}label-success{% else %}label-danger{% endif %}">{{ bot.display }}</span></p>
|
|
||||||
<form action="" method="post">{% autoescape off %}{% get_csrf_html request %}{% endautoescape %}
|
|
||||||
<button class="btn btn-primary{% if bot.status is True %} disabled{% endif %}" type="submit" name="{{ bot.name }}" value="start" {% if bot.status is True %} disabled="disabled"{% endif %}>Start</button>
|
|
||||||
<button class="btn btn-primary{% if bot.status is False %} disabled{% endif %}" type="submit" name="{{ bot.name }}" value="stop" {% if bot.status is False %}disabled="disabled"{% endif %}>Stop</button>
|
|
||||||
<button class="btn btn-primary{% if bot.status is False %} disabled{% endif %}" type="submit" name="{{ bot.name }}" value="restart" {% if bot.status is False %}disabled="disabled"{% endif %}>Restart</button>
|
|
||||||
</form>
|
|
||||||
<br/>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endblock section %}
|
|
|
@ -28,6 +28,9 @@
|
||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
<a class="dropdown-toggle " href="#" role="button" data-toggle="dropdown" id="accountDropdown">{{ user.username }}</a>
|
<a class="dropdown-toggle " href="#" role="button" data-toggle="dropdown" id="accountDropdown">{{ user.username }}</a>
|
||||||
<ul class="dropdown-menu" role="menu" aria-labelledby="accountDropdown">
|
<ul class="dropdown-menu" role="menu" aria-labelledby="accountDropdown">
|
||||||
|
{% if user.is_staff %}
|
||||||
|
<li><a target="_blank" href="{% url "admin:index" %}">Admin Site</a></li>
|
||||||
|
{% endif %}
|
||||||
<li><a style="cursor:pointer;" data-toggle="modal" data-target="#settingsModal">Settings</a></li>
|
<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 style="cursor:pointer;" data-toggle="modal" data-target="#passwordModal">Change Password</a></li>
|
||||||
<li><a href="{% url "logout" %}">Logout</a></li>
|
<li><a href="{% url "logout" %}">Logout</a></li>
|
||||||
|
@ -69,9 +72,6 @@
|
||||||
<form id="settingsForm" method="POST" action="{% url 'api-web' keyword='settings' %}">{% autoescape off %}{% get_csrf_html request %}{% endautoescape %}
|
<form id="settingsForm" method="POST" action="{% url 'api-web' keyword='settings' %}">{% autoescape off %}{% get_csrf_html request %}{% endautoescape %}
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
{% get_form 'usersettings' request %}
|
{% get_form 'usersettings' request %}
|
||||||
{% if user.is_staff %}
|
|
||||||
<br/><a target="_blank" href="{% url "admin:index" %}">Admin Site</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
<div id="addAttachmentModal" 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">Add Attachment(s)</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form method="post" id="add-attachment-form" enctype="multipart/form-data">{% csrf_token %}
|
||||||
|
<input type="file" multiple name="attachments"/>
|
||||||
|
<input type="hidden" name="next" value="{% url 'note_info' note.id %}"/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="add-attachment-save">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$(document).ready(() => {
|
||||||
|
const $modal = $('#addAttachmentModal');
|
||||||
|
const addAttachmentSave = document.querySelector('#add-attachment-save');
|
||||||
|
const addAttachmentForm = document.querySelector('#add-attachment-form');
|
||||||
|
document.querySelectorAll('.add-attachment[data-model][data-ref]').forEach((elem) => {
|
||||||
|
elem.addEventListener('click', () => {
|
||||||
|
addAttachmentModal(elem.dataset.model, elem.dataset.ref);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$modal.on('hidden.bs.modal', () => {
|
||||||
|
addAttachmentForm.reset();
|
||||||
|
});
|
||||||
|
addAttachmentSave.addEventListener('click', () => {
|
||||||
|
addAttachmentForm.submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
function addAttachmentModal(model, ref) {
|
||||||
|
addAttachmentForm.action = '{% url 'attachment_add' 'X' 0 %}'
|
||||||
|
.replace('X', model)
|
||||||
|
.replace('0', ref);
|
||||||
|
$modal.modal('show');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,48 @@
|
||||||
|
<div id="deleteAttachmentModal" 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">Delete Attachment</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>Are you sure you want to delete <code id="delete-attachment-name"></code>?</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="delete-attachment-confirm">Confirm</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$(document).ready(() => {
|
||||||
|
const $modal = $('#deleteAttachmentModal');
|
||||||
|
const deleteAttachmentName = document.querySelector('#delete-attachment-name');
|
||||||
|
const deleteAttachmentConfirm = document.querySelector('#delete-attachment-confirm');
|
||||||
|
document.querySelectorAll('.delete-attachment[data-name][data-id]').forEach((elem) => {
|
||||||
|
elem.addEventListener('click', () => {
|
||||||
|
deleteAttachmentModal(elem.dataset.name, elem.dataset.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
deleteAttachmentConfirm.addEventListener('click', () => {
|
||||||
|
const attachmentURL = '{% url 'attachment' 0 %}'.replace('0', deleteAttachmentConfirm.dataset.id);
|
||||||
|
fetch(attachmentURL, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': '{{ csrf_token }}'
|
||||||
|
}
|
||||||
|
}).then(() => {
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function deleteAttachmentModal(name, id) {
|
||||||
|
deleteAttachmentName.innerHTML = name;
|
||||||
|
deleteAttachmentConfirm.dataset.id = id;
|
||||||
|
$modal.modal('show');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -2,24 +2,34 @@
|
||||||
{% block title %}Notes{% endblock %}
|
{% block title %}Notes{% endblock %}
|
||||||
{% block section %}
|
{% block section %}
|
||||||
<div id="content" hidden="hidden">
|
<div id="content" hidden="hidden">
|
||||||
<table id="model-table" class="table table-hover link-table">
|
<table id="model-table" class="table table-striped table-hover link-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Player</th>
|
<th>Player</th>
|
||||||
<th>Message</th>
|
<th>Message</th>
|
||||||
<th>Importance</th>
|
<th>Importance</th>
|
||||||
<td>Issued By</td>
|
<td>Issued By</td>
|
||||||
|
<td>Attachments</td>
|
||||||
<th>Date</th>
|
<th>Date</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for note in notes %}
|
{% for note in notes %}
|
||||||
<tr {% if note.importance == 'L' %}class="info"{% endif %}{% if note.importance == 'M' %}class="warning"{% endif %}{% if note.importance == 'H' %}class="danger"{% endif %} data-id="{{ note.id }}">
|
<tr data-id="{{ note.id }}">
|
||||||
<!-- <td>{{ note.id }}</td> -->
|
<!-- <td>{{ note.id }}</td> -->
|
||||||
<td>{{ note.issuee }}</td>
|
<td>{{ note.issuee }}</td>
|
||||||
<td>{{ note.snippet }}</td>
|
<td>{{ note.snippet }}</td>
|
||||||
<td>{{ note.importance_display }}</td>
|
<td>
|
||||||
|
<span class="label label-{% if note.importance == 'L' %}info{% elif note.importance == 'M' %}warning{% elif note.importance == 'H' %}danger{% endif %}">
|
||||||
|
{{ note.importance_display }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
<td>{{ note.issuer }}</td>
|
<td>{{ note.issuer }}</td>
|
||||||
|
<td>
|
||||||
|
{% for attachment in note.attachments %}
|
||||||
|
<a href="{% url 'attachment' attachment.id %}">{{ attachment.file_name }}</a><br/>
|
||||||
|
{% endfor %}
|
||||||
|
</td>
|
||||||
<td>{{ note.date }}</td>
|
<td>{{ note.date }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -6,21 +6,17 @@
|
||||||
<h2 class="sub-header">New Note</h2>
|
<h2 class="sub-header">New Note</h2>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-18 col-md-12">
|
<div class="col-xs-18 col-md-12">
|
||||||
<form action="" method="post">{% autoescape off %}{% get_csrf_html request %}{% endautoescape %}
|
<form action="" method="post" enctype="multipart/form-data">{% autoescape off %}{% get_csrf_html request %}{% endautoescape %}
|
||||||
<p><label for="">Player Filter:</label> <input id="id_filter" type="text"/></p>
|
<p><label for="id_filter">Player Filter:</label> <input id="id_filter" type="text"/></p>
|
||||||
{{ form }}
|
{{ form }}
|
||||||
|
<p>
|
||||||
|
<label for="attachments">Attachments:</label>
|
||||||
|
<input id="attachments" name="attachments" type="file" multiple/>
|
||||||
|
</p>
|
||||||
<button id="saveButton" class="btn btn-primary" type="submit">Save</button>
|
<button id="saveButton" class="btn btn-primary" type="submit">Save</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<div class="col-xs-12 col-md-8">
|
<div class="col-xs-12 col-md-8">
|
||||||
<form action="" method="post">{% autoescape off %}{% get_csrf_html request %}{% endautoescape %}
|
<form action="" method="post">{% autoescape off %}{% get_csrf_html request %}{% endautoescape %}
|
||||||
<p>Issuer: {{ note.staff.username }} </p>
|
<p>Issuer: {{ note.staff.username }} </p>
|
||||||
<p>Importance:
|
<p><label for="noteImportance">Importance:</label>
|
||||||
{% if user.is_staff or user == note.staff %}
|
{% if user.is_staff or user == note.staff %}
|
||||||
<select id="noteImportance" name="importance">
|
<select id="noteImportance" name="importance">
|
||||||
{% for p in form.importance %}
|
{% for p in form.importance %}
|
||||||
|
@ -32,23 +32,42 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% if note.attachments.all|length > 0 %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-6 col-md-4">
|
<table>
|
||||||
|
<thead>
|
||||||
</div>
|
<tr><th>Attachments</th></tr>
|
||||||
<div class="col-xs-12 col-md-8">
|
</thead>
|
||||||
|
<tbody>
|
||||||
</div>
|
{% for attachment in note.attachments %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="{% url 'attachment' attachment.id %}">{{ attachment.file_name }}</a>
|
||||||
|
<span class="delete-attachment" data-name="{{ attachment.file_name }}" data-id="{{ attachment.id }}">
|
||||||
|
<i class="glyphicon glyphicon-remove-circle text-danger"></i>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<br/>
|
||||||
|
<button type="button" class="btn btn-sm btn-primary add-attachment" data-model="N" data-ref="{{ note.id }}">Add Attachment(s)</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
$("#saveButton").hide();
|
$("#saveButton").hide();
|
||||||
$("#noteImportance").change(function() {
|
$("#noteImportance").change(function() {
|
||||||
if (("{{ user.username }}" == "{{ note.staff.username }}" || "{{ user.is_staff }}" == "True") && $(this).val() != "{{ note.importance }}") {
|
if (("{{ user.username }}" === "{{ note.staff.username }}" || "{{ user.is_staff }}" === "True") && $(this).val() !== "{{ note.importance }}") {
|
||||||
$("#saveButton").show();
|
$("#saveButton").show();
|
||||||
} else {
|
} else {
|
||||||
$("#saveButton").hide();
|
$("#saveButton").hide();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{% include 'minecraft_manager/modal/add_attachment.html' %}
|
||||||
|
{% include 'minecraft_manager/modal/delete_attachment.html' %}
|
||||||
{% endblock section %}
|
{% endblock section %}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
{% block title %}Players{% endblock %}
|
{% block title %}Players{% endblock %}
|
||||||
{% block section %}
|
{% block section %}
|
||||||
<div id="content" hidden="hidden">
|
<div id="content" hidden="hidden">
|
||||||
<table id="model-table" class="table table-hover link-table">
|
<table id="model-table" class="table table-striped table-hover link-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Username</th>
|
<th>Username</th>
|
||||||
|
@ -13,10 +13,18 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for player in players %}
|
{% 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 }}">
|
<tr 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.username }}{% if user.usersettings.search_player_ip is True %}<span hidden="hidden">{{ player.ips }}</span>{% endif %}</td>
|
||||||
<td>{{ player.uuid }}</td>
|
<td>{{ player.uuid }}</td>
|
||||||
<td>{% if player.uuid in bans %}True {% else %}False{% endif %}</td>
|
<td>
|
||||||
|
{% if player.uuid in bans %}
|
||||||
|
Yes
|
||||||
|
<span><i class="glyphicon glyphicon-remove-circle text-danger"></i></span>
|
||||||
|
{% else %}
|
||||||
|
No
|
||||||
|
<span><i class="glyphicon glyphicon-ok-circle text-success"></i></span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
<td>{{ player.last_seen }}</td>
|
<td>{{ player.last_seen }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -6,12 +6,12 @@
|
||||||
<h2 class="sub-header">Player Info ({{ player.username }})</h2>
|
<h2 class="sub-header">Player Info ({{ player.username }})</h2>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-9 col-md-6">
|
<div class="col-xs-9 col-md-6">
|
||||||
<p>UUID: {{ player.uuid }}</p>
|
<p>UUID: <code>{{ player.uuid }}</code> <button data-copy="{{ player.uuid }}" class="btn btn-xs btn-primary"><i class="glyphicon glyphicon-copy"></i></button></p>
|
||||||
{% if player.auth_user %}
|
{% if player.auth_user %}
|
||||||
<p>Connected User: {{ player.auth_user.username }}</p>
|
<p>Connected User: {{ player.auth_user.username }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if player.application %}
|
{% if player.application %}
|
||||||
<p>Application: <a href="{% url "application" %}{{ player.application.id }}">Here</a></p>
|
<p>Application: <a href="{% url "application" %}{{ player.application.id }}">{{ player.application.username }}</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<p>Donor Status: {{ player.donor_status }}</p>
|
<p>Donor Status: {{ player.donor_status }}</p>
|
||||||
<p>First Seen: {{ player.first_seen }}</p>
|
<p>First Seen: {{ player.first_seen }}</p>
|
||||||
|
@ -20,14 +20,35 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-9 col-md-6">
|
<div class="col-xs-9 col-md-6">
|
||||||
<h4>Tickets</h4>
|
<h4>Tickets</h4>
|
||||||
<table class="table table-hover link-table">
|
<table class="table table-striped table-hover link-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>ID</td>
|
||||||
|
<td>Message</td>
|
||||||
|
<td>Resolved</td>
|
||||||
|
<td>Attachments</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% if form.tickets %}
|
{% if form.tickets %}
|
||||||
{% for ticket in 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" %}">
|
<tr data-id="{{ ticket.id }}" data-url="{% url "ticket" %}">
|
||||||
<td>{{ ticket.id }}</td>
|
<td>{{ ticket.id }}</td>
|
||||||
<td>{{ ticket.snippet }}</td>
|
<td>{{ ticket.snippet }}</td>
|
||||||
<td>{{ ticket.resolved }}</td>
|
<td>
|
||||||
|
{% if ticket.resolved is True %}
|
||||||
|
<span><i class="glyphicon glyphicon-ok-circle text-success"></i></span>
|
||||||
|
{% else %}
|
||||||
|
<span><i class="glyphicon glyphicon-remove-circle text-danger"></i></span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% for note in ticket.notes %}
|
||||||
|
{% for attachment in note.attachments %}
|
||||||
|
<a href="{% url 'attachment' attachment.id %}">{{ attachment.file_name }}</a> ({{ note.author.username }})<br/>
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -36,15 +57,33 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<br/>
|
<br/>
|
||||||
<table class="table table-hover link-table">
|
<h4>Notes</h4>
|
||||||
<h4>Notes</h4>
|
<table class="table table-striped table-hover link-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>Message</td>
|
||||||
|
<td>Importance</td>
|
||||||
|
<td>Attachments</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% if form.notes %}
|
{% if form.notes %}
|
||||||
{% for note in form.notes %}
|
{% for note in form.notes %}
|
||||||
<tr {% if note.importance == 'L' %}class="info"{% endif %}{% if note.importance == 'M' %}class="warning"{% endif %}{% if note.importance == 'H' %}class="danger"{% endif %} data-id="{{ note.id }}" data-url="{% url "note" %}">
|
<tr data-id="{{ note.id }}" data-url="{% url "note" %}">
|
||||||
<!-- {{ note.id }} -->
|
<!-- {{ note.id }} -->
|
||||||
<td>{{ note.snippet }}</td>
|
<td>{{ note.snippet }}</td>
|
||||||
<td>{{ note.importance_display }}</td>
|
<td>
|
||||||
|
<span class="label label-{% if note.importance == 'L' %}info{% elif note.importance == 'M' %}warning{% elif note.importance == 'H' %}danger{% endif %}">
|
||||||
|
{{ note.importance_display }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% for attachment in note.attachments %}
|
||||||
|
<a href="{% url 'attachment' attachment.id %}">{{ attachment.file_name }}</a>
|
||||||
|
{% if note.staff %}({{ note.staff.username }}){% endif %}
|
||||||
|
<br/>
|
||||||
|
{% endfor %}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -54,14 +93,22 @@
|
||||||
</table>
|
</table>
|
||||||
<a class="btn btn-primary" href="{% url 'note_add' %}?player={{ player.id }}">Add Note</a>
|
<a class="btn btn-primary" href="{% url 'note_add' %}?player={{ player.id }}">Add Note</a>
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
|
<h4>IPs</h4>
|
||||||
<table class="table table-striped table-hover link-table">
|
<table class="table table-striped table-hover link-table">
|
||||||
<h4>IPs</h4>
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>IP</th>
|
||||||
|
<th>Last Used</th>
|
||||||
|
<th>Associated Users</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% if form.ips %}
|
{% if form.ips %}
|
||||||
{% for ip in form.ips %}
|
{% for ip in form.ips %}
|
||||||
<tr class="default" data-id="" data-url="{% url 'ip' ip.id %}">
|
<tr class="default" data-id="" data-url="{% url 'ip' ip.id %}">
|
||||||
<!-- {{ ip.id }} -->
|
<!-- {{ ip.id }} -->
|
||||||
<td>{{ ip.ip }} ({{ ip.last_used_formatted }})</td>
|
<td>{{ ip.ip }}</td>
|
||||||
|
<td>{{ ip.last_used_formatted }}</td>
|
||||||
{% if ip.associated %}
|
{% if ip.associated %}
|
||||||
<td>
|
<td>
|
||||||
{% for assoc in ip.associated %}
|
{% for assoc in ip.associated %}
|
||||||
|
@ -81,4 +128,21 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.querySelectorAll('[data-copy]').forEach((elem) => {
|
||||||
|
elem.addEventListener('click', () => {
|
||||||
|
const text = document.createElement('textarea');
|
||||||
|
text.value = elem.dataset.copy;
|
||||||
|
text.style.top = "0";
|
||||||
|
text.style.left = "0";
|
||||||
|
text.style.position = "fixed";
|
||||||
|
document.body.appendChild(text);
|
||||||
|
text.focus();
|
||||||
|
text.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
text.remove();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
{% endblock section %}
|
{% endblock section %}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
{% block title %}Tickets{% endblock %}
|
{% block title %}Tickets{% endblock %}
|
||||||
{% block section %}
|
{% block section %}
|
||||||
<div id="content" hidden="hidden">
|
<div id="content" hidden="hidden">
|
||||||
<table id="model-table" class="table table-hover link-table">
|
<table id="model-table" class="table table-striped table-hover link-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
|
@ -16,13 +16,23 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for ticket in tickets %}
|
{% 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 }}">
|
<tr data-id="{{ ticket.id }}">
|
||||||
<td>{{ ticket.id }}</td>
|
<td>{{ ticket.id }}</td>
|
||||||
<td>{{ ticket.issuer }}</td>
|
<td>{{ ticket.issuer }}</td>
|
||||||
<td>{{ ticket.snippet }}</td>
|
<td>{{ ticket.snippet }}</td>
|
||||||
<td>{{ ticket.priority_display }}</td>
|
<td>
|
||||||
|
<span class="label label-{% if ticket.priority == 'L' %}info{% elif ticket.priority == 'M' %}warning{% elif ticket.priority == 'H' %}danger{% endif %}">
|
||||||
|
{{ ticket.priority_display }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
<td>{{ ticket.claimed_by }}</td>
|
<td>{{ ticket.claimed_by }}</td>
|
||||||
<td>{{ ticket.resolved }}</td>
|
<td>
|
||||||
|
{% if ticket.resolved %}
|
||||||
|
<span><i class="glyphicon glyphicon-ok-circle text-success"></i></span>
|
||||||
|
{% else %}
|
||||||
|
<span><i class="glyphicon glyphicon-remove-circle text-danger"></i></span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
<td>{{ ticket.date }}</td>
|
<td>{{ ticket.date }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -74,20 +74,42 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<p>Created: {{ ticket_note.date }}</p>
|
<p>Created: {{ ticket_note.date }}</p>
|
||||||
<p>Last Update: {{ ticket_note.last_update }}</p>
|
<p>Last Update: {{ ticket_note.last_update }}</p>
|
||||||
|
{% if ticket_note.attachments|length > 0 %}
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr><th>Attachments</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for attachment in ticket_note.attachments %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="{% url 'attachment' attachment.id %}">{{ attachment.file_name }}</a>
|
||||||
|
<span class="delete-attachment" data-name="{{ attachment.file_name }}" data-id="{{ attachment.id }}">
|
||||||
|
<i class="glyphicon glyphicon-remove-circle text-danger"></i>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% if not form.has_ticket_note and not form.show_ticket_note %}
|
{% if not form.has_ticket_note and not form.show_ticket_note %}
|
||||||
<div id="createDiv" class="row">
|
<div id="createDiv" class="row">
|
||||||
<button class="btn btn-primary" onClick="showNote()">Create Note</button>
|
<button class="btn btn-primary" onClick="showNote();">Create Note</button>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div id="ticket_noteDiv" class="row" {% if not form.show_ticket_note %}style="display: none;"{% endif %}>
|
<div id="ticket_noteDiv" class="row" {% if not form.show_ticket_note %}style="display: none;"{% endif %}>
|
||||||
<br/>
|
<br/>
|
||||||
<h3>Note</h3>
|
<h3>Note</h3>
|
||||||
<form action="" method="POST">{% autoescape off %}{% get_csrf_html request %}{% endautoescape %}
|
<form action="" method="POST" enctype="multipart/form-data">{% autoescape off %}{% get_csrf_html request %}{% endautoescape %}
|
||||||
{{ form.ticket_note_form }}
|
{{ form.ticket_note_form }}
|
||||||
<br/>
|
<p>
|
||||||
|
<label for="attachments">Attachments:</label>
|
||||||
|
<input id="attachments" name="attachments" type="file" multiple/>
|
||||||
|
</p>
|
||||||
<button type="submit" class="btn btn-primary" name="ticket_note" value="{% if form.has_ticket_note %}edit{% else %}create{% endif %}">Save</button>
|
<button type="submit" class="btn btn-primary" name="ticket_note" value="{% if form.has_ticket_note %}edit{% else %}create{% endif %}">Save</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -97,7 +119,7 @@
|
||||||
$("#saveButton").hide();
|
$("#saveButton").hide();
|
||||||
$("#ticketStaff").change(function() {
|
$("#ticketStaff").change(function() {
|
||||||
var $priority = $("#ticketPriority");
|
var $priority = $("#ticketPriority");
|
||||||
if ($(this).val() != "{{ ticket.staff.id }}" || $priority.val() != "{{ ticket.priority }}") {
|
if ($(this).val() !== "{{ ticket.staff.id }}" || $priority.val() !== "{{ ticket.priority }}") {
|
||||||
toggleSave(true);
|
toggleSave(true);
|
||||||
} else {
|
} else {
|
||||||
toggleSave(false);
|
toggleSave(false);
|
||||||
|
@ -105,7 +127,7 @@
|
||||||
});
|
});
|
||||||
$("#ticketPriority").change(function() {
|
$("#ticketPriority").change(function() {
|
||||||
var $staff = $("#ticketStaff");
|
var $staff = $("#ticketStaff");
|
||||||
if ($(this).val() != "{{ ticket.priority }}" || $staff.val() != "{{ ticket.staff.id }}") {
|
if ($(this).val() !== "{{ ticket.priority }}" || $staff.val() !== "{{ ticket.staff.id }}") {
|
||||||
toggleSave(true);
|
toggleSave(true);
|
||||||
} else {
|
} else {
|
||||||
toggleSave(false);
|
toggleSave(false);
|
||||||
|
@ -113,10 +135,10 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
function toggleSave(toggle) {
|
function toggleSave(toggle) {
|
||||||
if (toggle == true) {
|
if (toggle === true) {
|
||||||
$("#resolveButton").hide();
|
$("#resolveButton").hide();
|
||||||
$("#saveButton").show();
|
$("#saveButton").show();
|
||||||
} else if (toggle == false) {
|
} else if (toggle === false) {
|
||||||
$("#resolveButton").show();
|
$("#resolveButton").show();
|
||||||
$("#saveButton").hide();
|
$("#saveButton").hide();
|
||||||
}
|
}
|
||||||
|
@ -128,4 +150,5 @@
|
||||||
$("#editBtn").hide();
|
$("#editBtn").hide();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
{% include 'minecraft_manager/modal/delete_attachment.html' %}
|
||||||
{% endblock section %}
|
{% endblock section %}
|
||||||
|
|
38
urls.py
38
urls.py
|
@ -7,37 +7,41 @@ urlpatterns = [
|
||||||
url(r'^$', RedirectView.as_view(pattern_name='overview')),
|
url(r'^$', RedirectView.as_view(pattern_name='overview')),
|
||||||
|
|
||||||
# Dashboard
|
# Dashboard
|
||||||
url(r'^dashboard/overview/$', login_required(mcm.Overview.as_view()), name="overview"),
|
url(r'^overview/$', login_required(mcm.Overview.as_view()), name="overview"),
|
||||||
url(r'^dashboard/ban/$', login_required(mcm.Ban.as_view()), name="ban"),
|
url(r'^ban/$', login_required(mcm.Ban.as_view()), name="ban"),
|
||||||
|
|
||||||
# Alerts
|
# Alerts
|
||||||
url(r'^dashboard/alert/$', login_required(mcm.Alert.as_view()), name="alert"),
|
url(r'^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())),
|
url(r'^alert/(?P<alert_id>[0-9]{1,5})/$', login_required(mcm.AlertInfo.as_view())),
|
||||||
|
|
||||||
# Applications
|
# Applications
|
||||||
url(r'^dashboard/application/$', login_required(mcm.Application.as_view()), name="application"),
|
url(r'^application/$', login_required(mcm.Application.as_view()), name="application"),
|
||||||
url(r'^dashboard/reference/$', login_required(mcm.Reference.as_view()), name="reference"),
|
url(r'^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())),
|
url(r'^application/(?P<application_id>[0-9]{1,5})/$', login_required(mcm.ApplicationInfo.as_view())),
|
||||||
|
|
||||||
# Players
|
# Players
|
||||||
url(r'^dashboard/player/$', login_required(mcm.Player.as_view()), name="player"),
|
url(r'^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())),
|
url(r'^player/(?P<player_id>[0-9]{1,5})/$', login_required(mcm.PlayerInfo.as_view())),
|
||||||
|
|
||||||
# Tickets
|
# Tickets
|
||||||
url(r'^dashboard/ticket/$', login_required(mcm.Ticket.as_view()), name="ticket"),
|
url(r'^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())),
|
url(r'^ticket/(?P<ticket_id>[0-9]{1,5})/$', login_required(mcm.TicketInfo.as_view())),
|
||||||
|
|
||||||
# Warnings
|
# Notes
|
||||||
url(r'^dashboard/note/$', login_required(mcm.Note.as_view()), name="note"),
|
url(r'^note/$', login_required(mcm.Note.as_view()), name="note"),
|
||||||
url(r'^dashboard/note/(?P<note_id>[0-9]{1,5})/$', login_required(mcm.NoteInfo.as_view())),
|
url(r'^note/(?P<note_id>[0-9]{1,5})/$', login_required(mcm.NoteInfo.as_view()), name='note_info'),
|
||||||
url(r'^dashboard/note/add$', login_required(mcm.NoteAdd.as_view()), name="note_add"),
|
url(r'^note/add$', login_required(mcm.NoteAdd.as_view()), name="note_add"),
|
||||||
|
|
||||||
|
# Attachments
|
||||||
|
url(r'^attachment/(?P<attachment_id>[0-9]{1,5})/$', login_required(mcm.Attachment.as_view()), name="attachment"),
|
||||||
|
url(r'attachment/(?P<ref_model>[A-Za-z])/(?P<ref_id>[0-9]{1,5})/$', login_required(mcm.AddAttachment.as_view()), name='attachment_add'),
|
||||||
|
|
||||||
# IP
|
# IP
|
||||||
url(r'^dashboard/ip/(?P<ip_id>[0-9]{1,5})/$', login_required(mcm.IP.as_view()), name="ip"),
|
url(r'^ip/(?P<ip_id>[0-9]{1,5})/$', login_required(mcm.IP.as_view()), name="ip"),
|
||||||
|
|
||||||
# Report
|
# Report
|
||||||
url(r'^report/$', login_required(mcm.Report.as_view()), name="report"),
|
url(r'^report/$', login_required(mcm.Report.as_view()), name="report"),
|
||||||
|
|
||||||
# Chat
|
# Chat
|
||||||
url(r'^dashboard/chat/$', permission_required('minecraft_manager.chat')(mcm.Chat.as_view()), name="chat"),
|
url(r'^chat/$', permission_required('minecraft_manager.chat')(mcm.Chat.as_view()), name="chat"),
|
||||||
]
|
]
|
||||||
|
|
1
utils.py
1
utils.py
|
@ -1,5 +1,6 @@
|
||||||
import discord, requests
|
import discord, requests
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core.files.storage import FileSystemStorage
|
||||||
from minecraft_manager.models import Player, Application
|
from minecraft_manager.models import Player, Application
|
||||||
|
|
||||||
|
|
||||||
|
|
81
views.py
81
views.py
|
@ -1,18 +1,16 @@
|
||||||
#create your views here.
|
|
||||||
# https://api.mojang.com/users/profiles/minecraft/<username>
|
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import json, datetime, pytz, os, sys
|
import json, datetime, pytz, os
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse, HttpResponse
|
||||||
from django.shortcuts import render, reverse, redirect
|
from django.shortcuts import render, reverse, redirect
|
||||||
from django.views.decorators.csrf import csrf_protect
|
from django.views.decorators.csrf import csrf_protect
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
from django.contrib.auth.models import User
|
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
|
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.forms import TicketNoteForm, NoteForm
|
||||||
from minecraft_manager.overview import overview_data
|
from minecraft_manager.overview import overview_data
|
||||||
from minecraft_manager.utils import resolve_player
|
from minecraft_manager.utils import resolve_player
|
||||||
|
@ -280,6 +278,11 @@ class TicketInfo(View):
|
||||||
db.message = n.message
|
db.message = n.message
|
||||||
db.last_update = timezone.now()
|
db.last_update = timezone.now()
|
||||||
db.save()
|
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:
|
else:
|
||||||
show_ticket_note = True
|
show_ticket_note = True
|
||||||
else:
|
else:
|
||||||
|
@ -314,23 +317,30 @@ class NoteInfo(View):
|
||||||
def get(self, request, note_id):
|
def get(self, request, note_id):
|
||||||
note = NoteModel.objects.get(id=note_id)
|
note = NoteModel.objects.get(id=note_id)
|
||||||
form = {'importance': NoteModel.IMPORTANCE}
|
form = {'importance': NoteModel.IMPORTANCE}
|
||||||
return render(request, 'minecraft_manager/note_info.html', {'current_app': 'note', 'form': form, 'note': note})
|
return render(request, 'minecraft_manager/note_info.html', {
|
||||||
|
'current_app': 'note',
|
||||||
|
'form': form,
|
||||||
|
'note': note
|
||||||
|
})
|
||||||
|
|
||||||
def post(self, request, note_id):
|
def post(self, request, note_id):
|
||||||
post = request.POST
|
post = request.POST
|
||||||
note = NoteModel.objects.get(id=note_id)
|
note = NoteModel.objects.get(id=note_id)
|
||||||
if 'importance' in post:
|
if 'importance' in post:
|
||||||
API.discord_mcm("Note #**{0}**'s importance was changed from {1} to {2} by {3}".format(note.id,
|
API.discord_mcm("Note #**{0}**'s importance was changed from {1} to {2} by {3}".format(
|
||||||
note.importance_display,
|
note.id,
|
||||||
NoteModel.importance_code_to_display(
|
note.importance_display,
|
||||||
post[
|
NoteModel.importance_code_to_display(post['importance']),
|
||||||
'importance']),
|
request.user.player.username)
|
||||||
request.user.player.username))
|
)
|
||||||
note.importance = post['importance']
|
note.importance = post['importance']
|
||||||
note.save()
|
note.save()
|
||||||
form = {'importance': NoteModel.IMPORTANCE}
|
form = {'importance': NoteModel.IMPORTANCE}
|
||||||
return render(request, 'minecraft_manager/note_info.html',
|
return render(request, 'minecraft_manager/note_info.html', context={
|
||||||
{'current_app': 'note', 'form': form, 'note': note})
|
'current_app': 'note',
|
||||||
|
'form': form,
|
||||||
|
'note': note
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class NoteAdd(View):
|
class NoteAdd(View):
|
||||||
|
@ -341,8 +351,7 @@ class NoteAdd(View):
|
||||||
form = NoteForm()
|
form = NoteForm()
|
||||||
if 'player' in get:
|
if 'player' in get:
|
||||||
form.initial = {'player': get['player']}
|
form.initial = {'player': get['player']}
|
||||||
return render(request, 'minecraft_manager/note_add.html',
|
return render(request, 'minecraft_manager/note_add.html', context={'current_app': 'note', 'form': form.as_p()})
|
||||||
{'current_app': 'note', 'form': form.as_p()})
|
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
post = request.POST
|
post = request.POST
|
||||||
|
@ -352,14 +361,41 @@ class NoteAdd(View):
|
||||||
note.staff = request.user
|
note.staff = request.user
|
||||||
note.save()
|
note.save()
|
||||||
API.discord_mcm(
|
API.discord_mcm(
|
||||||
"**{0}** made a **{1}** importance note for **{2}**\nPreview: {3}".format(note.staff.player.username,
|
"**{0}** made a **{1}** importance note for **{2}**\nPreview: {3}".format(
|
||||||
note.importance_display,
|
note.staff.player.username,
|
||||||
note.player.username,
|
note.importance_display,
|
||||||
note.snippet))
|
note.player.username,
|
||||||
|
note.snippet)
|
||||||
|
)
|
||||||
|
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(reverse('note'), note.id))
|
return redirect("{0}{1}".format(reverse('note'), note.id))
|
||||||
else:
|
else:
|
||||||
return render(request, 'minecraft_manager/note_add.html',
|
return render(request, 'minecraft_manager/note_add.html', context={'current_app': 'note', 'form': form})
|
||||||
{'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):
|
class IP(View):
|
||||||
|
@ -427,7 +463,6 @@ class Report(View):
|
||||||
|
|
||||||
class Chat(View):
|
class Chat(View):
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def replace_ascii(message):
|
def replace_ascii(message):
|
||||||
return message.replace(" ", "\\040").replace("\"", "\\042").replace("#", "\\043").replace("$", "\\044")\
|
return message.replace(" ", "\\040").replace("\"", "\\042").replace("#", "\\043").replace("$", "\\044")\
|
||||||
|
|
Loading…
Reference in New Issue