Compare commits

...

5 Commits

Author SHA1 Message Date
jolheiser fd3ffa6857
Fix coords clause
Signed-off-by: jolheiser <john.olheiser@gmail.com>
2022-02-12 09:38:47 -06:00
jolheiser e1b3319247
Fix connection import
Signed-off-by: jolheiser <john.olheiser@gmail.com>
2022-02-12 09:28:56 -06:00
jolheiser e3b0194ebd Fix coords and re-add presets (#4)
Co-authored-by: Etzelia <etzelia@hotmail.com>
Reviewed-on: #4
Co-authored-by: jolheiser <john.olheiser@gmail.com>
Co-committed-by: jolheiser <john.olheiser@gmail.com>
2022-02-11 03:20:48 +00:00
Etzelia e5160f3839 Check world instead of X because of false-y values (#3)
Check world instead of X because of false-y values

Signed-off-by: Etzelia <etzelia@hotmail.com>

Reviewed-on: https://git.canopymc.net/Canopy/django_coreprotect/pulls/3
Co-Authored-By: Etzelia <etzelia@hotmail.com>
Co-Committed-By: Etzelia <etzelia@hotmail.com>
2021-07-17 01:56:00 +00:00
Etzelia fbd29e2c70 concat (#1)
Fix concatenation for MySQL

Signed-off-by: Etzelia <etzelia@hotmail.com>

Add block clause (#1)

Change UNION to UNION ALL

Change action to compare with strings

Improve LIKE query

Signed-off-by: Etzelia <etzelia@hotmail.com>

Try to prevent corrupt activity

Signed-off-by: Etzelia <etzelia@hotmail.com>

Update user in activity query

Signed-off-by: Etzelia <etzelia@hotmail.com>

Convert to HAVING

Signed-off-by: Etzelia <etzelia@hotmail.com>

Escape percent signs in raw SQL

Signed-off-by: Etzelia <etzelia@hotmail.com>

Convert id to rowid

Signed-off-by: Etzelia <etzelia@hotmail.com>

New migration

Signed-off-by: Etzelia <etzelia@hotmail.com>

Add activity pages

Signed-off-by: Etzelia <etzelia@hotmail.com>

Finish pagination

Re-work permissions (and test)
Move form methods to separate file

Signed-off-by: Etzelia <etzelia@hotmail.com>

Start pagination

SQL is finished

Signed-off-by: Etzelia <etzelia@hotmail.com>

Half SQL completed

Signed-off-by: Etzelia <etzelia@hotmail.com>

Move to Bulma
Start UI work

Signed-off-by: Etzelia <etzelia@hotmail.com>

More work

Signed-off-by: Etzelia <etzelia@hotmail.com>

Add migration

Signed-off-by: Etzelia <etzelia@hotmail.com>

More work

Signed-off-by: Etzelia <etzelia@hotmail.com>

Initial local commit

Signed-off-by: Etzelia <etzelia@hotmail.com>

Reviewed-on: https://git.birbmc.com/BirbMC/django_coreprotect/pulls/1
Co-Authored-By: Etzelia <etzelia@hotmail.com>
Co-Committed-By: Etzelia <etzelia@hotmail.com>
2021-03-16 02:02:17 +00:00
32 changed files with 9754 additions and 1 deletions

5
.gitignore vendored 100644
View File

@ -0,0 +1,5 @@
# PyCharm
.idea
# Compiled
*.pyc

View File

@ -1,2 +1 @@
# DjangoCoreProtect # DjangoCoreProtect

0
__init__.py 100644
View File

75
activity.py 100644
View File

@ -0,0 +1,75 @@
from django_coreprotect.models import SessionResult, ActivityResult
from django_coreprotect.utils import safe_int
from datetime import datetime
class ActivityForm:
def __init__(self):
self.players = ""
self.date_from = ""
self.date_to = ""
def activity_data(request):
request_data = request.GET
form = ActivityForm()
# Players
form.players = request_data["players"] if "players" in request_data else ""
# Date and Time
form.date_from = request_data["date_from"] if "date_from" in request_data else ""
form.date_to = request_data["date_to"] if "date_to" in request_data else ""
return form
def activity_results(form):
query = ""
players_clause = ""
if form.players:
players = []
for player in form.players.split(","):
players.append(player.strip())
players_clause = " WHERE ({}) ".format(" OR ".join(["cu.user LIKE '%%{}%%'".format(p) for p in players]))
time_clause = ""
if form.date_from or form.date_to:
df, dt = form.date_from, form.date_to
if form.date_from and not form.date_to:
dt = datetime.now().timestamp()
if form.date_to and not form.date_from:
df = datetime.now().timestamp()
time_clause = " HAVING unix BETWEEN {} AND {} ".format(df, dt)
if players_clause or time_clause:
query = '''SELECT
0 AS id, cs.time AS unix, cu.user AS player, cs.action
FROM co_session cs
JOIN co_user cu ON cs.user = cu.rowid
{players}
{time}
'''.format(players=players_clause, time=time_clause)
if query:
print(query)
sessions = SessionResult.objects.raw(query)
activity = {}
last_session = {}
for session in sessions:
time = -safe_int(session.unix) if session.action == 1 else safe_int(session.unix)
if session.player in activity:
if last_session[session.player] == session.action:
continue
activity[session.player] += time
last_session[session.player] = session.action
else:
activity[session.player] = time
last_session[session.player] = session.action
results = []
for player, time in activity.items():
results.append(ActivityResult(player, time))
return results
return []

54
admin.py 100644
View File

@ -0,0 +1,54 @@
import ast
from django import forms
from django.contrib import admin
from django.contrib.admin import widgets as admin_widgets
from django_coreprotect.models import CoWorld, Preset
from django_coreprotect.utils import time_to_form, form_to_time
class PresetAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(PresetAdminForm, self).__init__(*args, **kwargs)
worlds = tuple([(world.id, world.world) for world in CoWorld.objects.all()])
self.fields['worlds'].widget = forms.SelectMultiple(choices=worlds)
self.fields['date_from'].widget = admin_widgets.AdminSplitDateTime()
self.fields['date_to'].widget = admin_widgets.AdminSplitDateTime()
if self.instance:
if "worlds" in self.initial and self.initial["worlds"]:
self.initial["worlds"] = tuple(self.initial["worlds"])
if "date_from" in self.initial and self.initial["date_from"]:
self.initial["date_from"] = time_to_form(self.initial["date_from"])
if "date_to" in self.initial and self.initial["date_to"]:
self.initial["date_to"] = time_to_form(self.initial["date_to"])
def clean(self):
cleaned = super(PresetAdminForm, self).clean()
worlds = cleaned.get("worlds", "")
if worlds:
worlds = ast.literal_eval(worlds)
cleaned.update({"worlds": ",".join(worlds)})
date_from = cleaned.get("date_from", "")
if date_from:
cleaned.update({"date_from": form_to_time(date_from)})
date_to = cleaned.get("date_to", "")
if date_to:
cleaned.update({"date_to": form_to_time(date_to)})
return cleaned
class Meta:
model = Preset
fields = "__all__"
class PresetAdmin(admin.ModelAdmin):
form = PresetAdminForm
try:
admin.site.register(Preset, PresetAdmin)
except admin.sites.AlreadyRegistered:
pass

5
apps.py 100644
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class DjangoCoreprotectConfig(AppConfig):
name = 'django_coreprotect'

253
gui.py 100644
View File

@ -0,0 +1,253 @@
from datetime import datetime
from django.db import connection
from django_coreprotect.models import CoWorld, GUIResult
from django_coreprotect.utils import safe_int, checkbox, time_to_form
class GUIForm:
def __init__(self):
self.block_break = False
self.block_place = False
self.chat = False
self.chest_use = False
self.command = False
self.interact = False
self.login_logout = False
self.sign_place = False
self.worlds = []
self.ignore_environment = False
self.oldest_first = False
self.page = ""
self.page_size = ""
self.start = ""
self.players = ""
self.x = ""
self.y = ""
self.z = ""
self.radius = ""
self.blocks = ""
self.date_from = ""
self.date_to = ""
def gui_data(request):
request_data = request.GET
form = GUIForm()
worlds = CoWorld.objects.all()
# Actions
form.block_break = checkbox(request_data["block_break"]) if "block_break" in request_data else False
form.block_place = checkbox(request_data["block_place"]) if "block_place" in request_data else False
form.chat = checkbox(request_data["chat"]) if "chat" in request_data else False
form.chest_use = checkbox(request_data["chest_use"]) if "chest_use" in request_data else False
form.command = checkbox(request_data["command"]) if "command" in request_data else False
form.interact = checkbox(request_data["interact"]) if "interact" in request_data else False
form.login_logout = checkbox(request_data["login_logout"]) if "login_logout" in request_data else False
form.sign_place = checkbox(request_data["sign_place"]) if "sign_place" in request_data else False
# Permissions
if not request.user.has_perm("django_coreprotect.gui_extra"):
form.chat = False
form.command = False
# Worlds
request_worlds = request_data.getlist("world")
for world in worlds:
world_id = "world_{}".format(world.id)
w = {
"id": str(world.id),
"world_id": world_id,
"checked": True if str(world.id) in request_worlds else False,
"name": world.world
}
form.worlds.append(w)
# Options
form.ignore_environment = checkbox(request_data["ignore_environment"]) if "ignore_environment" in request_data else False
form.oldest_first = checkbox(request_data["oldest_first"]) if "oldest_first" in request_data else False
# Limit Results
form.page = request_data["page"] if "page" in request_data else "0"
form.page_size = request_data["page_size"] if "page_size" in request_data else "20"
if safe_int(form.page_size) == 0 or safe_int(form.page_size) > 1000:
form.page_size = "1000"
form.start = (safe_int(form.page) * safe_int(form.page_size))
# Players
form.players = request_data["players"] if "players" in request_data else ""
# Coordinates
form.x = request_data["x"] if "x" in request_data else ""
form.y = request_data["y"] if "y" in request_data else ""
form.z = request_data["z"] if "z" in request_data else ""
form.radius = request_data["radius"] if "radius" in request_data else ""
# Blocks
form.blocks = request_data["blocks"] if "blocks" in request_data else ""
# Date and Time
form.date_from = time_to_form(request_data["date_from"]) if "date_from" in request_data else ""
if form.date_from:
form.date_from = form.date_from.timestamp()
form.date_to = time_to_form(request_data["date_to"]) if "date_to" in request_data else ""
if form.date_to:
form.date_to = form.date_to.timestamp()
return form
def gui_results(form):
queries = []
ignore_environment = " AND cu.user NOT LIKE '#%%' " if form.ignore_environment else ""
oldest_first = " ASC " if form.oldest_first else " DESC "
coords = []
radius = form.radius if form.radius else "0"
if form.x:
coords.append(" x BETWEEN {0} - {1} AND {0} + {1}".format(form.x, radius))
if form.y:
coords.append(" y BETWEEN {0} - {1} AND {0} + {1}".format(form.y, radius))
if form.z:
coords.append(" z BETWEEN {0} - {1} AND {0} + {1}".format(form.z, radius))
coords_clause = ""
if coords:
coords_clause = " AND ({})".format(" AND ".join(coords))
players = []
players_clause = ""
if form.players:
for player in form.players.split(","):
players.append(player.strip())
players_clause = " AND ({})".format(" OR ".join(["cu.user LIKE '{}%%'".format(p) for p in players]))
blocks = []
blocks_clause = ""
chat_clause = ""
command_clause = ""
if form.blocks:
for block in form.blocks.split(","):
blocks.append(block.strip())
blocks_clause = " AND ({})".format(" OR ".join(["cmm.material LIKE 'minecraft:{}%%'".format(b) for b in blocks]))
chat_clause = " AND ({})".format(" OR ".join(["cc.message LIKE '%%{}%%'".format(b) for b in blocks]))
command_clause = " AND ({})".format(" OR ".join(["cc.message LIKE '%%{}%%'".format(b) for b in blocks]))
worlds_clause = ""
worlds = [world["id"] for world in form.worlds if world["checked"]]
if len(worlds):
worlds_clause = " AND cw.rowid IN ({})".format(",".join(worlds))
time_clause = ""
if form.date_from or form.date_to:
df, dt = form.date_from, form.date_to
if form.date_from and not form.date_to:
dt = datetime.now().timestamp()
if form.date_to and not form.date_from:
df = datetime.now().timestamp()
time_clause = " GROUP BY unix HAVING unix BETWEEN {} AND {} ".format(df, dt)
# Block Break, Block Place, and Interact
block_actions = []
if form.block_break:
block_actions.append("0")
if form.block_place:
block_actions.append("1")
if form.interact:
block_actions.append("2")
if len(block_actions):
queries.append('''SELECT
0 AS id, "block" AS type, cb.time AS unix, cu.user AS player, cb.action, cmm.material AS data, cb.x, cb.y, cb.z, cw.world
FROM co_block cb
JOIN co_user cu ON cb.user = cu.rowid
JOIN co_material_map cmm ON cb.type = cmm.rowid
JOIN co_world cw ON cb.wid = cw.rowid
WHERE cb.action IN ({action})
{ignore_environment}
{players}
{coords}
{blocks}
{worlds}
{time}
'''.format(action=",".join(block_actions), ignore_environment=ignore_environment, players=players_clause,
coords=coords_clause, blocks=blocks_clause, worlds=worlds_clause, time=time_clause))
# Chat
if form.chat:
queries.append('''SELECT
0 AS id, "chat" AS type, cc.time AS unix, cu.user AS player, "" AS action, cc.message AS data, "" AS x, "" AS y, "" AS z, "" AS world
FROM co_chat cc
JOIN co_user cu ON cc.user = cu.rowid
WHERE 1 = 1
{players}
{chat}
{time}
'''.format(players=players_clause, chat=chat_clause, time=time_clause))
# Chest Use
if form.chest_use:
concat = 'CONCAT(cc.amount, " ", cmm.material)'
if connection.vendor == "sqlite":
concat = 'cc.amount || " " || cmm.material'
queries.append('''SELECT
0 AS id, "container" AS type, cc.time AS unix, cu.user AS player, cc.action, {concat} AS data, cc.x, cc.y, cc.z, cw.world
FROM co_container cc
JOIN co_user cu ON cc.user = cu.rowid
JOIN co_material_map cmm ON cc.type = cmm.rowid
JOIN co_world cw ON cc.wid = cw.rowid
WHERE 1 = 1
{worlds}
{players}
{coords}
{blocks}
{time}
'''.format(concat=concat, worlds=worlds_clause, players=players_clause, coords=coords_clause, blocks=blocks_clause, time=time_clause))
# Commands
if form.command:
queries.append('''SELECT
0 AS id, "command" AS type, cc.time AS unix, cu.user AS player, "" AS action, cc.message AS data, "" AS x, "" AS y, "" AS z, "" AS world
FROM co_command cc
JOIN co_user cu ON cc.user = cu.rowid
WHERE 1 = 1
{players}
{command}
{coords}
{time}
'''.format(players=players_clause, command=command_clause, coords=coords_clause, time=time_clause))
# Login/Logout
if form.login_logout:
queries.append('''SELECT
0 AS id, "session" AS type, cs.time AS unix, cu.user AS player, cs.action, "" AS data, cs.x, cs.y, cs.z, cw.world
FROM co_session cs
JOIN co_user cu ON cs.user = cu.rowid
JOIN co_world cw ON cs.wid = cw.rowid
WHERE 1 = 1
{worlds}
{players}
{coords}
{time}
'''.format(worlds=worlds_clause, players=players_clause, coords=coords_clause, time=time_clause))
# Sign Place
if form.sign_place:
queries.append('''SELECT
0 AS id, "sign" AS type, cs.time AS unix, cu.user AS player, "" AS action, cs.line_1 || "|" || cs.line_2 || "|" || cs.line_3 || "|" || cs.line_4 AS data, cs.x, cs.y, cs.z, cw.world
FROM co_sign cs
JOIN co_user cu ON cs.user = cu.rowid
JOIN co_world cw ON cs.wid = cw.rowid
WHERE 1 = 1
{worlds}
{players}
{coords}
{time}
'''.format(worlds=worlds_clause, players=players_clause, coords=coords_clause, time=time_clause))
query = " UNION ALL ".join(queries)
if query:
query += " ORDER BY unix {} LIMIT {}, {}".format(oldest_first, form.start, form.page_size)
if query:
return GUIResult.objects.raw(query)
return []

View File

@ -0,0 +1,336 @@
# Generated by Django 2.2.3 on 2019-08-16 16:28
import django.contrib.auth.models
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0011_update_proxy_permissions'),
]
operations = [
migrations.CreateModel(
name='CoArtMap',
fields=[
('id', models.IntegerField(primary_key=True, serialize=False)),
('art', models.TextField(blank=True, null=True)),
],
options={
'verbose_name': 'Art Map',
'verbose_name_plural': 'Art Maps',
'db_table': 'co_art_map',
'managed': False,
},
),
migrations.CreateModel(
name='CoBlock',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('time', models.IntegerField(blank=True, null=True)),
('user', models.IntegerField(blank=True, null=True)),
('wid', models.IntegerField(blank=True, null=True)),
('x', models.IntegerField(blank=True, null=True)),
('y', models.IntegerField(blank=True, null=True)),
('z', models.IntegerField(blank=True, null=True)),
('type', models.IntegerField(blank=True, null=True)),
('data', models.IntegerField(blank=True, null=True)),
('meta', models.BinaryField(blank=True, null=True)),
('blockdata', models.BinaryField(blank=True, null=True)),
('action', models.IntegerField(blank=True, null=True)),
('rolled_back', models.IntegerField(blank=True, null=True)),
],
options={
'verbose_name': 'Block',
'verbose_name_plural': 'Blocks',
'db_table': 'co_block',
'managed': False,
},
),
migrations.CreateModel(
name='CoBlockdataMap',
fields=[
('id', models.IntegerField(primary_key=True, serialize=False)),
('data', models.TextField(blank=True, null=True)),
],
options={
'verbose_name': 'BlockData Map',
'verbose_name_plural': 'BlockData Maps',
'db_table': 'co_blockdata_map',
'managed': False,
},
),
migrations.CreateModel(
name='CoChat',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('time', models.IntegerField(blank=True, null=True)),
('user', models.IntegerField(blank=True, null=True)),
('message', models.TextField(blank=True, null=True)),
],
options={
'verbose_name': 'Chat Message',
'verbose_name_plural': 'Chat Messages',
'db_table': 'co_chat',
'managed': False,
},
),
migrations.CreateModel(
name='CoCommand',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('time', models.IntegerField(blank=True, null=True)),
('user', models.IntegerField(blank=True, null=True)),
('message', models.TextField(blank=True, null=True)),
],
options={
'verbose_name': 'Command',
'verbose_name_plural': 'Commands',
'db_table': 'co_command',
'managed': False,
},
),
migrations.CreateModel(
name='CoContainer',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('time', models.IntegerField(blank=True, null=True)),
('user', models.IntegerField(blank=True, null=True)),
('wid', models.IntegerField(blank=True, null=True)),
('x', models.IntegerField(blank=True, null=True)),
('y', models.IntegerField(blank=True, null=True)),
('z', models.IntegerField(blank=True, null=True)),
('type', models.IntegerField(blank=True, null=True)),
('data', models.IntegerField(blank=True, null=True)),
('amount', models.IntegerField(blank=True, null=True)),
('metadata', models.BinaryField(blank=True, null=True)),
('action', models.IntegerField(blank=True, null=True)),
('rolled_back', models.IntegerField(blank=True, null=True)),
],
options={
'verbose_name': 'Container Transaction',
'verbose_name_plural': 'Container Transactions',
'db_table': 'co_container',
'managed': False,
},
),
migrations.CreateModel(
name='CoDatabaseLock',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.IntegerField(blank=True, null=True)),
('time', models.IntegerField(blank=True, null=True)),
],
options={
'verbose_name': 'Database Lock',
'verbose_name_plural': 'Database Locks',
'db_table': 'co_database_lock',
'managed': False,
},
),
migrations.CreateModel(
name='CoEntity',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('time', models.IntegerField(blank=True, null=True)),
('data', models.BinaryField(blank=True, null=True)),
],
options={
'verbose_name': 'Entity',
'verbose_name_plural': 'Entities',
'db_table': 'co_entity',
'managed': False,
},
),
migrations.CreateModel(
name='CoEntityMap',
fields=[
('id', models.IntegerField(primary_key=True, serialize=False)),
('entity', models.TextField(blank=True, null=True)),
],
options={
'verbose_name': 'Entity Mapping',
'verbose_name_plural': 'Entity Mappings',
'db_table': 'co_entity_map',
'managed': False,
},
),
migrations.CreateModel(
name='CoMaterialMap',
fields=[
('id', models.IntegerField(primary_key=True, serialize=False)),
('material', models.TextField(blank=True, null=True)),
],
options={
'verbose_name': 'Material Mapping',
'verbose_name_plural': 'Material Mappings',
'db_table': 'co_material_map',
'managed': False,
},
),
migrations.CreateModel(
name='CoSession',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('time', models.IntegerField(blank=True, null=True)),
('user', models.IntegerField(blank=True, null=True)),
('wid', models.IntegerField(blank=True, null=True)),
('x', models.IntegerField(blank=True, null=True)),
('y', models.IntegerField(blank=True, null=True)),
('z', models.IntegerField(blank=True, null=True)),
('action', models.IntegerField(blank=True, null=True)),
],
options={
'verbose_name': 'Session',
'verbose_name_plural': 'Sessions',
'db_table': 'co_session',
'managed': False,
},
),
migrations.CreateModel(
name='CoSign',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('time', models.IntegerField(blank=True, null=True)),
('user', models.IntegerField(blank=True, null=True)),
('wid', models.IntegerField(blank=True, null=True)),
('x', models.IntegerField(blank=True, null=True)),
('y', models.IntegerField(blank=True, null=True)),
('z', models.IntegerField(blank=True, null=True)),
('color', models.IntegerField(blank=True, null=True)),
('line_1', models.TextField(blank=True, null=True)),
('line_2', models.TextField(blank=True, null=True)),
('line_3', models.TextField(blank=True, null=True)),
('line_4', models.TextField(blank=True, null=True)),
],
options={
'verbose_name': 'Sign',
'verbose_name_plural': 'Signs',
'db_table': 'co_sign',
'managed': False,
},
),
migrations.CreateModel(
name='CoSkull',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('time', models.IntegerField(blank=True, null=True)),
('owner', models.TextField(blank=True, null=True)),
],
options={
'verbose_name': 'Skull',
'verbose_name_plural': 'Skulls',
'db_table': 'co_skull',
'managed': False,
},
),
migrations.CreateModel(
name='CoUser',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('time', models.IntegerField(blank=True, null=True)),
('user', models.TextField(blank=True, null=True)),
('uuid', models.TextField(blank=True, null=True)),
],
options={
'verbose_name': 'User',
'verbose_name_plural': 'Users',
'db_table': 'co_user',
'managed': False,
},
),
migrations.CreateModel(
name='CoUsernameLog',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('time', models.IntegerField(blank=True, null=True)),
('uuid', models.TextField(blank=True, null=True)),
('user', models.TextField(blank=True, null=True)),
],
options={
'verbose_name': 'Username',
'verbose_name_plural': 'Usernames',
'db_table': 'co_username_log',
'managed': False,
},
),
migrations.CreateModel(
name='CoVersion',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('time', models.IntegerField(blank=True, null=True)),
('version', models.TextField(blank=True, null=True)),
],
options={
'verbose_name': 'Version',
'verbose_name_plural': 'Versions',
'db_table': 'co_version',
'managed': False,
},
),
migrations.CreateModel(
name='CoWorld',
fields=[
('id', models.IntegerField(primary_key=True, serialize=False)),
('world', models.TextField(blank=True, null=True)),
],
options={
'verbose_name': 'World',
'verbose_name_plural': 'Worlds',
'db_table': 'co_world',
'managed': False,
},
),
migrations.CreateModel(
name='GUIResult',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('type', models.TextField()),
('unix', models.TextField()),
('player', models.TextField()),
('action', models.TextField()),
('data', models.TextField()),
('x', models.TextField()),
('y', models.TextField()),
('z', models.TextField()),
('world', models.TextField()),
],
options={
'verbose_name': 'GUI Result',
'verbose_name_plural': 'GUI Results',
'managed': False,
},
),
migrations.CreateModel(
name='SessionResult',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('unix', models.TextField()),
('player', models.TextField()),
('action', models.TextField()),
],
options={
'verbose_name': 'Session Result',
'verbose_name_plural': 'Session Results',
'managed': False,
},
),
migrations.CreateModel(
name='CoreProtectUser',
fields=[
],
options={
'permissions': (('gui', 'Can use CoreProtect GUI'), ('gui_extra', 'Can search Chat/Commands'), ('activity', 'Can use CoreProtect Activity Monitor')),
'proxy': True,
'indexes': [],
'constraints': [],
},
bases=('auth.user',),
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
]

View File

@ -0,0 +1,40 @@
# Generated by Django 2.2.3 on 2019-10-01 04:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('django_coreprotect', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Preset',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50)),
('block_break', models.BooleanField(default=False)),
('block_place', models.BooleanField(default=False)),
('chat', models.BooleanField(default=False)),
('chest_use', models.BooleanField(default=False)),
('command', models.BooleanField(default=False)),
('interact', models.BooleanField(default=False)),
('login_logout', models.BooleanField(default=False)),
('sign_place', models.BooleanField(default=False)),
('worlds', models.CharField(blank=True, default='', max_length=20)),
('ignore_environment', models.BooleanField(default=False)),
('oldest_first', models.BooleanField(default=False)),
('players', models.CharField(blank=True, max_length=50)),
('x', models.CharField(blank=True, default='', max_length=10)),
('y', models.CharField(blank=True, default='', max_length=10)),
('z', models.CharField(blank=True, default='', max_length=10)),
('radius', models.CharField(blank=True, default='', max_length=10)),
('blocks', models.CharField(blank=True, default='', max_length=50)),
('date_from', models.CharField(blank=True, default='', max_length=50)),
('date_to', models.CharField(blank=True, default='', max_length=50)),
('enabled', models.BooleanField(default=True)),
],
),
]

View File

448
models.py 100644
View File

@ -0,0 +1,448 @@
from datetime import datetime
from django.contrib.auth.models import User
from django.db import models
from django.shortcuts import reverse
from django_coreprotect.utils import safe_int
class CoreProtectUser(User):
class Meta:
proxy = True
permissions = (
('gui', 'Can use CoreProtect GUI'),
('gui_extra', 'Can search Chat/Commands'),
('activity', 'Can use CoreProtect Activity Monitor'),
)
class CoArtMap(models.Model):
id = models.IntegerField(primary_key=True)
art = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'co_art_map'
verbose_name = 'Art Map'
verbose_name_plural = 'Art Maps'
def __str__(self):
return self.art
class CoBlock(models.Model):
time = models.IntegerField(blank=True, null=True)
user = models.IntegerField(blank=True, null=True)
wid = models.IntegerField(blank=True, null=True)
x = models.IntegerField(blank=True, null=True)
y = models.IntegerField(blank=True, null=True)
z = models.IntegerField(blank=True, null=True)
type = models.IntegerField(blank=True, null=True)
data = models.IntegerField(blank=True, null=True)
meta = models.BinaryField(blank=True, null=True)
blockdata = models.BinaryField(blank=True, null=True)
action = models.IntegerField(blank=True, null=True)
rolled_back = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'co_block'
verbose_name = 'Block'
verbose_name_plural = 'Blocks'
def __str__(self):
return self.type
class CoBlockdataMap(models.Model):
id = models.IntegerField(primary_key=True)
data = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'co_blockdata_map'
verbose_name = 'BlockData Map'
verbose_name_plural = 'BlockData Maps'
def __str__(self):
return self.data
class CoChat(models.Model):
time = models.IntegerField(blank=True, null=True)
user = models.IntegerField(blank=True, null=True)
message = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'co_chat'
verbose_name = 'Chat Message'
verbose_name_plural = 'Chat Messages'
def __str__(self):
return self.message
class CoCommand(models.Model):
time = models.IntegerField(blank=True, null=True)
user = models.IntegerField(blank=True, null=True)
message = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'co_command'
verbose_name = 'Command'
verbose_name_plural = 'Commands'
def __str__(self):
return self.message
class CoContainer(models.Model):
time = models.IntegerField(blank=True, null=True)
user = models.IntegerField(blank=True, null=True)
wid = models.IntegerField(blank=True, null=True)
x = models.IntegerField(blank=True, null=True)
y = models.IntegerField(blank=True, null=True)
z = models.IntegerField(blank=True, null=True)
type = models.IntegerField(blank=True, null=True)
data = models.IntegerField(blank=True, null=True)
amount = models.IntegerField(blank=True, null=True)
metadata = models.BinaryField(blank=True, null=True)
action = models.IntegerField(blank=True, null=True)
rolled_back = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'co_container'
verbose_name = 'Container Transaction'
verbose_name_plural = 'Container Transactions'
def __str__(self):
return self.user
class CoDatabaseLock(models.Model):
status = models.IntegerField(blank=True, null=True)
time = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'co_database_lock'
verbose_name = 'Database Lock'
verbose_name_plural = 'Database Locks'
def __str__(self):
return self.status
class CoEntity(models.Model):
time = models.IntegerField(blank=True, null=True)
data = models.BinaryField(blank=True, null=True)
class Meta:
managed = False
db_table = 'co_entity'
verbose_name = 'Entity'
verbose_name_plural = 'Entities'
def __str__(self):
return self.time
class CoEntityMap(models.Model):
id = models.IntegerField(primary_key=True)
entity = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'co_entity_map'
verbose_name = 'Entity Mapping'
verbose_name_plural = 'Entity Mappings'
def __str__(self):
return self.entity
class CoMaterialMap(models.Model):
id = models.IntegerField(primary_key=True)
material = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'co_material_map'
verbose_name = 'Material Mapping'
verbose_name_plural = 'Material Mappings'
def __str__(self):
return self.material
class CoSession(models.Model):
time = models.IntegerField(blank=True, null=True)
user = models.IntegerField(blank=True, null=True)
wid = models.IntegerField(blank=True, null=True)
x = models.IntegerField(blank=True, null=True)
y = models.IntegerField(blank=True, null=True)
z = models.IntegerField(blank=True, null=True)
action = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'co_session'
verbose_name = 'Session'
verbose_name_plural = 'Sessions'
def __str__(self):
return self.user
class CoSign(models.Model):
time = models.IntegerField(blank=True, null=True)
user = models.IntegerField(blank=True, null=True)
wid = models.IntegerField(blank=True, null=True)
x = models.IntegerField(blank=True, null=True)
y = models.IntegerField(blank=True, null=True)
z = models.IntegerField(blank=True, null=True)
color = models.IntegerField(blank=True, null=True)
line_1 = models.TextField(blank=True, null=True)
line_2 = models.TextField(blank=True, null=True)
line_3 = models.TextField(blank=True, null=True)
line_4 = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'co_sign'
verbose_name = 'Sign'
verbose_name_plural = 'Signs'
def __str__(self):
return "{} | {} | {} | {}".format(self.line_1, self.line_2, self.line_3, self.line_4)
class CoSkull(models.Model):
time = models.IntegerField(blank=True, null=True)
owner = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'co_skull'
verbose_name = 'Skull'
verbose_name_plural = 'Skulls'
def __str__(self):
return self.owner
class CoUser(models.Model):
time = models.IntegerField(blank=True, null=True)
user = models.TextField(blank=True, null=True)
uuid = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'co_user'
verbose_name = 'User'
verbose_name_plural = 'Users'
def __str__(self):
return self.user
class CoUsernameLog(models.Model):
time = models.IntegerField(blank=True, null=True)
uuid = models.TextField(blank=True, null=True)
user = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'co_username_log'
verbose_name = 'Username'
verbose_name_plural = 'Usernames'
def __str__(self):
return self.user
class CoVersion(models.Model):
time = models.IntegerField(blank=True, null=True)
version = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'co_version'
verbose_name = 'Version'
verbose_name_plural = 'Versions'
def __str__(self):
return self.version
class CoWorld(models.Model):
id = models.IntegerField(primary_key=True)
world = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'co_world'
verbose_name = 'World'
verbose_name_plural = 'Worlds'
def __str__(self):
return self.world
class Preset(models.Model):
name = models.CharField(max_length=50)
block_break = models.BooleanField(default=False)
block_place = models.BooleanField(default=False)
chat = models.BooleanField(default=False)
chest_use = models.BooleanField(default=False)
command = models.BooleanField(default=False)
interact = models.BooleanField(default=False)
login_logout = models.BooleanField(default=False)
sign_place = models.BooleanField(default=False)
worlds = models.CharField(max_length=20, default="", blank=True)
ignore_environment = models.BooleanField(default=False)
oldest_first = models.BooleanField(default=False)
players = models.CharField(max_length=50, blank=True)
x = models.CharField(max_length=10, default="", blank=True)
y = models.CharField(max_length=10, default="", blank=True)
z = models.CharField(max_length=10, default="", blank=True)
radius = models.CharField(max_length=10, default="", blank=True)
blocks = models.CharField(max_length=50, default="", blank=True)
date_from = models.CharField(max_length=50, default="", blank=True)
date_to = models.CharField(max_length=50, default="", blank=True)
enabled = models.BooleanField(default=True)
@property
def link(self):
url = reverse("coreprotect_gui") + "?"
params = []
if self.block_break:
params.append("block_break=on")
if self.block_place:
params.append("block_place=on")
if self.chat:
params.append("chat=on")
if self.chest_use:
params.append("chest_use=on")
if self.command:
params.append("command=on")
if self.interact:
params.append("interact=on")
if self.login_logout:
params.append("login_logout=on")
if self.sign_place:
params.append("sign_place=on")
if self.worlds:
for world in self.worlds.split(","):
params.append("world=" + world)
if self.ignore_environment:
params.append("ignore_environment=on")
if self.oldest_first:
params.append("oldest_first=on")
if self.players:
params.append("players=" + self.players)
if self.x:
params.append("x=" + self.x)
if self.y:
params.append("y=" + self.y)
if self.z:
params.append("z=" + self.z)
if self.radius:
params.append("radius=" + self.radius)
if self.blocks:
params.append("blocks=" + self.blocks)
if self.date_from:
params.append("date_from=" + self.date_from)
if self.date_to:
params.append("date_to=" + self.date_to)
return url + "&".join(params)
def __str__(self):
return self.name
class GUIResult(models.Model):
type = models.TextField()
unix = models.TextField()
player = models.TextField()
action = models.TextField()
data = models.TextField()
x = models.TextField()
y = models.TextField()
z = models.TextField()
world = models.TextField()
@property
def display_time(self):
dt = datetime.fromtimestamp(float(self.unix))
return dt.strftime("%b %d, %Y at %I:%M:%S %p")
@property
def display_action(self):
if self.type == "block":
return "Block Break" if str(self.action) == "0" else "Block Place" if str(self.action) == "1" else "Interact/Used"
if self.type == "chat":
return "Chat"
if self.type == "container":
return "Took from Container" if str(self.action) == "0" else "Placed in Container"
if self.type == "command":
return "Command"
if self.type == "session":
return "Logout" if str(self.action) == "0" else "Login"
if self.type == "sign":
return "Sign"
return self.action
@property
def display_data(self):
def material_name(namespace):
m = namespace.replace("minecraft:", "")
pp = m.split("_")
return " ".join([p.capitalize() for p in pp])
if self.type == "block":
return material_name(self.data)
if self.type == "container":
parts = self.data.split()
return "{} {}".format(parts[0], material_name(parts[1]))
return self.data
class Meta:
managed = False
verbose_name = "GUI Result"
verbose_name_plural = "GUI Results"
class SessionResult(models.Model):
unix = models.TextField()
player = models.TextField()
action = models.TextField()
class Meta:
managed = False
verbose_name = "Session Result"
verbose_name_plural = "Session Results"
class ActivityResult:
def __init__(self, player, time):
self.player = player
self.time = time
@property
def time_display(self):
time = safe_int(self.time)
seconds = time % 60
time //= 60
minutes = time % 60
time //= 60
hours = time % 24
time //= 24
days = time
return "{}d, {}h, {}m, {}s".format(days, hours, minutes, seconds)

38
router.py 100644
View File

@ -0,0 +1,38 @@
class CoreProtectRouter:
"""
A router to control all database operations on models in the
auth application.
"""
def db_for_read(self, model, **hints):
"""
Attempts to read auth models go to auth_db.
"""
if model._meta.app_label == 'django_coreprotect':
return 'django_coreprotect'
return None
def db_for_write(self, model, **hints):
"""
Attempts to write django_coreprotect models go to django_coreprotect.
"""
if model._meta.app_label == 'django_coreprotect':
return 'django_coreprotect'
return None
def allow_relation(self, obj1, obj2, **hints):
"""
Allow relations if a model in the django_coreprotect app is involved.
"""
if obj1._meta.app_label == 'django_coreprotect' or \
obj2._meta.app_label == 'django_coreprotect':
return True
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
"""
Make sure the django_coreprotect app only appears in the 'django_coreprotect'
database.
"""
if app_label == 'django_coreprotect':
return db == 'django_coreprotect'
return None

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,784 @@
.flatpickr-calendar {
background: transparent;
opacity: 0;
display: none;
text-align: center;
visibility: hidden;
padding: 0;
-webkit-animation: none;
animation: none;
direction: ltr;
border: 0;
font-size: 14px;
line-height: 24px;
border-radius: 5px;
position: absolute;
width: 307.875px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
-ms-touch-action: manipulation;
touch-action: manipulation;
background: #3f4458;
-webkit-box-shadow: 1px 0 0 #20222c, -1px 0 0 #20222c, 0 1px 0 #20222c, 0 -1px 0 #20222c, 0 3px 13px rgba(0,0,0,0.08);
box-shadow: 1px 0 0 #20222c, -1px 0 0 #20222c, 0 1px 0 #20222c, 0 -1px 0 #20222c, 0 3px 13px rgba(0,0,0,0.08);
}
.flatpickr-calendar.open,
.flatpickr-calendar.inline {
opacity: 1;
max-height: 640px;
visibility: visible;
}
.flatpickr-calendar.open {
display: inline-block;
z-index: 99999;
}
.flatpickr-calendar.animate.open {
-webkit-animation: fpFadeInDown 300ms cubic-bezier(0.23, 1, 0.32, 1);
animation: fpFadeInDown 300ms cubic-bezier(0.23, 1, 0.32, 1);
}
.flatpickr-calendar.inline {
display: block;
position: relative;
top: 2px;
}
.flatpickr-calendar.static {
position: absolute;
top: calc(100% + 2px);
}
.flatpickr-calendar.static.open {
z-index: 999;
display: block;
}
.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+1) .flatpickr-day.inRange:nth-child(7n+7) {
-webkit-box-shadow: none !important;
box-shadow: none !important;
}
.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+2) .flatpickr-day.inRange:nth-child(7n+1) {
-webkit-box-shadow: -2px 0 0 #e6e6e6, 5px 0 0 #e6e6e6;
box-shadow: -2px 0 0 #e6e6e6, 5px 0 0 #e6e6e6;
}
.flatpickr-calendar .hasWeeks .dayContainer,
.flatpickr-calendar .hasTime .dayContainer {
border-bottom: 0;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.flatpickr-calendar .hasWeeks .dayContainer {
border-left: 0;
}
.flatpickr-calendar.showTimeInput.hasTime .flatpickr-time {
height: 40px;
border-top: 1px solid #20222c;
}
.flatpickr-calendar.noCalendar.hasTime .flatpickr-time {
height: auto;
}
.flatpickr-calendar:before,
.flatpickr-calendar:after {
position: absolute;
display: block;
pointer-events: none;
border: solid transparent;
content: '';
height: 0;
width: 0;
left: 22px;
}
.flatpickr-calendar.rightMost:before,
.flatpickr-calendar.rightMost:after {
left: auto;
right: 22px;
}
.flatpickr-calendar:before {
border-width: 5px;
margin: 0 -5px;
}
.flatpickr-calendar:after {
border-width: 4px;
margin: 0 -4px;
}
.flatpickr-calendar.arrowTop:before,
.flatpickr-calendar.arrowTop:after {
bottom: 100%;
}
.flatpickr-calendar.arrowTop:before {
border-bottom-color: #20222c;
}
.flatpickr-calendar.arrowTop:after {
border-bottom-color: #3f4458;
}
.flatpickr-calendar.arrowBottom:before,
.flatpickr-calendar.arrowBottom:after {
top: 100%;
}
.flatpickr-calendar.arrowBottom:before {
border-top-color: #20222c;
}
.flatpickr-calendar.arrowBottom:after {
border-top-color: #3f4458;
}
.flatpickr-calendar:focus {
outline: 0;
}
.flatpickr-wrapper {
position: relative;
display: inline-block;
}
.flatpickr-months {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
.flatpickr-months .flatpickr-month {
background: #3f4458;
color: #fff;
fill: #fff;
height: 34px;
line-height: 1;
text-align: center;
position: relative;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
overflow: hidden;
-webkit-box-flex: 1;
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
}
.flatpickr-months .flatpickr-prev-month,
.flatpickr-months .flatpickr-next-month {
text-decoration: none;
cursor: pointer;
position: absolute;
top: 0;
height: 34px;
padding: 10px;
z-index: 3;
color: #fff;
fill: #fff;
}
.flatpickr-months .flatpickr-prev-month.flatpickr-disabled,
.flatpickr-months .flatpickr-next-month.flatpickr-disabled {
display: none;
}
.flatpickr-months .flatpickr-prev-month i,
.flatpickr-months .flatpickr-next-month i {
position: relative;
}
.flatpickr-months .flatpickr-prev-month.flatpickr-prev-month,
.flatpickr-months .flatpickr-next-month.flatpickr-prev-month {
/*
/*rtl:begin:ignore*/
/*
*/
left: 0;
/*
/*rtl:end:ignore*/
/*
*/
}
/*
/*rtl:begin:ignore*/
/*
/*rtl:end:ignore*/
.flatpickr-months .flatpickr-prev-month.flatpickr-next-month,
.flatpickr-months .flatpickr-next-month.flatpickr-next-month {
/*
/*rtl:begin:ignore*/
/*
*/
right: 0;
/*
/*rtl:end:ignore*/
/*
*/
}
/*
/*rtl:begin:ignore*/
/*
/*rtl:end:ignore*/
.flatpickr-months .flatpickr-prev-month:hover,
.flatpickr-months .flatpickr-next-month:hover {
color: #eee;
}
.flatpickr-months .flatpickr-prev-month:hover svg,
.flatpickr-months .flatpickr-next-month:hover svg {
fill: #f64747;
}
.flatpickr-months .flatpickr-prev-month svg,
.flatpickr-months .flatpickr-next-month svg {
width: 14px;
height: 14px;
}
.flatpickr-months .flatpickr-prev-month svg path,
.flatpickr-months .flatpickr-next-month svg path {
-webkit-transition: fill 0.1s;
transition: fill 0.1s;
fill: inherit;
}
.numInputWrapper {
position: relative;
height: auto;
}
.numInputWrapper input,
.numInputWrapper span {
display: inline-block;
}
.numInputWrapper input {
width: 100%;
}
.numInputWrapper input::-ms-clear {
display: none;
}
.numInputWrapper input::-webkit-outer-spin-button,
.numInputWrapper input::-webkit-inner-spin-button {
margin: 0;
-webkit-appearance: none;
}
.numInputWrapper span {
position: absolute;
right: 0;
width: 14px;
padding: 0 4px 0 2px;
height: 50%;
line-height: 50%;
opacity: 0;
cursor: pointer;
border: 1px solid rgba(255,255,255,0.15);
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.numInputWrapper span:hover {
background: rgba(192,187,167,0.1);
}
.numInputWrapper span:active {
background: rgba(192,187,167,0.2);
}
.numInputWrapper span:after {
display: block;
content: "";
position: absolute;
}
.numInputWrapper span.arrowUp {
top: 0;
border-bottom: 0;
}
.numInputWrapper span.arrowUp:after {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-bottom: 4px solid rgba(255,255,255,0.6);
top: 26%;
}
.numInputWrapper span.arrowDown {
top: 50%;
}
.numInputWrapper span.arrowDown:after {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid rgba(255,255,255,0.6);
top: 40%;
}
.numInputWrapper span svg {
width: inherit;
height: auto;
}
.numInputWrapper span svg path {
fill: rgba(255,255,255,0.5);
}
.numInputWrapper:hover {
background: rgba(192,187,167,0.05);
}
.numInputWrapper:hover span {
opacity: 1;
}
.flatpickr-current-month {
font-size: 135%;
line-height: inherit;
font-weight: 300;
color: inherit;
position: absolute;
width: 75%;
left: 12.5%;
padding: 7.48px 0 0 0;
line-height: 1;
height: 34px;
display: inline-block;
text-align: center;
-webkit-transform: translate3d(0px, 0px, 0px);
transform: translate3d(0px, 0px, 0px);
}
.flatpickr-current-month span.cur-month {
font-family: inherit;
font-weight: 700;
color: inherit;
display: inline-block;
margin-left: 0.5ch;
padding: 0;
}
.flatpickr-current-month span.cur-month:hover {
background: rgba(192,187,167,0.05);
}
.flatpickr-current-month .numInputWrapper {
width: 6ch;
width: 7ch\0;
display: inline-block;
}
.flatpickr-current-month .numInputWrapper span.arrowUp:after {
border-bottom-color: #fff;
}
.flatpickr-current-month .numInputWrapper span.arrowDown:after {
border-top-color: #fff;
}
.flatpickr-current-month input.cur-year {
background: transparent;
-webkit-box-sizing: border-box;
box-sizing: border-box;
color: inherit;
cursor: text;
padding: 0 0 0 0.5ch;
margin: 0;
display: inline-block;
font-size: inherit;
font-family: inherit;
font-weight: 300;
line-height: inherit;
height: auto;
border: 0;
border-radius: 0;
vertical-align: initial;
-webkit-appearance: textfield;
-moz-appearance: textfield;
appearance: textfield;
}
.flatpickr-current-month input.cur-year:focus {
outline: 0;
}
.flatpickr-current-month input.cur-year[disabled],
.flatpickr-current-month input.cur-year[disabled]:hover {
font-size: 100%;
color: rgba(255,255,255,0.5);
background: transparent;
pointer-events: none;
}
.flatpickr-current-month .flatpickr-monthDropdown-months {
appearance: menulist;
background: #3f4458;
border: none;
border-radius: 0;
box-sizing: border-box;
color: inherit;
cursor: pointer;
font-size: inherit;
font-family: inherit;
font-weight: 300;
height: auto;
line-height: inherit;
margin: -1px 0 0 0;
outline: none;
padding: 0 0 0 0.5ch;
position: relative;
vertical-align: initial;
-webkit-box-sizing: border-box;
-webkit-appearance: menulist;
-moz-appearance: menulist;
width: auto;
}
.flatpickr-current-month .flatpickr-monthDropdown-months:focus,
.flatpickr-current-month .flatpickr-monthDropdown-months:active {
outline: none;
}
.flatpickr-current-month .flatpickr-monthDropdown-months:hover {
background: rgba(192,187,167,0.05);
}
.flatpickr-current-month .flatpickr-monthDropdown-months .flatpickr-monthDropdown-month {
background-color: #3f4458;
outline: none;
padding: 0;
}
.flatpickr-weekdays {
background: transparent;
text-align: center;
overflow: hidden;
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-webkit-align-items: center;
-ms-flex-align: center;
align-items: center;
height: 28px;
}
.flatpickr-weekdays .flatpickr-weekdaycontainer {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-flex: 1;
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
}
span.flatpickr-weekday {
cursor: default;
font-size: 90%;
background: #3f4458;
color: #fff;
line-height: 1;
margin: 0;
text-align: center;
display: block;
-webkit-box-flex: 1;
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
font-weight: bolder;
}
.dayContainer,
.flatpickr-weeks {
padding: 1px 0 0 0;
}
.flatpickr-days {
position: relative;
overflow: hidden;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-align: start;
-webkit-align-items: flex-start;
-ms-flex-align: start;
align-items: flex-start;
width: 307.875px;
}
.flatpickr-days:focus {
outline: 0;
}
.dayContainer {
padding: 0;
outline: 0;
text-align: left;
width: 307.875px;
min-width: 307.875px;
max-width: 307.875px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
display: inline-block;
display: -ms-flexbox;
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
-ms-flex-wrap: wrap;
-ms-flex-pack: justify;
-webkit-justify-content: space-around;
justify-content: space-around;
-webkit-transform: translate3d(0px, 0px, 0px);
transform: translate3d(0px, 0px, 0px);
opacity: 1;
}
.dayContainer + .dayContainer {
-webkit-box-shadow: -1px 0 0 #20222c;
box-shadow: -1px 0 0 #20222c;
}
.flatpickr-day {
background: none;
border: 1px solid transparent;
border-radius: 150px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
color: rgba(255,255,255,0.95);
cursor: pointer;
font-weight: 400;
width: 14.2857143%;
-webkit-flex-basis: 14.2857143%;
-ms-flex-preferred-size: 14.2857143%;
flex-basis: 14.2857143%;
max-width: 39px;
height: 39px;
line-height: 39px;
margin: 0;
display: inline-block;
position: relative;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
text-align: center;
}
.flatpickr-day.inRange,
.flatpickr-day.prevMonthDay.inRange,
.flatpickr-day.nextMonthDay.inRange,
.flatpickr-day.today.inRange,
.flatpickr-day.prevMonthDay.today.inRange,
.flatpickr-day.nextMonthDay.today.inRange,
.flatpickr-day:hover,
.flatpickr-day.prevMonthDay:hover,
.flatpickr-day.nextMonthDay:hover,
.flatpickr-day:focus,
.flatpickr-day.prevMonthDay:focus,
.flatpickr-day.nextMonthDay:focus {
cursor: pointer;
outline: 0;
background: #646c8c;
border-color: #646c8c;
}
.flatpickr-day.today {
border-color: #eee;
}
.flatpickr-day.today:hover,
.flatpickr-day.today:focus {
border-color: #eee;
background: #eee;
color: #3f4458;
}
.flatpickr-day.selected,
.flatpickr-day.startRange,
.flatpickr-day.endRange,
.flatpickr-day.selected.inRange,
.flatpickr-day.startRange.inRange,
.flatpickr-day.endRange.inRange,
.flatpickr-day.selected:focus,
.flatpickr-day.startRange:focus,
.flatpickr-day.endRange:focus,
.flatpickr-day.selected:hover,
.flatpickr-day.startRange:hover,
.flatpickr-day.endRange:hover,
.flatpickr-day.selected.prevMonthDay,
.flatpickr-day.startRange.prevMonthDay,
.flatpickr-day.endRange.prevMonthDay,
.flatpickr-day.selected.nextMonthDay,
.flatpickr-day.startRange.nextMonthDay,
.flatpickr-day.endRange.nextMonthDay {
background: #80cbc4;
-webkit-box-shadow: none;
box-shadow: none;
color: #fff;
border-color: #80cbc4;
}
.flatpickr-day.selected.startRange,
.flatpickr-day.startRange.startRange,
.flatpickr-day.endRange.startRange {
border-radius: 50px 0 0 50px;
}
.flatpickr-day.selected.endRange,
.flatpickr-day.startRange.endRange,
.flatpickr-day.endRange.endRange {
border-radius: 0 50px 50px 0;
}
.flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n+1)),
.flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n+1)),
.flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n+1)) {
-webkit-box-shadow: -10px 0 0 #80cbc4;
box-shadow: -10px 0 0 #80cbc4;
}
.flatpickr-day.selected.startRange.endRange,
.flatpickr-day.startRange.startRange.endRange,
.flatpickr-day.endRange.startRange.endRange {
border-radius: 50px;
}
.flatpickr-day.inRange {
border-radius: 0;
-webkit-box-shadow: -5px 0 0 #646c8c, 5px 0 0 #646c8c;
box-shadow: -5px 0 0 #646c8c, 5px 0 0 #646c8c;
}
.flatpickr-day.flatpickr-disabled,
.flatpickr-day.flatpickr-disabled:hover,
.flatpickr-day.prevMonthDay,
.flatpickr-day.nextMonthDay,
.flatpickr-day.notAllowed,
.flatpickr-day.notAllowed.prevMonthDay,
.flatpickr-day.notAllowed.nextMonthDay {
color: rgba(255,255,255,0.3);
background: transparent;
border-color: transparent;
cursor: default;
}
.flatpickr-day.flatpickr-disabled,
.flatpickr-day.flatpickr-disabled:hover {
cursor: not-allowed;
color: rgba(255,255,255,0.1);
}
.flatpickr-day.week.selected {
border-radius: 0;
-webkit-box-shadow: -5px 0 0 #80cbc4, 5px 0 0 #80cbc4;
box-shadow: -5px 0 0 #80cbc4, 5px 0 0 #80cbc4;
}
.flatpickr-day.hidden {
visibility: hidden;
}
.rangeMode .flatpickr-day {
margin-top: 1px;
}
.flatpickr-weekwrapper {
float: left;
}
.flatpickr-weekwrapper .flatpickr-weeks {
padding: 0 12px;
-webkit-box-shadow: 1px 0 0 #20222c;
box-shadow: 1px 0 0 #20222c;
}
.flatpickr-weekwrapper .flatpickr-weekday {
float: none;
width: 100%;
line-height: 28px;
}
.flatpickr-weekwrapper span.flatpickr-day,
.flatpickr-weekwrapper span.flatpickr-day:hover {
display: block;
width: 100%;
max-width: none;
color: rgba(255,255,255,0.3);
background: transparent;
cursor: default;
border: none;
}
.flatpickr-innerContainer {
display: block;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-sizing: border-box;
box-sizing: border-box;
overflow: hidden;
}
.flatpickr-rContainer {
display: inline-block;
padding: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.flatpickr-time {
text-align: center;
outline: 0;
display: block;
height: 0;
line-height: 40px;
max-height: 40px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
overflow: hidden;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
.flatpickr-time:after {
content: "";
display: table;
clear: both;
}
.flatpickr-time .numInputWrapper {
-webkit-box-flex: 1;
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
width: 40%;
height: 40px;
float: left;
}
.flatpickr-time .numInputWrapper span.arrowUp:after {
border-bottom-color: rgba(255,255,255,0.95);
}
.flatpickr-time .numInputWrapper span.arrowDown:after {
border-top-color: rgba(255,255,255,0.95);
}
.flatpickr-time.hasSeconds .numInputWrapper {
width: 26%;
}
.flatpickr-time.time24hr .numInputWrapper {
width: 49%;
}
.flatpickr-time input {
background: transparent;
-webkit-box-shadow: none;
box-shadow: none;
border: 0;
border-radius: 0;
text-align: center;
margin: 0;
padding: 0;
height: inherit;
line-height: inherit;
color: rgba(255,255,255,0.95);
font-size: 14px;
position: relative;
-webkit-box-sizing: border-box;
box-sizing: border-box;
-webkit-appearance: textfield;
-moz-appearance: textfield;
appearance: textfield;
}
.flatpickr-time input.flatpickr-hour {
font-weight: bold;
}
.flatpickr-time input.flatpickr-minute,
.flatpickr-time input.flatpickr-second {
font-weight: 400;
}
.flatpickr-time input:focus {
outline: 0;
border: 0;
}
.flatpickr-time .flatpickr-time-separator,
.flatpickr-time .flatpickr-am-pm {
height: inherit;
float: left;
line-height: inherit;
color: rgba(255,255,255,0.95);
font-weight: bold;
width: 2%;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-align-self: center;
-ms-flex-item-align: center;
align-self: center;
}
.flatpickr-time .flatpickr-am-pm {
outline: 0;
width: 18%;
cursor: pointer;
text-align: center;
font-weight: 400;
}
.flatpickr-time input:hover,
.flatpickr-time .flatpickr-am-pm:hover,
.flatpickr-time input:focus,
.flatpickr-time .flatpickr-am-pm:focus {
background: #6a7395;
}
.flatpickr-input[readonly] {
cursor: pointer;
}
@-webkit-keyframes fpFadeInDown {
from {
opacity: 0;
-webkit-transform: translate3d(0, -20px, 0);
transform: translate3d(0, -20px, 0);
}
to {
opacity: 1;
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
}
@keyframes fpFadeInDown {
from {
opacity: 0;
-webkit-transform: translate3d(0, -20px, 0);
transform: translate3d(0, -20px, 0);
}
to {
opacity: 1;
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,7 @@
.click-row {
cursor: pointer;
}
form {
margin-left: 7rem;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 B

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,78 @@
(function() {
flatpickr('[type="date"]', {
altInput: true,
altFormat: "F j, Y at H:i",
dateFormat: "U",
enableTime: true,
});
})();
function ajax(method, url, callback) {
let xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState === XMLHttpRequest.DONE) { // XMLHttpRequest.DONE == 4
callback(xmlhttp.status, xmlhttp.responseText);
}
};
xmlhttp.open(method, url, true);
xmlhttp.send();
}
function formURI(formData) {
let pairs = [];
for (let data of formData.entries()) {
pairs.push(encodeURIComponent(data[0]) + '=' + encodeURIComponent(data[1]))
}
return pairs.join("&").replace(/%20/g, '+');
}
function elementHTML(id, html) {
document.getElementById(id).innerHTML = html
}
function downloadCSV(csv, filename) {
let csvFile;
let downloadLink;
// CSV file
csvFile = new Blob([csv], {type: "text/csv"});
// Download link
downloadLink = document.createElement("a");
// File name
downloadLink.download = filename;
// Create a link to the file
downloadLink.href = window.URL.createObjectURL(csvFile);
// Hide download link
downloadLink.style.display = "none";
// Add the link to DOM
document.body.appendChild(downloadLink);
// Click download link
downloadLink.click();
}
function exportTableToCSV(filename, elementId) {
let csv = [];
let selector = "table tr";
if (elementId !== "") {
selector = "#" + elementId + " tr"
}
let rows = document.querySelectorAll(selector);
for (let i = 0; i < rows.length; i++) {
let row = [], cols = rows[i].querySelectorAll("td, th");
for (let j = 0; j < cols.length; j++)
row.push('"' + cols[j].innerText + '"');
csv.push(row.join(","));
}
// Download CSV file
downloadCSV(csv.join("\n"), filename);
}

View File

@ -0,0 +1,84 @@
{% extends "coreprotect/base.html" %}
{% load static %}
{% block head %}
<link rel="shortcut icon" type="image/png" href="{% static 'coreprotect/img/ymca.png' %}"/>
{% endblock %}
{% block body %}
<div class="block"></div>
<div class="has-text-centered">
<h1 class="title">CoreProtect Activity Monitor</h1>
<hr/>
</div>
<div class="columns">
<form id="coreprotect_form" name="coreprotect_form" class="has-text-left column is-one-fifth">
<h4 class="title is-5 has-text-primary">
Players
</h4>
<div class="field tooltip" data-tooltip="Accepts full or partial names. Separated by commas.">
<input class="input" id="players" name="players" type="text" placeholder="Player Names" value="{{form.players}}">
</div>
<br/>
<h4 class="title is-5 has-text-primary">
Date and Time
</h4>
<div class="field tooltip" data-tooltip="Click to open date/time picker.">
<p class="control">
<input type="date" id="date_from" name="date_from" placeholder="Date From" value="{{form.date_from}}">
</p>
</div>
<div class="field tooltip" data-tooltip="Click to open date/time picker.">
<p class="control">
<input type="date" id="date_to" name="date_to" placeholder="Date To" value="{{form.date_to}}">
</p>
</div>
<br/>
<div class="field is-grouped">
<p class="control">
<button class="button is-primary" id="search" type="button">Search</button>
</p>
<p class="control">
<button class="button is-dark" id="clear" type="button">Clear</button>
</p>
<p class="control">
<button class="button is-info" id="export" type="button">Export</button>
</p>
</div>
</form>
<div class="column is-three-fifths" id="results">
</div>
</div>
{% endblock %}
{% block script %}
<script>
document.getElementById("search").addEventListener("click", function() {
elementHTML("results", 'Querying Database...<br/><progress class="progress is-primary"></progress>');
let form = document.getElementById("coreprotect_form");
let formData = new FormData(form);
ajax("GET", "{% url "coreprotect_activity_query" %}?" + formURI(formData), function(status, data) {
elementHTML("results", data);
});
window.history.pushState("", "", "{% url "coreprotect_activity" %}?" + formURI(formData));
});
document.getElementById("clear").addEventListener("click", function() {
// Players
document.getElementById("players").value = "";
// Date and Time
document.getElementById("date_from")._flatpickr.setDate();
document.getElementById("date_to")._flatpickr.setDate();
});
document.getElementById("export").addEventListener("click", function() {
exportTableToCSV("activity.csv", "results");
});
</script>
{% endblock %}

View File

@ -0,0 +1,23 @@
{% load static %}
<!doctype html>
<html lang="en">
<head>
<title>CoreProtect Web Interface</title>
{% block head %}{% endblock %}
<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Ubuntu"/>
<link rel="stylesheet" type="text/css" href="{% static 'coreprotect/css/bulma.min.css' %}"/>
<link rel="stylesheet" type="text/css" href="{% static 'coreprotect/css/bulma.dark.css' %}"/>
<link rel="stylesheet" type="text/css" href="{% static 'coreprotect/css/bulma-extensions.min.css' %}"/>
<link rel="stylesheet" type="text/css" href="{% static 'coreprotect/css/flatpickr.min.css' %}"/>
<link rel="stylesheet" type="text/css" href="{% static 'coreprotect/css/flatpickr.dark.css' %}"/>
<link rel="stylesheet" type="text/css" href="{% static 'coreprotect/css/main.css' %}"/>
</head>
<body>
{% block body %}{% endblock %}
</body>
<script src="{% static 'coreprotect/js/bulma-extensions.min.js' %}"></script>
<script src="{% static 'coreprotect/js/flatpickr.js' %}"></script>
<script src="{% static 'coreprotect/js/main.js' %}"></script>
{% block script %}{% endblock %}
</html>

View File

@ -0,0 +1,310 @@
{% extends "coreprotect/base.html" %}
{% load static %}
{% block head %}
<link rel="shortcut icon" type="image/png" href="{% static 'coreprotect/img/intelli.png' %}"/>
{% endblock %}
{% block body %}
<div class="block"></div>
<div class="has-text-centered">
<h1 class="title">CoreProtect GUI</h1>
<hr/>
</div>
<div class="columns">
<form id="coreprotect_form" name="coreprotect_form" class="has-text-left column is-one-third">
<div class="columns">
<div class="column is-one-half">
<h4 class="title is-5 has-text-primary">
Actions
</h4>
<div class="field">
<input class="is-checkradio is-block is-info" id="block_break" name="block_break" type="checkbox" {% if form.block_break %}checked{% endif %}>
<label for="block_break">Block Break</label>
</div>
<div class="field">
<input class="is-checkradio is-block is-info" id="block_place" name="block_place" type="checkbox" {% if form.block_place %}checked{% endif %}>
<label for="block_place">Block Place</label>
</div>
<div class="field">
<input class="is-checkradio is-block is-info" id="chat" name="chat" type="checkbox" {% if form.chat %}checked{% endif %} {% if not perms.django_coreprotect.gui_extra %}disabled{% endif %}>
<label for="chat">Chat</label>
</div>
<div class="field">
<input class="is-checkradio is-block is-info" id="chest_use" name="chest_use" type="checkbox" {% if form.chest_use %}checked{% endif %}>
<label for="chest_use">Chest Use</label>
</div>
<div class="field">
<input class="is-checkradio is-block is-info" id="command" name="command" type="checkbox" {% if form.command %}checked{% endif %} {% if not perms.django_coreprotect.gui_extra %}disabled{% endif %}>
<label for="command">Command</label>
</div>
<div class="field">
<input class="is-checkradio is-block is-info" id="interact" name="interact" type="checkbox" {% if form.interact %}checked{% endif %}>
<label for="interact">Interact</label>
</div>
<div class="field">
<input class="is-checkradio is-block is-info" id="login_logout" name="login_logout" type="checkbox" {% if form.login_logout %}checked{% endif %}>
<label for="login_logout">Login/Logout</label>
</div>
<div class="field">
<input class="is-checkradio is-block is-info" id="sign_place" name="sign_place" type="checkbox" {% if form.sign_place %}checked{% endif %}>
<label for="sign_place">Sign Place</label>
</div>
<br/>
<h4 class="title is-5 has-text-primary">
Worlds
</h4>
{% for world in form.worlds %}
<div class="field">
<input class="is-checkradio is-block is-info" id="{{world.world_id}}" name="world" value="{{world.id}}" type="checkbox" {% if world.checked %}checked{% endif %}>
<label for="{{world.world_id}}">{{world.name}}</label>
</div>
{% endfor %}
<br/>
<h4 class="title is-5 has-text-primary">
Options
</h4>
<div class="field">
<input class="is-checkradio is-block is-info" id="ignore_environment" name="ignore_environment" type="checkbox" {% if form.ignore_environment %}checked{% endif %}>
<label for="ignore_environment">Ignore Environment</label>
</div>
<div class="field">
<input class="is-checkradio is-block is-info" id="oldest_first" name="oldest_first" type="checkbox" {% if form.oldest_first %}checked{% endif %}>
<label for="oldest_first">Oldest Results First</label>
</div>
{% if presets %}
<br/>
<h4 class="title is-5 has-text-primary">
Presets
</h4>
<div class="field">
<p class="control">
<span class="select">
<select id="preset">
<option></option>
{% for preset in presets %}
<option value="{{ preset.link }}">{{ preset.name }}</option>
{% endfor %}
</select>
</span>
</p>
</div>
{% endif %}
</div>
<div class="column is-one-half">
<h4 class="title is-5 has-text-primary">
Players
</h4>
<div class="field tooltip" data-tooltip="Accepts full or partial names. Separated by commas.">
<input class="input" id="players" name="players" type="text" placeholder="Player Names" value="{{form.players}}">
</div>
<br/>
<h4 class="title is-5 has-text-primary">
Coordinates
</h4>
<div class="field has-addons tooltip" data-tooltip="Whole numbers only.">
<p class="control">
<input class="input" id="x" name="x" type="text" placeholder="X" value="{{form.x}}">
</p>
<p class="control">
<input class="input" id="y" name="y" type="text" placeholder="Y" value="{{form.y}}">
</p>
<p class="control">
<input class="input" id="z" name="z" type="text" placeholder="Z" value="{{form.z}}">
</p>
</div>
<div class="field tooltip" data-tooltip="Whole numbers only.">
<p class="control">
<input class="input" id="radius" name="radius" type="text" placeholder="Radius" value="{{form.radius}}">
</p>
</div>
<br/>
<h4 class="title is-5 has-text-primary">
Blocks
</h4>
<div class="field tooltip is-tooltip-multiline" data-tooltip="Accepts full or partial names. Separated by commas.">
<input class="input is-4" id="blocks" name="blocks" type="text" placeholder="Block Names" value="{{form.blocks}}">
</div>
<br/>
<h4 class="title is-5 has-text-primary">
Date and Time
</h4>
<div class="field tooltip" data-tooltip="Click to open date/time picker.">
<p class="control">
<input type="date" id="date_from" name="date_from" placeholder="Date From" value="{{form.date_from}}">
</p>
</div>
<div class="field tooltip" data-tooltip="Click to open date/time picker.">
<p class="control">
<input type="date" id="date_to" name="date_to" placeholder="Date To" value="{{form.date_to}}">
</p>
</div>
<br/>
<h4 class="title is-5 has-text-primary">
Page Size
</h4>
<div class="field">
<p class="control">
<span class="select">
<select id="page_size" name="page_size">
<option value="20" {% if form.page_size == "20" %}selected{% endif %}>20</option>
<option value="50" {% if form.page_size == "50" %}selected{% endif %}>50</option>
<option value="100" {% if form.page_size == "100" %}selected{% endif %}>100</option>
<option value="200" {% if form.page_size == "200" %}selected{% endif %}>200</option>
<option value="500" {% if form.page_size == "500" %}selected{% endif %}>500</option>
<option value="1000" {% if form.page_size == "1000" %}selected{% endif %}>1000</option>
</select>
</span>
</p>
</div>
<input type="hidden" id="page" name="page" value="{{form.page}}"/>
<br/>
<div class="field is-grouped">
<p class="control">
<button class="button is-primary" id="search" type="button">Search</button>
</p>
<p class="control">
<button class="button is-dark" id="clear" type="button">Clear</button>
</p>
<p class="control">
<button class="button is-info" id="export" type="button">Export</button>
</p>
</div>
</div>
</div>
</form>
<div class="column is-three-fifths" id="results">
</div>
</div>
{% endblock %}
{% block script %}
<script>
let text = "Copy the below command to teleport";
function attachClickRows() {
Array.from(document.querySelectorAll(".click-row")).forEach(elem => {
elem.addEventListener("click", function() {
prompt(text, elem.getAttribute("data-prompt"));
});
});
}
let url = new URL(window.location);
let pageParam = url.searchParams.get("page");
let page = 0;
if (pageParam !== "") {
page = parseInt(pageParam);
if (isNaN(page)) {
page = 0;
}
}
function attachPagination() {
let prev = document.getElementById("prev");
if (prev.getAttribute("disabled") == null) {
prev.addEventListener("click", function () {
document.getElementById("page").value = (--page).toString();
search(false);
});
}
let next = document.getElementById("next");
if (next.getAttribute("disabled") == null) {
next.addEventListener("click", function () {
document.getElementById("page").value = (++page).toString();
search(false);
});
}
}
function search(resetPage) {
elementHTML("results", 'Querying Database...<br/><progress class="progress is-primary"></progress>');
if (resetPage) {
page = 0;
document.getElementById("page").value = "0";
}
let form = document.getElementById("coreprotect_form");
let formData = new FormData(form);
ajax("GET", "{% url "coreprotect_gui_query" %}?" + formURI(formData), function(status, data) {
elementHTML("results", data);
attachClickRows();
attachPagination();
});
window.history.pushState("", "", "{% url "coreprotect_gui" %}?" + formURI(formData));
}
document.getElementById("search").addEventListener("click", function() {
search(true);
});
document.getElementById("clear").addEventListener("click", function() {
// Actions
document.getElementById("block_break").checked = false;
document.getElementById("block_place").checked = false;
document.getElementById("chat").checked = false;
document.getElementById("chest_use").checked = false;
document.getElementById("command").checked = false;
document.getElementById("interact").checked = false;
document.getElementById("login_logout").checked = false;
document.getElementById("sign_place").checked = false;
// Worlds
{% for world in form.worlds %}
document.getElementById("{{world.world_id}}").checked = false;
{% endfor %}
// Options
document.getElementById("ignore_environment").checked = false;
document.getElementById("oldest_first").checked = false;
// Page Size
document.getElementById("page_size").selectedIndex = "0";
document.getElementById("page").value = "1";
// Players
document.getElementById("players").value = "";
// Coordinates
document.getElementById("x").value = "";
document.getElementById("y").value = "";
document.getElementById("z").value = "";
document.getElementById("radius").value = "";
// Blocks
document.getElementById("blocks").value = "";
// Date and Time
document.getElementById("date_from")._flatpickr.setDate();
document.getElementById("date_to")._flatpickr.setDate();
});
document.getElementById("export").addEventListener("click", function() {
exportTableToCSV("coreprotect.csv", "results");
});
{% if presets %}
document.getElementById("preset").addEventListener("change", function() {
if (this.value !== "") {
location.href = this.value;
}
});
{% endif %}
</script>
{% endblock %}

View File

@ -0,0 +1,16 @@
<table class="table is-fullwidth is-striped is-bordered">
<thead>
<tr>
<th>Player</th>
<th>Time</th>
</tr>
</thead>
<tbody>
{% for result in results %}
<tr>
<td>{{ result.player }}</td>
<td>{{ result.time_display }}</td>
</tr>
{% endfor %}
</tbody>
</table>

View File

@ -0,0 +1,33 @@
<table class="table is-fullwidth is-striped is-bordered is-hoverable">
<thead>
<tr>
<th>Time</th>
<th>Player</th>
<th>Action</th>
<th>Data</th>
<th>X</th>
<th>Y</th>
<th>Z</th>
<th>World</th>
</tr>
</thead>
<tbody>
{% for result in results %}
<tr {% if result.world %}class="click-row" data-prompt="/tp {{ result.x }} {{ result.y }} {{ result.z }}"{% endif %}>
<td>{{ result.display_time }}</td>
<td>{{ result.player }}</td>
<td>{{ result.display_action }}</td>
<td>{{ result.display_data }}</td>
<td>{{ result.x }}</td>
<td>{{ result.y }}</td>
<td>{{ result.z }}</td>
<td>{{ result.world }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<nav class="pagination is-rounded is-right" role="navigation">
<a class="pagination-previous" id="prev" {% if not prev %}disabled{% endif %}>Previous</a>
<a class="pagination-next" id="next" {% if not next %}disabled{% endif %}>Next</a>
<ul class="pagination-list"></ul>
</nav>

3
tests.py 100644
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

10
urls.py 100644
View File

@ -0,0 +1,10 @@
from django.urls import path
from django.contrib.auth.decorators import permission_required
from django_coreprotect.views import GUI, GUIQuery, Activity, ActivityQuery
urlpatterns = [
path('', permission_required('django_coreprotect.gui')(GUI.as_view()), name="coreprotect_gui"),
path('query/', permission_required('django_coreprotect.gui')(GUIQuery.as_view()), name="coreprotect_gui_query"),
path('activity/', permission_required('django_coreprotect.activity')(Activity.as_view()), name="coreprotect_activity"),
path('activity/query/', permission_required('django_coreprotect.activity')(ActivityQuery.as_view()), name="coreprotect_activity_query"),
]

36
utils.py 100644
View File

@ -0,0 +1,36 @@
import ast
from datetime import datetime, timedelta
def checkbox(value):
return True if value == "on" else False
def safe_int(value):
try:
return int(value)
except:
return 0
def time_to_form(date_time):
if date_time == "~T":
return datetime.today()
elif date_time == "~N":
return datetime.now()
elif date_time == "~Y":
return datetime.today() - timedelta(days=1)
elif date_time == "":
return ""
else:
return datetime.utcfromtimestamp(float(date_time))
def form_to_time(date_time):
dt = ast.literal_eval(date_time)
if dt[0] in ["~T", "~N", "~Y"]:
return dt[0]
else:
dt[0] = dt[0] if dt[0] else datetime.now().strftime("%Y-%m-%d")
dt[1] = dt[1] if dt[1] else "00:00:00"
return datetime.strptime(" ".join(dt), "%Y-%m-%d %H:%M:%S").timestamp()

65
views.py 100644
View File

@ -0,0 +1,65 @@
from django.http.response import JsonResponse
from django.shortcuts import render
from django.views.generic import View
from django_coreprotect.activity import activity_data, activity_results
from django_coreprotect.gui import gui_data, gui_results
from django_coreprotect.models import Preset
from django_coreprotect.utils import safe_int
class GUI(View):
def get(self, request):
form = gui_data(request)
return render(request, "coreprotect/coreprotect.html", {"form": form, "presets": Preset.objects.filter(enabled=True)})
def post(self, request):
pass
class GUIQuery(View):
def get(self, request):
form = gui_data(request)
results = gui_results(form)
if "format" in request.GET and request.GET["format"] == "json":
return JsonResponse(results)
prev_page, next_page = False, False
if safe_int(form.page) > 0:
prev_page = True
if len(results) == safe_int(form.page_size):
next_page = True
return render(request, "coreprotect/table/coreprotect.html", {"results": results, "num": len(results), "prev": prev_page, "next": next_page})
def post(self, request):
pass
class Activity(View):
def get(self, request):
form = activity_data(request)
return render(request, "coreprotect/activity.html", {"form": form})
def post(self, request):
pass
class ActivityQuery(View):
def get(self, request):
form = activity_data(request)
results = activity_results(form)
if "format" in request.GET and request.GET["format"] == "json":
return JsonResponse(results)
return render(request, "coreprotect/table/activity.html", {"results": results})
def post(self, request):
pass