diff options
Diffstat (limited to 'pydis_site')
| -rw-r--r-- | pydis_site/apps/api/models/bot/deleted_message.py | 5 | ||||
| -rw-r--r-- | pydis_site/apps/api/models/bot/message.py | 12 | ||||
| -rw-r--r-- | pydis_site/apps/staff/apps.py | 2 | ||||
| -rw-r--r-- | pydis_site/apps/staff/templatetags/__init__.py | 3 | ||||
| -rw-r--r-- | pydis_site/apps/staff/templatetags/deletedmessage_filters.py | 17 | ||||
| -rw-r--r-- | pydis_site/apps/staff/urls.py | 4 | ||||
| -rw-r--r-- | pydis_site/apps/staff/viewsets/__init__.py | 2 | ||||
| -rw-r--r-- | pydis_site/apps/staff/viewsets/logs.py | 20 | ||||
| -rw-r--r-- | pydis_site/static/css/staff/logs.css | 215 | ||||
| -rw-r--r-- | pydis_site/templates/staff/logs.html | 76 | 
10 files changed, 346 insertions, 10 deletions
| 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() + + +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/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/<int:pk>/', 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 %} +    <ul class="is-size-7"> +        <li>Deleted by: <span style="color: {{ actor_colour | hex_colour }}">{{ actor }}</span></li> +        <li>Date: {{ creation }}</li> +    </ul> +    <div class="is-divider has-small-margin"></div>      {%  for message in messages %}          <div class="discord-message">              <div class="discord-message-header"> -                <span class="discord-username" style="color: #{{ message.author.top_role.hex_colour }}">{{ message.author.name }}#{{ message.author.discriminator }}</span><span class="discord-message-metadata">Today at 6:15 PM | #helpers | User ID: 190549806198816768</span> +                <span class="discord-username" style="color: {{ message.author.top_role.colour | hex_colour }}">{{ message.author }}</span><span class="discord-message-metadata">{{ message.timestamp }} | User ID: {{ message.author.id }}</span>              </div>              <div class="discord-message-content">                  {{ message.content|linebreaks }}              </div> + +        {% for embed in message.embeds %} +            <div class="discord-embed is-size-7"> +                <div class="discord-embed-color" style="background-color: {% if embed.color %}{{ embed.color | hex_colour }}{%  else %}#cacbce{% endif %}"> +                </div> +                <div class="discord-embed-inner"> +                    <div class="discord-embed-content"> +                        <div class="discord-embed-main"> + +                            {% if embed.author %} +                                <div class="discord-embed-author"> +                                    {% if embed.author.icon_url %}<img alt="Author Icon" class="discord-embed-author-icon" src="{{ embed.author.icon_url }}">{% endif %} +                                    {% if embed.author.url %}<a class="discord-embed-author-url" href="{{ embed.author.url }}">{% endif %} +                                    <span class="discord-embed-author-name">{{ embed.author.name }}</span> +                                    {% if embed.author.url %}</a>{% endif %} +                                </div> +                            {% endif %} + +                            {% if embed.title %} +                            <div class="discord-embed-title"> +                                {%  if embed.url %}<a href="{{ embed.url }}">{% endif %} +                                {{ embed.title }} +                                {%  if embed.url %}</a>{% endif %} +                            </div> +                            {%  endif %} + +                            {% if embed.description %} +                            <div class="discord-embed-description"> +                                {{ embed.description | linebreaksbr }} +                            </div> +                            {% endif %} + +                            {% if embed.fields %} +                                <div class="discord-embed-fields"> +                                    {% for field in embed.fields %} +                                    <div class="discord-embed-field{% if field.inline %} discord-embed-field-inline{% endif %}"> +                                        <div class="discord-embed-field-name">{{ field.name }}</div> +                                        <div class="discord-embed-field-value">{{ field.value }}</div> +                                    </div> +                                    {% endfor %} +                                </div> +                            {% endif %} +                            {% if embed.image %} +                            <div class="discord-embed-image"> +                                <img alt="Discord Embed Image" src="{{ embed.image.url }}"> +                            </div> +                            {% endif %} +                        </div> +                    {% if embed.thumbnail %} +                        <div class="discord-embed-thumbnail"> +                            <img alt="Embed thumbnail" src="{{ embed.thumbnail.url }}"> +                        </div> +                    {% endif %} +                    </div> +                {%  if embed.footer or embed.timestamp %} +                <div class="discord-embed-footer"> +                    {% if embed.footer.icon_url %} +                        <img class="discord-embed-footer-icon" alt="Footer Icon" src="{{ embed.footer.icon_url }}"> +                    {% endif %} + +                    {%  if embed.footer.text or embed.timestamp %}<span class="discord-embed-footer-text">{% endif %} +                        {% if embed.footer.text %}{{ embed.footer.text }}{%  endif %}{% if embed.footer.text and embed.timestamp %} • {% endif %}{% if embed.timestamp %}{{ embed.timestamp | footer_datetime }}{% endif %} +                    {%  if embed.footer.text or embed.timestamp %}</span>{% endif %} +                </div> +                {%  endif %} +                </div> +            </div> +        {% endfor %}          </div>      {% endfor %}  {% endblock %}
\ No newline at end of file | 
