diff --git a/.gitignore b/.gitignore index 1f56d95..e296653 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *.pyc *.sqlite3 .venv +.vscode /*.env /build /docs/_build diff --git a/activities/views/posts.py b/activities/views/posts.py index 7950093..fce17e3 100644 --- a/activities/views/posts.py +++ b/activities/views/posts.py @@ -135,9 +135,10 @@ class Compose(FormView): ) content_warning = forms.CharField( required=False, + label=Config.lazy_system_value("content_warning_text"), widget=forms.TextInput( attrs={ - "placeholder": "Content Warning", + "placeholder": Config.lazy_system_value("content_warning_text"), }, ), help_text="Optional - Post will be hidden behind this text until clicked", diff --git a/activities/views/timelines.py b/activities/views/timelines.py index e4c9920..837e5df 100644 --- a/activities/views/timelines.py +++ b/activities/views/timelines.py @@ -5,6 +5,7 @@ from django.utils.decorators import method_decorator from django.views.generic import FormView, ListView from activities.models import Post, PostInteraction, TimelineEvent +from core.models import Config from users.decorators import identity_required @@ -23,10 +24,11 @@ class Home(FormView): ) content_warning = forms.CharField( required=False, + label=Config.lazy_system_value("content_warning_text"), widget=forms.TextInput( attrs={ - "placeholder": "Content Warning", "class": "hidden", + "placeholder": Config.lazy_system_value("content_warning_text"), }, ), ) diff --git a/core/models/config.py b/core/models/config.py index d69205c..dca8a0c 100644 --- a/core/models/config.py +++ b/core/models/config.py @@ -5,6 +5,7 @@ import pydantic from django.core.files import File from django.db import models from django.templatetags.static import static +from django.utils.functional import lazy from core.uploads import upload_namer from takahe import __version__ @@ -55,6 +56,15 @@ class Config(models.Model): system: ClassVar["Config.ConfigOptions"] # type: ignore + @classmethod + def lazy_system_value(cls, key: str): + """ + Lazily load a System.Config value + """ + if key not in cls.SystemOptions.__fields__: + raise KeyError(f"Undefined SystemOption for {key}") + return lazy(lambda: getattr(Config.system, key)) + @classmethod def load_values(cls, options_class, filters): """ @@ -166,6 +176,7 @@ class Config(models.Model): signup_allowed: bool = True signup_invite_only: bool = False signup_text: str = "" + content_warning_text: str = "Content Warning" post_length: int = 500 identity_min_length: int = 2 diff --git a/tests/activities/views/test_posts.py b/tests/activities/views/test_posts.py new file mode 100644 index 0000000..b04c30f --- /dev/null +++ b/tests/activities/views/test_posts.py @@ -0,0 +1,24 @@ +import re + +import mock +import pytest + +from activities.views.posts import Compose + + +@pytest.mark.django_db +def test_content_warning_text(identity, user, rf, config_system): + request = rf.get("/compose/") + request.user = user + request.identity = identity + + config_system.content_warning_text = "Content Summary" + with mock.patch("core.models.Config.load_system", return_value=config_system): + view = Compose.as_view() + resp = view(request) + assert resp.status_code == 200 + content = str(resp.rendered_content) + assert 'placeholder="Content Summary"' in content + assert re.search( + r"\s*Content Summary\s*", content, flags=re.MULTILINE + ) diff --git a/tests/activities/views/test_timelines.py b/tests/activities/views/test_timelines.py new file mode 100644 index 0000000..6c8b355 --- /dev/null +++ b/tests/activities/views/test_timelines.py @@ -0,0 +1,18 @@ +import mock +import pytest + +from activities.views.timelines import Home + + +@pytest.mark.django_db +def test_content_warning_text(identity, user, rf, config_system): + request = rf.get("/") + request.user = user + request.identity = identity + + config_system.content_warning_text = "Content Summary" + with mock.patch("core.models.Config.load_system", return_value=config_system): + view = Home.as_view() + resp = view(request) + assert resp.status_code == 200 + assert 'placeholder="Content Summary"' in str(resp.rendered_content) diff --git a/tests/conftest.py b/tests/conftest.py index 536162c..69e8e7a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -62,11 +62,16 @@ def config_system(keypair): @pytest.fixture @pytest.mark.django_db -def identity(): +def user() -> User: + return User.objects.create(email="test@example.com") + + +@pytest.fixture +@pytest.mark.django_db +def identity(user): """ Creates a basic test identity with a user and domain. """ - user = User.objects.create(email="test@example.com") domain = Domain.objects.create(domain="example.com", local=True, public=True) identity = Identity.objects.create( actor_uri="https://example.com/test-actor/", diff --git a/users/views/admin/settings.py b/users/views/admin/settings.py index 0ed9a60..44a338f 100644 --- a/users/views/admin/settings.py +++ b/users/views/admin/settings.py @@ -37,6 +37,10 @@ class BasicSettings(AdminSettingsPage): "title": "Maximum Post Length", "help_text": "The maximum number of characters allowed per post", }, + "content_warning_text": { + "title": "Content Warning Feature Name", + "help_text": "What the feature that lets users provide post summaries is called", + }, "site_about": { "title": "About This Site", "help_text": "Displayed on the homepage and the about page.\nNewlines are preserved; HTML also allowed.", @@ -87,7 +91,7 @@ class BasicSettings(AdminSettingsPage): "highlight_color", ], "Signups": ["signup_allowed", "signup_invite_only", "signup_text"], - "Posts": ["post_length"], + "Posts": ["post_length", "content_warning_text"], "Identities": [ "identity_max_per_user", "identity_min_length",