Skip to content

Commit

Permalink
Policy pages and signup tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewgodwin committed Dec 6, 2022
1 parent da9a3d8 commit a31f676
Show file tree
Hide file tree
Showing 18 changed files with 275 additions and 67 deletions.
16 changes: 9 additions & 7 deletions core/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,21 @@ class ConfigLoadingMiddleware:
Caches the system config every request
"""

refresh_interval: float = 30.0
refresh_interval: float = 5.0

def __init__(self, get_response):
self.get_response = get_response
self.config_ts: float = 0.0

def __call__(self, request):
if (
not getattr(Config, "system", None)
or (time() - self.config_ts) >= self.refresh_interval
):
Config.system = Config.load_system()
self.config_ts = time()
# Allow test fixtures to force and lock the config
if not getattr(Config, "__forced__", False):
if (
not getattr(Config, "system", None)
or (time() - self.config_ts) >= self.refresh_interval
):
Config.system = Config.load_system()
self.config_ts = time()
response = self.get_response(request)
return response

Expand Down
4 changes: 4 additions & 0 deletions core/models/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ class SystemOptions(pydantic.BaseModel):
site_icon: UploadedImage = static("img/icon-128.png")
site_banner: UploadedImage = static("img/fjords-banner-600.jpg")

policy_terms: str = ""
policy_privacy: str = ""
policy_rules: str = ""

signup_allowed: bool = True
signup_invite_only: bool = False
signup_text: str = ""
Expand Down
29 changes: 29 additions & 0 deletions core/views.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import markdown_it
from django.http import JsonResponse
from django.templatetags.static import static
from django.utils.decorators import method_decorator
from django.utils.safestring import mark_safe
from django.views.generic import TemplateView, View

from activities.views.timelines import Home
from core.decorators import cache_page
from core.models import Config
from users.models import Identity


Expand All @@ -22,6 +25,9 @@ class LoggedOutHomepage(TemplateView):

def get_context_data(self):
return {
"about": mark_safe(
markdown_it.MarkdownIt().render(Config.system.site_about)
),
"identities": Identity.objects.filter(
local=True,
discoverable=True,
Expand Down Expand Up @@ -60,3 +66,26 @@ def get(self, request):
],
}
)


class FlatPage(TemplateView):
"""
Serves a "flat page" from a config option,
returning 404 if it is empty.
"""

template_name = "flatpage.html"
config_option = None
title = None

def get_context_data(self):
if self.config_option is None:
raise ValueError("No config option provided")
# Get raw content
content = getattr(Config.system, self.config_option)
# Render it
html = markdown_it.MarkdownIt().render(content)
return {
"title": self.title,
"content": mark_safe(html),
}
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ django~=4.1
email-validator~=1.3.0
gunicorn~=20.1.0
httpx~=0.23
markdown_it_py~=2.1.0
pillow~=9.3.0
psycopg2~=2.9.5
pydantic~=1.10.2
Expand Down
1 change: 1 addition & 0 deletions static/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ footer {

footer a {
border-bottom: 1px solid var(--color-text-duller);
margin-right: 5px;
}

header {
Expand Down
14 changes: 14 additions & 0 deletions stator/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import traceback
import uuid

from asgiref.sync import async_to_sync
from django.utils import timezone

from core import exceptions, sentry
Expand Down Expand Up @@ -142,3 +143,16 @@ def remove_completed_tasks(self):
Removes all completed asyncio.Tasks from our local in-progress list
"""
self.tasks = [t for t in self.tasks if not t.done()]

async def run_single_cycle(self):
"""
Testing entrypoint to advance things just one cycle
"""
await asyncio.wait_for(self.fetch_and_process_tasks(), timeout=1)
for _ in range(100):
if not self.tasks:
break
self.remove_completed_tasks()
await asyncio.sleep(0.01)

run_single_cycle_sync = async_to_sync(run_single_cycle)
26 changes: 26 additions & 0 deletions takahe/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@
admin.TuningSettings.as_view(),
name="admin_tuning",
),
path(
"admin/policies/",
admin.PoliciesSettings.as_view(),
name="admin_policies",
),
path(
"admin/domains/",
admin.Domains.as_view(),
Expand Down Expand Up @@ -150,6 +155,27 @@
path("@<handle>/activate/", identity.ActivateIdentity.as_view()),
path("identity/select/", identity.SelectIdentity.as_view()),
path("identity/create/", identity.CreateIdentity.as_view()),
# Flat pages
path(
"about/",
core.FlatPage.as_view(title="About This Server", config_option="site_about"),
name="about",
),
path(
"pages/privacy/",
core.FlatPage.as_view(title="Privacy Policy", config_option="policy_privacy"),
name="privacy",
),
path(
"pages/terms/",
core.FlatPage.as_view(title="Terms of Service", config_option="policy_terms"),
name="terms",
),
path(
"pages/rules/",
core.FlatPage.as_view(title="Server Rules", config_option="policy_rules"),
name="rules",
),
# Well-known endpoints and system actor
path(".well-known/webfinger", activitypub.Webfinger.as_view()),
path(".well-known/host-meta", activitypub.HostMeta.as_view()),
Expand Down
6 changes: 5 additions & 1 deletion templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,11 @@
</main>

<footer>
<span>Powered by <a href="https://jointakahe.org">Takahē {{ config.version }}</a></span>
{% if config.site_about %}<a href="{% url "about" %}">About</a>{% endif %}
{% if config.policy_rules %}<a href="{% url "rules" %}">Server Rules</a>{% endif %}
{% if config.policy_terms %}<a href="{% url "terms" %}">Terms of Service</a>{% endif %}
{% if config.policy_privacy %}<a href="{% url "privacy" %}">Privacy Policy</a>{% endif %}
<a href="https://jointakahe.org">Takahē {{ config.version }}</a>
</footer>

</body>
Expand Down
8 changes: 8 additions & 0 deletions templates/flatpage.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{% extends "base.html" %}

{% block title%}{{ title }}{% endblock %}

{% block content %}
<h1>{{ title }}</h1>
{{ content }}
{% endblock %}
2 changes: 1 addition & 1 deletion templates/forms/_field.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</label>
{% if field.help_text %}
<p class="help">
{{ field.help_text|linebreaksbr }}
{{ field.help_text|safe|linebreaksbr }}
</p>
{% endif %}
{{ field.errors }}
Expand Down
2 changes: 1 addition & 1 deletion templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
{% block content %}
<div class="about">
<img class="banner" src="{{ config.site_banner }}">
{{ config.site_about|safe|linebreaks }}
{{ about }}
</div>
<h2>People</h2>
{% for identity in identities %}
Expand Down
3 changes: 3 additions & 0 deletions templates/settings/_menu.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ <h3>Administration</h3>
<a href="{% url "admin_basic" %}" {% if section == "basic" %}class="selected"{% endif %} title="Basic">
<i class="fa-solid fa-book"></i> Basic
</a>
<a href="{% url "admin_policies" %}" {% if section == "policies" %}class="selected"{% endif %} title="Policies">
<i class="fa-solid fa-file-lines"></i> Policies
</a>
<a href="{% url "admin_domains" %}" {% if section == "domains" %}class="selected"{% endif %} title="Domains">
<i class="fa-solid fa-globe"></i> Domains
</a>
Expand Down
24 changes: 4 additions & 20 deletions tests/activities/models/test_post.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import asyncio

import pytest
from asgiref.sync import async_to_sync
from pytest_httpx import HTTPXMock

from activities.models import Post, PostStates
Expand Down Expand Up @@ -128,21 +125,8 @@ def test_linkify_mentions_local(identity, remote_identity):
assert post.safe_content_local() == "<p>@[email protected], welcome!</p>"


async def stator_process_tasks(stator):
"""
Guarded wrapper to simply async_to_sync and ensure all stator tasks are
run to completion without blocking indefinitely.
"""
await asyncio.wait_for(stator.fetch_and_process_tasks(), timeout=1)
for _ in range(100):
if not stator.tasks:
break
stator.remove_completed_tasks()
await asyncio.sleep(0.01)


@pytest.mark.django_db
def test_post_transitions(identity, stator_runner):
def test_post_transitions(identity, stator):

# Create post
post = Post.objects.create(
Expand All @@ -153,18 +137,18 @@ def test_post_transitions(identity, stator_runner):
)
# Test: | --> new --> fanned_out
assert post.state == str(PostStates.new)
async_to_sync(stator_process_tasks)(stator_runner)
stator.run_single_cycle_sync()
post = Post.objects.get(id=post.id)
assert post.state == str(PostStates.fanned_out)

# Test: fanned_out --> (forced) edited --> edited_fanned_out
Post.transition_perform(post, PostStates.edited)
async_to_sync(stator_process_tasks)(stator_runner)
stator.run_single_cycle_sync()
post = Post.objects.get(id=post.id)
assert post.state == str(PostStates.edited_fanned_out)

# Test: edited_fanned_out --> (forced) deleted --> deleted_fanned_out
Post.transition_perform(post, PostStates.deleted)
async_to_sync(stator_process_tasks)(stator_runner)
stator.run_single_cycle_sync()
post = Post.objects.get(id=post.id)
assert post.state == str(PostStates.deleted_fanned_out)
5 changes: 4 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ def config_system(keypair):
system_actor_private_key=keypair["private_key"],
system_actor_public_key=keypair["public_key"],
)
Config.__forced__ = True
yield Config.system
Config.__forced__ = False
del Config.system


@pytest.fixture
Expand Down Expand Up @@ -126,7 +129,7 @@ def remote_identity() -> Identity:


@pytest.fixture
def stator_runner(config_system) -> StatorRunner:
def stator(config_system) -> StatorRunner:
"""
Return an initialized StatorRunner for tests that need state transitioning
to happen.
Expand Down
Loading

0 comments on commit a31f676

Please sign in to comment.