From 8da376b0ebff72db9f72b7026b6f2fef4dff4f13 Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Thu, 22 Aug 2019 15:44:06 +0200 Subject: Making the deleted-messages-frontend functional with changes, including: - Adding support for embeds to both the template and the css; - Adding Discord fonts to create a Discord-realistic rendering; - Adding Discord color int to html hex filter for use in templates; - Removing unnecessary int -> hex property from role model (see previous point); - Adding support to compute timestamp from snowflake int in the message model; - Forcing the order of deleted messages list view to snowflake `id` to guarantee chronological order. --- pydis_site/apps/api/models/bot/deleted_message.py | 5 + pydis_site/apps/api/models/bot/message.py | 12 ++ pydis_site/apps/staff/apps.py | 2 +- pydis_site/apps/staff/templatetags/__init__.py | 3 + .../staff/templatetags/deletedmessage_filters.py | 17 ++ pydis_site/apps/staff/urls.py | 4 +- pydis_site/apps/staff/viewsets/__init__.py | 2 +- pydis_site/apps/staff/viewsets/logs.py | 20 +- pydis_site/static/css/staff/logs.css | 215 ++++++++++++++++++++- pydis_site/templates/staff/logs.html | 76 +++++++- 10 files changed, 346 insertions(+), 10 deletions(-) create mode 100644 pydis_site/apps/staff/templatetags/__init__.py create mode 100644 pydis_site/apps/staff/templatetags/deletedmessage_filters.py (limited to 'pydis_site') diff --git a/pydis_site/apps/api/models/bot/deleted_message.py b/pydis_site/apps/api/models/bot/deleted_message.py index eb7f4c89..1eb4516e 100644 --- a/pydis_site/apps/api/models/bot/deleted_message.py +++ b/pydis_site/apps/api/models/bot/deleted_message.py @@ -12,3 +12,8 @@ class DeletedMessage(Message): help_text="The deletion context this message is part of.", on_delete=models.CASCADE ) + + class Meta: + """Sets the default ordering for list views to oldest first.""" + + ordering = ["id"] diff --git a/pydis_site/apps/api/models/bot/message.py b/pydis_site/apps/api/models/bot/message.py index 6b566620..0713b9d2 100644 --- a/pydis_site/apps/api/models/bot/message.py +++ b/pydis_site/apps/api/models/bot/message.py @@ -1,6 +1,11 @@ +from datetime import datetime + +import pytz from django.contrib.postgres import fields as pgfields from django.core.validators import MinValueValidator from django.db import models +from django.utils import timezone + from pydis_site.apps.api.models.bot.tag import validate_tag_embed from pydis_site.apps.api.models.bot.user import User @@ -49,6 +54,13 @@ class Message(ModelReprMixin, models.Model): help_text="Embeds attached to this message." ) + @property + def timestamp(self) -> datetime: + """Attribute that represents the message timestamp as derived from the snowflake id.""" + tz_naive_datetime = datetime.utcfromtimestamp(((self.id >> 22) + 1420070400000) / 1000) + tz_aware_datetime = timezone.make_aware(tz_naive_datetime, timezone=pytz.timezone("UTC")) + return tz_aware_datetime + class Meta: """Metadata provided for Django's ORM.""" diff --git a/pydis_site/apps/staff/apps.py b/pydis_site/apps/staff/apps.py index fb8bda03..70a15f40 100644 --- a/pydis_site/apps/staff/apps.py +++ b/pydis_site/apps/staff/apps.py @@ -4,4 +4,4 @@ from django.apps import AppConfig class StaffConfig(AppConfig): """Django AppConfig for the staff app.""" - name = 'staff' \ No newline at end of file + name = 'staff' 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() + + +@register.filter +def hex_colour(color: int) -> str: + """Converts an integer representation of a colour to the RGB hex value.""" + return f"#{color:0>6X}" + + +@register.filter +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/urls.py b/pydis_site/apps/staff/urls.py index 95b3caf3..a564d516 100644 --- a/pydis_site/apps/staff/urls.py +++ b/pydis_site/apps/staff/urls.py @@ -1,12 +1,10 @@ from django.conf import settings from django.conf.urls.static import static -from django.contrib import admin -from django.urls import include, path +from django.urls import path from .viewsets import LogView app_name = 'staff' urlpatterns = [ path('bot/logs//', LogView.as_view(), name="logs"), - path('admin/', admin.site.urls), ] + 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 index ccb57d43..6b10eb83 100644 --- a/pydis_site/apps/staff/viewsets/__init__.py +++ b/pydis_site/apps/staff/viewsets/__init__.py @@ -1,3 +1,3 @@ from .logs import LogView -__all__ = ["LogView"] \ No newline at end of file +__all__ = ["LogView"] diff --git a/pydis_site/apps/staff/viewsets/logs.py b/pydis_site/apps/staff/viewsets/logs.py index d59847a3..0898d606 100644 --- a/pydis_site/apps/staff/viewsets/logs.py +++ b/pydis_site/apps/staff/viewsets/logs.py @@ -1,3 +1,5 @@ +import logging + from django.core.handlers.wsgi import WSGIRequest from django.http import HttpResponse from django.shortcuts import get_object_or_404, render @@ -5,11 +7,27 @@ from django.views import View from pydis_site.apps.api.models.bot.message_deletion_context import MessageDeletionContext +log = logging.getLogger(__name__) + class LogView(View): + """The default view for the Deleted Messages logs.""" + template_name = "staff/logs.html" def get(self, request: WSGIRequest, pk: int) -> HttpResponse: + """Get method that answers a request with an html response by rendering a template.""" message_context = get_object_or_404(MessageDeletionContext, pk=pk) + + actor = message_context.actor + creation = message_context.creation messages = message_context.deletedmessage_set.all() - return render(request, self.template_name, {"message_context": message_context, "messages": messages}) + + template_fields = { + 'actor': actor, + 'actor_colour': message_context.actor.top_role.colour, + 'creation': creation, + 'messages': messages + } + + return render(request, self.template_name, template_fields) diff --git a/pydis_site/static/css/staff/logs.css b/pydis_site/static/css/staff/logs.css index ef271e1e..a09d33ac 100644 --- a/pydis_site/static/css/staff/logs.css +++ b/pydis_site/static/css/staff/logs.css @@ -1,16 +1,31 @@ main.site-content { background-color: hsl(220, 8%, 23%); color: #dcddde; - font-family: sans-serif; font-size: 0.9375rem; font-weight: 400; line-height: 1.3; letter-spacing: 0; text-rendering: optimizeLegibility; + padding: 1rem; + font-family: Whitney,Helvetica Neue,Helvetica,Arial,Lucida Grande,sans-serif; +} + +.has-small-margin { + margin: 1rem 0; +} + +.deleted-header { + font-weight: 700; + margin-top: 1rem; } .discord-message { - margin: 1rem; + margin-bottom: 15px; +} + +.discord-message:first-child { + border-top: 1px; + } .discord-message-header { @@ -20,7 +35,7 @@ main.site-content { .discord-username { font-size: 1rem; - font-weight: 500; + font-weight: 600; } .discord-message-metadata { @@ -40,4 +55,198 @@ main.site-content { color: #dcddde; font-weight: 300; margin-left: 0.3rem; +} + +.discord-embed { + + position: relative; + margin-top: 5px; + max-width: 520px; + display: flex; +} + +.discord-embed a { + text-decoration: none; + color: hsl(197, 100%, 41%); +} + +.discord-embed a:hover { + text-decoration: underline; + color: hsl(197, 100%, 41%); +} + +.discord-embed-color { + width: 4px; + border-radius: 3px 0 0 3px; + flex-shrink: 0; +} + +.discord-embed-inner { + background-color: #34363b; + padding: 8px 10px; + border-radius: 0 3px 3px 0; + box-sizing: border-box; + border: 1px solid hsla(225, 8%, 20%, 0.6); + display: flex; + flex-direction: column; +} + +.discord-embed-content { + width: 100%; + display: flex; +} + +.discord-embed-main { + flex: 1; +} + +.discord-embed-thumbnail > img { + max-width: 80px; + max-height: 80px; + border-radius: 3px; + width: auto; + object-fit: contain; + margin-left: 20px; + flex-shrink: 0; + border-style: none; +} + +.discord-embed-author { + display: flex; + align-items: center; + margin-bottom: 5px; + font-weight: 600; + font-size: 14px; + line-height: 1.15; +} + +.discord-embed-author-icon { + margin-right: 9px; + width: 20px; + height: 20px; + object-fit: contain; + border-radius: 50%; +} + +.discord-embed-author a { + color: white; +} + +.discord-embed-author a:hover { + color: white; +} + +.discord-embed-title { + margin-bottom: 5px; + font-size: 14px; + display: inline-block; + font-weight: 600; +} + +.discord-embed-description { + margin-bottom: 10px; +} + +.discord-embed-fields { + display: flex; + flex-direction: row; + flex-wrap: wrap; + margin-top: -10px; + margin-bottom: 10px; +} + +.discord-embed-field { + flex: 0; + padding-top: 5px; + min-width: 100%; + max-width: 506px; +} + +.discord-embed-field-name { + margin-bottom: 4px; + font-weight: 600; +} + +.discord-embed-field-value { + font-weight: 500; +} + +.discord-embed-field-inline { + flex: 1; + min-width: 150px; + flex-basis: auto; +} + +.discord-embed-main > :last-child { + margin-bottom: 0 !important; +} + +.discord-embed-image { + position: relative; + display: inline-block; + margin-bottom: 10px; +} + +.discord-embed-image > img { + margin: 0; + vertical-align: bottom; + max-width: 300px; + display: flex; + overflow: hidden; + border-radius: 2px; +} + +.discord-embed-footer-text { + font-size: .70rem !important; + letter-spacing: 0; + display: inline-block; +} + +.discord-embed-footer-icon { + margin-right: 10px; + height: 18px; + width: 18px; + object-fit: contain; + float: left; + border-radius: 50%; +} + +.discord-embed-content { + margin-bottom: 10px; +} + +.discord-embed-inner > :last-child { + margin-bottom: 0 !important; +} + +/* Discord Font definitions */ +@font-face { + font-family: Whitney; + font-style: light; + font-weight:300; + src:url(https://discordapp.com/assets/6c6374bad0b0b6d204d8d6dc4a18d820.woff) format("woff") +} +@font-face { + font-family: Whitney; + font-style: normal; + font-weight:500; + src:url(https://discordapp.com/assets/e8acd7d9bf6207f99350ca9f9e23b168.woff) format("woff") +} +@font-face { + font-family:Whitney; + font-style: medium; + font-weight:600; + src:url(https://discordapp.com/assets/3bdef1251a424500c1b3a78dea9b7e57.woff) format("woff") +} +@font-face { + font-family: WhitneyMedium; + font-style: medium; + font-weight: 600; + src:url(https://discordapp.com/assets/be0060dafb7a0e31d2a1ca17c0708636.woff) format("woff") +} +@font-face { + font-family: Whitney; + font-style: bold; + font-weight: 700; + src:url(https://discordapp.com/assets/8e12fb4f14d9c4592eb8ec9f22337b04.woff) format("woff") } \ No newline at end of file diff --git a/pydis_site/templates/staff/logs.html b/pydis_site/templates/staff/logs.html index 66b42f6a..49d9c368 100644 --- a/pydis_site/templates/staff/logs.html +++ b/pydis_site/templates/staff/logs.html @@ -1,5 +1,6 @@ {% extends 'base/base.html' %} {% load static %} +{% load deletedmessage_filters %} {% block title %}Logs for Deleted Message Context {{ message_context.id }}{% endblock %} @@ -8,15 +9,88 @@ {% endblock %} {% block content %} + +
{% for message in messages %}
- {{ message.author.name }}#{{ message.author.discriminator }} + {{ message.author }}
{{ message.content|linebreaks }}
+ + {% for embed in message.embeds %} +
+
+
+
+
+
+ + {% if embed.author %} +
+ {% if embed.author.icon_url %}Author Icon{% endif %} + {% if embed.author.url %}{% endif %} + {{ embed.author.name }} + {% if embed.author.url %}{% endif %} +
+ {% endif %} + + {% if embed.title %} +
+ {% if embed.url %}{% endif %} + {{ embed.title }} + {% if embed.url %}{% endif %} +
+ {% endif %} + + {% if embed.description %} +
+ {{ embed.description | linebreaksbr }} +
+ {% endif %} + + {% if embed.fields %} +
+ {% for field in embed.fields %} +
+
{{ field.name }}
+
{{ field.value }}
+
+ {% endfor %} +
+ {% endif %} + {% if embed.image %} +
+ Discord Embed Image +
+ {% endif %} +
+ {% if embed.thumbnail %} +
+ Embed thumbnail +
+ {% endif %} +
+ {% if embed.footer or embed.timestamp %} + + {% endif %} +
+
+ {% endfor %}
{% endfor %} {% endblock %} \ No newline at end of file -- cgit v1.2.3