diff --git a/moku/templates/moku/base.jinja b/moku/templates/moku/base.jinja
index 8a987f6..eb0a1e3 100644
--- a/moku/templates/moku/base.jinja
+++ b/moku/templates/moku/base.jinja
@@ -3,7 +3,15 @@
- moku.blog
+ {% if page_title %}{{ page_title }} — {% endif %}moku.blog
+ {% if page_title %}{% endif %}
+
+
+
+
+
+
+
diff --git a/moku/views/auth.py b/moku/views/auth.py
index e40aca3..6197a0b 100644
--- a/moku/views/auth.py
+++ b/moku/views/auth.py
@@ -12,6 +12,7 @@ class LoginView(View, BaseLoginView):
"""Allows users to log in by username and password."""
template_name = "moku/login.jinja"
+ page_title = "log in"
def get(self, request, *args, **kwargs):
if self.request.user.is_authenticated:
diff --git a/moku/views/base.py b/moku/views/base.py
index 1fc3ed5..eaa60a6 100644
--- a/moku/views/base.py
+++ b/moku/views/base.py
@@ -1,17 +1,23 @@
import random
+from django.conf import settings
from django.views import generic
class View(generic.TemplateView):
"""Defines a common set of data to be passed to the template context."""
+ page_title = None
+ """The title of the current page. Should be overridden by subclasses."""
+
def get_context_data(self, **kwargs):
return {
**super().get_context_data(**kwargs),
"header_emoji": random.choice(
["🍔", "🍕", "🍟", "🥪", "🥘", "🍰", "🍻", "🧁", "🍞", "🥯", "🥐"]
),
+ "site_root_url": settings.SITE_ROOT_URL,
+ "page_title": self.page_title,
}
diff --git a/moku/views/recipe.py b/moku/views/recipe.py
index d91922d..aca7e68 100644
--- a/moku/views/recipe.py
+++ b/moku/views/recipe.py
@@ -2,7 +2,7 @@ from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.shortcuts import get_object_or_404, redirect
from django.utils.functional import cached_property
-from django.utils.translation import gettext as _
+from django.utils.translation import gettext as _, gettext_lazy as _l
from moku.forms.recipe import RecipeForm, RecipeStepForm
from moku.models.recipe import Recipe, RecipeStep
@@ -56,6 +56,10 @@ class EditStepView(LoginRequiredMixin, UserPassesTestMixin, FormView):
template_name = "moku/recipe/edit_step.jinja"
form_class = RecipeStepForm
+ @property
+ def page_title(self):
+ return f"edit: step #{self.step.order + 1} of {self.step.recipe.title}"
+
def form_valid(self, form):
form.save()
messages.success(self.request, _("step updated!"))
@@ -80,6 +84,7 @@ class IndexRecipeView(LoginRequiredMixin, View):
"""Shows a list of recipes created by the authenticated user."""
template_name = "moku/recipe/index.jinja"
+ page_title = _l("my recipes")
def get_context_data(self, **kwargs):
return {
@@ -95,6 +100,7 @@ class NewRecipeView(LoginRequiredMixin, FormView):
template_name = "moku/recipe/form.jinja"
form_class = RecipeForm
+ page_title = _l("new recipe")
def form_valid(self, form):
form.instance.created_by = self.request.user
@@ -115,6 +121,10 @@ class ShowRecipeView(FormView):
template_name = "moku/recipe/show.jinja"
form_class = RecipeStepForm
+ @property
+ def page_title(self):
+ return self.recipe.title
+
@cached_property
def recipe(self):
return Recipe.objects.get(uuid=self.kwargs.get("uuid"))
diff --git a/moku/views/static.py b/moku/views/static.py
index b1993cd..013ec8c 100644
--- a/moku/views/static.py
+++ b/moku/views/static.py
@@ -1,3 +1,5 @@
+from django.utils.translation import gettext_lazy as _l
+
from moku.views.base import View
@@ -5,15 +7,18 @@ class ChangelogView(View):
"""Displays the static changelog page."""
template_name = "moku/changelog.jinja"
+ page_title = _l("changelog")
class PrivacyView(View):
"""Displays the static privacy policy page."""
template_name = "moku/privacy.jinja"
+ page_title = _l("privacy policy")
class TermsView(View):
"""Displays the static terms of use page."""
template_name = "moku/terms.jinja"
+ page_title = _l("terms of use")
diff --git a/moku/views/user.py b/moku/views/user.py
index 89f7843..2fcdc01 100644
--- a/moku/views/user.py
+++ b/moku/views/user.py
@@ -1,8 +1,9 @@
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db import IntegrityError
-from django.shortcuts import redirect
-from django.utils.translation import gettext as _
+from django.shortcuts import get_object_or_404, redirect
+from django.utils.functional import cached_property
+from django.utils.translation import gettext as _, gettext_lazy as _l
from moku.forms.user import ProfileForm, UserForm, UserSettingsForm
from moku.images import process_avatar_image
@@ -15,6 +16,7 @@ class EditProfileView(LoginRequiredMixin, FormView):
template_name = "moku/profile/edit.jinja"
form_class = ProfileForm
+ page_title = _l("edit profile")
def form_valid(self, form):
if "avatar" in form.changed_data and form.instance.avatar is not None:
@@ -32,6 +34,7 @@ class EditSettingsView(LoginRequiredMixin, FormView):
template_name = "moku/settings.jinja"
form_class = UserSettingsForm
+ page_title = _("settings")
def form_valid(self, form):
form.instance.user = self.request.user
@@ -58,12 +61,19 @@ class ProfileView(View):
template_name = "moku/profile/show.jinja"
+ @property
+ def page_title(self):
+ return f"@{self.profile.username}"
+
+ @cached_property
+ def profile(self):
+ return get_object_or_404(User, username=self.kwargs.get("username"))
+
def get_context_data(self, **kwargs):
- user = User.objects.get(username=self.kwargs.get("username"))
return {
**super().get_context_data(**kwargs),
- "profile": user,
- "posts": user.posts.order_by("-created_at").all(),
+ "profile": self.profile,
+ "posts": self.profile.posts.order_by("-created_at").all(),
}
@@ -72,6 +82,7 @@ class SignupView(FormView):
template_name = "moku/signup.jinja"
form_class = UserForm
+ page_title = _("sign up")
def form_valid(self, form):
form.instance.username = form.instance.username.lower()