aboutsummaryrefslogtreecommitdiffstats
path: root/pydis_site/apps/staff
diff options
context:
space:
mode:
Diffstat (limited to 'pydis_site/apps/staff')
-rw-r--r--pydis_site/apps/staff/__init__.py0
-rw-r--r--pydis_site/apps/staff/apps.py7
-rw-r--r--pydis_site/apps/staff/migrations/__init__.py0
-rw-r--r--pydis_site/apps/staff/models/__init__.py0
-rw-r--r--pydis_site/apps/staff/templatetags/__init__.py3
-rw-r--r--pydis_site/apps/staff/templatetags/deletedmessage_filters.py17
-rw-r--r--pydis_site/apps/staff/tests/__init__.py0
-rw-r--r--pydis_site/apps/staff/tests/test_deletedmessage_filters.py33
-rw-r--r--pydis_site/apps/staff/tests/test_logs_view.py160
-rw-r--r--pydis_site/apps/staff/urls.py10
-rw-r--r--pydis_site/apps/staff/viewsets/__init__.py3
-rw-r--r--pydis_site/apps/staff/viewsets/logs.py11
12 files changed, 244 insertions, 0 deletions
diff --git a/pydis_site/apps/staff/__init__.py b/pydis_site/apps/staff/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pydis_site/apps/staff/__init__.py
diff --git a/pydis_site/apps/staff/apps.py b/pydis_site/apps/staff/apps.py
new file mode 100644
index 00000000..70a15f40
--- /dev/null
+++ b/pydis_site/apps/staff/apps.py
@@ -0,0 +1,7 @@
+from django.apps import AppConfig
+
+
+class StaffConfig(AppConfig):
+ """Django AppConfig for the staff app."""
+
+ name = 'staff'
diff --git a/pydis_site/apps/staff/migrations/__init__.py b/pydis_site/apps/staff/migrations/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pydis_site/apps/staff/migrations/__init__.py
diff --git a/pydis_site/apps/staff/models/__init__.py b/pydis_site/apps/staff/models/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pydis_site/apps/staff/models/__init__.py
diff --git a/pydis_site/apps/staff/templatetags/__init__.py b/pydis_site/apps/staff/templatetags/__init__.py
new file mode 100644
index 00000000..e8b6983a
--- /dev/null
+++ b/pydis_site/apps/staff/templatetags/__init__.py
@@ -0,0 +1,3 @@
+from .deletedmessage_filters import footer_datetime, hex_colour
+
+__all__ = ["hex_colour", "footer_datetime"]
diff --git a/pydis_site/apps/staff/templatetags/deletedmessage_filters.py b/pydis_site/apps/staff/templatetags/deletedmessage_filters.py
new file mode 100644
index 00000000..f950870f
--- /dev/null
+++ b/pydis_site/apps/staff/templatetags/deletedmessage_filters.py
@@ -0,0 +1,17 @@
+from datetime import datetime
+
+from django import template
+
+register = template.Library()
+
+
+def hex_colour(color: int) -> str:
+ """Converts an integer representation of a colour to the RGB hex value."""
+ return f"#{color:0>6X}"
+
+
+def footer_datetime(timestamp: str) -> datetime:
+ """Takes an embed timestamp and returns a timezone-aware datetime object."""
+ return datetime.fromisoformat(timestamp)
diff --git a/pydis_site/apps/staff/tests/__init__.py b/pydis_site/apps/staff/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pydis_site/apps/staff/tests/__init__.py
diff --git a/pydis_site/apps/staff/tests/test_deletedmessage_filters.py b/pydis_site/apps/staff/tests/test_deletedmessage_filters.py
new file mode 100644
index 00000000..d9179044
--- /dev/null
+++ b/pydis_site/apps/staff/tests/test_deletedmessage_filters.py
@@ -0,0 +1,33 @@
+import enum
+
+from django.test import TestCase
+from django.utils import timezone
+
+from ..templatetags import deletedmessage_filters
+
+
+class Colour(enum.IntEnum):
+ """Enumeration of integer colour values for readability."""
+
+ BLACK = 0
+ BLUE = 255
+ GREEN = 65280
+ RED = 16711680
+ WHITE = 16777215
+
+
+class DeletedMessageFilterTests(TestCase):
+ def test_hex_colour_filter(self):
+ self.assertEqual(deletedmessage_filters.hex_colour(Colour.BLACK), "#000000")
+ self.assertEqual(deletedmessage_filters.hex_colour(Colour.BLUE), "#0000FF")
+ self.assertEqual(deletedmessage_filters.hex_colour(Colour.GREEN), "#00FF00")
+ self.assertEqual(deletedmessage_filters.hex_colour(Colour.RED), "#FF0000")
+ self.assertEqual(deletedmessage_filters.hex_colour(Colour.WHITE), "#FFFFFF")
+
+ def test_footer_datetime_filter(self):
+ datetime_aware = timezone.now()
+ iso_string = datetime_aware.isoformat()
+
+ datetime_returned = deletedmessage_filters.footer_datetime(iso_string)
+ self.assertTrue(timezone.is_aware(datetime_returned))
+ self.assertEqual(datetime_aware, datetime_returned)
diff --git a/pydis_site/apps/staff/tests/test_logs_view.py b/pydis_site/apps/staff/tests/test_logs_view.py
new file mode 100644
index 00000000..32cb6bbf
--- /dev/null
+++ b/pydis_site/apps/staff/tests/test_logs_view.py
@@ -0,0 +1,160 @@
+from django.test import Client, TestCase
+from django.utils import timezone
+from django_hosts.resolvers import reverse, reverse_host
+
+from pydis_site.apps.api.models.bot import DeletedMessage, MessageDeletionContext, Role, User
+from pydis_site.apps.staff.templatetags.deletedmessage_filters import hex_colour
+
+
+class TestLogsView(TestCase):
+ @classmethod
+ def setUpTestData(cls):
+ cls.developers_role = Role.objects.create(
+ id=12345678,
+ name="Developers",
+ colour=16777215,
+ permissions=104324673,
+ position=1,
+ )
+
+ cls.author = cls.actor = User.objects.create(
+ id=12345678901,
+ name='Alan Turing',
+ discriminator=1912,
+ avatar_hash=None
+ )
+
+ cls.author.roles.add(cls.developers_role)
+
+ cls.deletion_context = MessageDeletionContext.objects.create(
+ actor=cls.actor,
+ creation=timezone.now()
+ )
+
+ cls.deleted_message_one = DeletedMessage.objects.create(
+ author=cls.author,
+ id=614125807161573397,
+ channel_id=1984,
+ content='<em>I think my tape has run out...</em>',
+ embeds=[],
+ deletion_context=cls.deletion_context,
+ )
+
+ cls.embed_one = {
+ "footer": {
+ "text": "This will be displayed in the footer!",
+ "icon_url": "https://avatars0.githubusercontent.com/u/33516116?s=460&v=4"
+ },
+ "image": {
+ "url": "https://avatars0.githubusercontent.com/u/33516116?s=460&v=4"
+ },
+ "thumbnail": {
+ "url": "https://avatars0.githubusercontent.com/u/33516116?s=460&v=4"
+ },
+ "author": {
+ "name": "Ves Zappa",
+ "url": "https://pydis.com",
+ "icon_url": "https://avatars0.githubusercontent.com/u/33516116?s=460&v=4"
+ },
+ "fields": [
+ {
+ "inline": False,
+ "name": "Field Name 1",
+ "value": "Field Value 1"
+ },
+ {
+ "inline": False,
+ "name": "Field Name 2",
+ "value": "Field Value 2"
+ },
+ {
+ "inline": True,
+ "name": "Field Name 3",
+ "value": "Field Value 3"
+ },
+ {
+ "inline": True,
+ "name": "Field Name 4",
+ "value": "Field Value 4"
+ },
+ {
+ "inline": True,
+ "name": "Field Name 5",
+ "value": "Field Value 5"
+ }
+ ],
+ "color": 16711680,
+ "timestamp": "2019-08-21T13:58:34.480053+00:00",
+ "type": "rich",
+ "description": "This embed is way too cool to be seen in public channels.",
+ "url": "https://pythondiscord.com/",
+ "title": "Hello, PyDis"
+ }
+
+ cls.embed_two = {
+ "description": "This embed is way too cool to be seen in public channels.",
+ }
+
+ cls.deleted_message_two = DeletedMessage.objects.create(
+ author=cls.author,
+ id=614444836291870750,
+ channel_id=1984,
+ content='Does that mean this thing will halt?',
+ embeds=[cls.embed_one, cls.embed_two],
+ deletion_context=cls.deletion_context,
+ )
+
+ def setUp(self):
+ """Sets up a test client that automatically sets the correct HOST header."""
+ self.client = Client(HTTP_HOST=reverse_host(host="staff"))
+
+ def test_logs_returns_200_for_existing_logs_pk(self):
+ url = reverse('logs', host="staff", args=(self.deletion_context.id,))
+ response = self.client.get(url)
+ self.assertEqual(response.status_code, 200)
+
+ def test_logs_returns_404_for_nonexisting_logs_pk(self):
+ url = reverse('logs', host="staff", args=(self.deletion_context.id + 100,))
+ response = self.client.get(url)
+ self.assertEqual(response.status_code, 404)
+
+ def test_author_color_is_set_in_response(self):
+ url = reverse('logs', host="staff", args=(self.deletion_context.id,))
+ response = self.client.get(url)
+ role_colour = hex_colour(self.developers_role.colour)
+ html_needle = (
+ f'<span class="discord-username" style="color: {role_colour}">{self.author}</span>'
+ )
+ self.assertInHTML(html_needle, response.content.decode())
+
+ def test_correct_messages_have_been_passed_to_template(self):
+ url = reverse('logs', host="staff", args=(self.deletion_context.id,))
+ response = self.client.get(url)
+ self.assertIn("messages", response.context)
+ self.assertListEqual(
+ [self.deleted_message_one, self.deleted_message_two],
+ list(response.context["deletion_context"].deletedmessage_set.all())
+ )
+
+ def test_if_both_embeds_are_included_html_response(self):
+ url = reverse('logs', host="staff", args=(self.deletion_context.id,))
+ response = self.client.get(url)
+
+ html_response = response.content.decode()
+ embed_colour_needle = (
+ '<div class="discord-embed-color" style="background-color: {colour}"></div>'
+ )
+ embed_one_colour = hex_colour(self.embed_one["color"])
+ embed_two_colour = "#cacbce"
+ self.assertInHTML(embed_colour_needle.format(colour=embed_one_colour), html_response)
+ self.assertInHTML(embed_colour_needle.format(colour=embed_two_colour), html_response)
+
+ def test_if_html_in_content_is_properly_escaped(self):
+ url = reverse('logs', host="staff", args=(self.deletion_context.id,))
+ response = self.client.get(url)
+
+ html_response = response.content.decode()
+ unescaped_content = "<em>I think my tape has run out...</em>"
+ self.assertInHTML(unescaped_content, html_response, count=0)
+ escaped_content = "&lt;em&gt;I think my tape has run out...&lt;/em&gt;"
+ self.assertInHTML(escaped_content, html_response, count=1)
diff --git a/pydis_site/apps/staff/urls.py b/pydis_site/apps/staff/urls.py
new file mode 100644
index 00000000..a564d516
--- /dev/null
+++ b/pydis_site/apps/staff/urls.py
@@ -0,0 +1,10 @@
+from django.conf import settings
+from django.conf.urls.static import static
+from django.urls import path
+
+from .viewsets import LogView
+
+app_name = 'staff'
+urlpatterns = [
+ path('bot/logs/<int:pk>/', LogView.as_view(), name="logs"),
+] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
diff --git a/pydis_site/apps/staff/viewsets/__init__.py b/pydis_site/apps/staff/viewsets/__init__.py
new file mode 100644
index 00000000..6b10eb83
--- /dev/null
+++ b/pydis_site/apps/staff/viewsets/__init__.py
@@ -0,0 +1,3 @@
+from .logs import LogView
+
+__all__ = ["LogView"]
diff --git a/pydis_site/apps/staff/viewsets/logs.py b/pydis_site/apps/staff/viewsets/logs.py
new file mode 100644
index 00000000..22dede95
--- /dev/null
+++ b/pydis_site/apps/staff/viewsets/logs.py
@@ -0,0 +1,11 @@
+from django.views.generic.detail import DetailView
+
+from pydis_site.apps.api.models.bot.message_deletion_context import MessageDeletionContext
+
+
+class LogView(DetailView):
+ """The default view for the Deleted Messages logs."""
+
+ model = MessageDeletionContext
+ context_object_name = "deletion_context"
+ template_name = "staff/logs.html"