summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--core/html.py3
-rw-r--r--core/ld.py17
-rw-r--r--takahe/settings/base.py6
-rw-r--r--takahe/urls.py4
-rw-r--r--templates/activities/_post.html4
-rw-r--r--templates/identity/view.html6
-rw-r--r--users/models/identity.py22
8 files changed, 54 insertions, 9 deletions
diff --git a/.gitignore b/.gitignore
index 2a5c47b..5f0eef3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
*.psql
*.sqlite3
.venv
+/*.env
/media/
notes.md
diff --git a/core/html.py b/core/html.py
index fd41a50..5045b16 100644
--- a/core/html.py
+++ b/core/html.py
@@ -20,12 +20,13 @@ def sanitize_post(post_html: str) -> str:
Only allows a, br, p and span tags, and class attributes.
"""
cleaner = bleach.Cleaner(
- tags=["a", "br", "p", "span"],
+ tags=["br", "p"],
attributes={ # type:ignore
"a": allow_a,
"p": ["class"],
"span": ["class"],
},
filters=[LinkifyFilter],
+ strip=True,
)
return mark_safe(cleaner.clean(post_html))
diff --git a/core/ld.py b/core/ld.py
index 6692dab..59b867c 100644
--- a/core/ld.py
+++ b/core/ld.py
@@ -1,4 +1,5 @@
import datetime
+import os
import urllib.parse as urllib_parse
from typing import Dict, List, Optional, Union
@@ -436,3 +437,19 @@ def parse_ld_date(value: Optional[str]) -> Optional[datetime.datetime]:
return datetime.datetime.strptime(value, DATETIME_FORMAT).replace(
tzinfo=datetime.timezone.utc
)
+
+
+def media_type_from_filename(filename):
+ _, extension = os.path.splitext(filename)
+ if extension == ".png":
+ return "image/png"
+ elif extension in [".jpg", ".jpeg"]:
+ return "image/png"
+ elif extension == ".gif":
+ return "image/gif"
+ elif extension == ".apng":
+ return "image/apng"
+ elif extension == ".webp":
+ return "image/webp"
+ else:
+ return "application/octet-stream"
diff --git a/takahe/settings/base.py b/takahe/settings/base.py
index b98b9a0..dd89818 100644
--- a/takahe/settings/base.py
+++ b/takahe/settings/base.py
@@ -108,5 +108,7 @@ STATICFILES_DIRS = [
ALLOWED_HOSTS = ["*"]
-MEDIA_ROOT = BASE_DIR / "media"
-MEDIA_URL = "/media/"
+
+# Note that this MUST be a fully qualified URL in production
+MEDIA_URL = os.environ.get("TAKAHE_MEDIA_URL", "/media/")
+MEDIA_ROOT = os.environ.get("TAKAHE_MEDIA_ROOT", BASE_DIR / "media")
diff --git a/takahe/urls.py b/takahe/urls.py
index c2d9d6b..0b23d7d 100644
--- a/takahe/urls.py
+++ b/takahe/urls.py
@@ -1,5 +1,3 @@
-import re
-
from django.conf import settings as djsettings
from django.contrib import admin as djadmin
from django.urls import path, re_path
@@ -99,7 +97,7 @@ urlpatterns = [
path("djadmin/", djadmin.site.urls),
# Media files
re_path(
- r"^%s(?P<path>.*)$" % re.escape(djsettings.MEDIA_URL.lstrip("/")),
+ r"^media/(?P<path>.*)$",
serve,
kwargs={"document_root": djsettings.MEDIA_ROOT},
),
diff --git a/templates/activities/_post.html b/templates/activities/_post.html
index 5de8bc7..3d455ea 100644
--- a/templates/activities/_post.html
+++ b/templates/activities/_post.html
@@ -2,7 +2,9 @@
{% load activity_tags %}
<div class="post" data-takahe-id="{{ post.id }}">
- <img src="{{ post.author.local_icon_url }}" class="icon">
+ <a href="{{ post.author.urls.view }}">
+ <img src="{{ post.author.local_icon_url }}" class="icon">
+ </a>
<time>
{% if post.visibility == 0 %}
diff --git a/templates/identity/view.html b/templates/identity/view.html
index 223c2bb..f877f59 100644
--- a/templates/identity/view.html
+++ b/templates/identity/view.html
@@ -28,6 +28,12 @@
{{ identity.name_or_handle }} <small>@{{ identity.handle }}</small>
</h1>
+ {% if identity.summary %}
+ <div class="summary">
+ {{ identity.safe_summary }}
+ </div>
+ {% endif %}
+
{% if not identity.local %}
{% if identity.outdated and not identity.name %}
<p class="system-note">
diff --git a/users/models/identity.py b/users/models/identity.py
index d4ab720..21912ac 100644
--- a/users/models/identity.py
+++ b/users/models/identity.py
@@ -8,10 +8,12 @@ from asgiref.sync import async_to_sync, sync_to_async
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from django.db import models
+from django.template.defaultfilters import linebreaks_filter
from django.templatetags.static import static
from django.utils import timezone
-from core.ld import canonicalise
+from core.html import sanitize_post
+from core.ld import canonicalise, media_type_from_filename
from core.uploads import upload_namer
from stator.models import State, StateField, StateGraph, StatorModel
from users.models.domain import Domain
@@ -139,6 +141,10 @@ class Identity(StatorModel):
elif self.image_uri:
return self.image_uri
+ @property
+ def safe_summary(self):
+ return sanitize_post(self.summary)
+
### Alternate constructors/fetchers ###
@classmethod
@@ -223,7 +229,19 @@ class Identity(StatorModel):
if self.name:
response["name"] = self.name
if self.summary:
- response["summary"] = self.summary
+ response["summary"] = str(linebreaks_filter(self.summary))
+ if self.icon:
+ response["icon"] = {
+ "type": "Image",
+ "mediaType": media_type_from_filename(self.icon.name),
+ "url": self.icon.url,
+ }
+ if self.image:
+ response["image"] = {
+ "type": "Image",
+ "mediaType": media_type_from_filename(self.image.name),
+ "url": self.image.url,
+ }
return response
### ActivityPub (inbound) ###