Fix Module slug generation

This commit is contained in:
Lorenz Padberg 2024-04-12 11:13:12 +02:00
parent 101670355e
commit f398839765
3 changed files with 76 additions and 9 deletions

View File

@ -3,12 +3,14 @@ from django.db import models
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from wagtail.admin.forms import WagtailAdminPageForm from wagtail.admin.forms import WagtailAdminPageForm
from wagtail.admin.panels import FieldPanel, TabbedInterface, ObjectList from wagtail.admin.panels import FieldPanel, TabbedInterface, ObjectList, TitleFieldPanel
from wagtail.fields import RichTextField from wagtail.fields import RichTextField
from django.conf import settings
from core.constants import DEFAULT_RICH_TEXT_FEATURES from core.constants import DEFAULT_RICH_TEXT_FEATURES
from core.wagtail_utils import StrictHierarchyPage, get_default_settings from core.wagtail_utils import StrictHierarchyPage, get_default_settings
from users.models import SchoolClass from users.models import SchoolClass
from django.utils.text import slugify
EXACT = "exact" EXACT = "exact"
@ -54,9 +56,10 @@ class ModuleCategory(models.Model):
class ModulePageForm(WagtailAdminPageForm): class ModulePageForm(WagtailAdminPageForm):
def clean(self): def clean(self):
cleaned_data = super().clean() cleaned_data = super().clean()
print("cleaning")
if "slug" in self.cleaned_data: if "slug" in self.cleaned_data:
page_slug = cleaned_data["slug"] page_slug = cleaned_data["slug"]
if not Module._slug_is_available(page_slug, self.instance): if not Module._slug_is_available(page_slug, self.parent_page, self.instance):
self.add_error( self.add_error(
"slug", "slug",
forms.ValidationError( forms.ValidationError(
@ -97,7 +100,7 @@ class Module(StrictHierarchyPage):
solutions_enabled_for = models.ManyToManyField(SchoolClass) solutions_enabled_for = models.ManyToManyField(SchoolClass)
content_panels = [ content_panels = [
FieldPanel("title", classname="full title"), TitleFieldPanel("title", classname="full title"),
FieldPanel("meta_title", classname="full title"), FieldPanel("meta_title", classname="full title"),
FieldPanel("level"), FieldPanel("level"),
FieldPanel("category"), FieldPanel("category"),
@ -106,7 +109,7 @@ class Module(StrictHierarchyPage):
FieldPanel("teaser"), FieldPanel("teaser"),
FieldPanel("intro"), FieldPanel("intro"),
] ]
base_form_class = ModulePageForm # base_form_class = ModulePageForm
edit_handler = TabbedInterface( edit_handler = TabbedInterface(
[ObjectList(content_panels, heading="Content"), get_default_settings()] [ObjectList(content_panels, heading="Content"), get_default_settings()]
@ -181,11 +184,42 @@ class Module(StrictHierarchyPage):
return f"{self.meta_title} - {self.title}" return f"{self.meta_title} - {self.title}"
@staticmethod @staticmethod
def _slug_is_available(slug, page): def _slug_is_available(slug, parent_page, page=None):
# modeled after `Page._slug_is_available` """
modules = Module.objects.filter(slug=slug).not_page(page)
return not modules.exists() # modeled after `Page._slug_is_available`
Determine whether the given slug is available for use on a child page of
parent_page. If 'page' is passed, the slug is intended for use on that page
(and so it will be excluded from the duplicate check).
"""
if parent_page is None:
# the root page's slug can be whatever it likes...
return True
modules = Module.objects.all()
if page:
modules = modules.not_page(page)
return not modules.filter(slug=slug).exists()
def _get_autogenerated_slug(self, base_slug):
# modeled after `Page._get_autogenerated_slug`
candidate_slug = base_slug
suffix = 1
parent_page = self.get_parent()
while not self._slug_is_available(candidate_slug, parent_page, self):
# try with incrementing suffix until we find a slug which is available
suffix += 1
candidate_slug = "%s-%d" % (base_slug, suffix)
return candidate_slug
def full_clean(self, *args, **kwargs):
super().full_clean(*args, **kwargs)
if not self._slug_is_available(self.slug, self.get_parent(), self):
self.slug = self._get_autogenerated_slug(self.slug)
class RecentModule(models.Model): class RecentModule(models.Model):

View File

@ -0,0 +1,32 @@
from django.test import TestCase, RequestFactory
from unittest import skip
from graphene.test import Client
from graphql_relay import to_global_id
from api.schema import schema
from api.utils import get_object
from books.models import ContentBlock, Chapter
from books.factories import ModuleFactory, ModuleLevelFactory, TopicFactory
from core.factories import UserFactory
from users.models import User
class TestModuleCreation(TestCase):
"""
Since the modules url in the frontend is not /topic/module but /module the slug has to be unique.
This test checks if the slug is generated correctly.
"""
def test_create_new_module_generates_slug(self):
topic = TopicFactory(title="Berufslehre")
module = ModuleFactory(title="Modul 1", parent=topic)
self.assertEqual("modul-1", module.slug)
def test_create_new_module_different_topic(self):
topic = TopicFactory(title="Berufslehre")
module = ModuleFactory(title="Modul 1", parent=topic)
topic2 = TopicFactory(title="Geld und Macht")
module2 = ModuleFactory(title="Modul 1", parent=topic2)
self.assertEqual("modul-1", module.slug)
self.assertEqual("modul-1-2", module2.slug, )

View File

@ -2,6 +2,7 @@ from django.contrib import admin
from wagtail.admin.panels import CommentPanel from wagtail.admin.panels import CommentPanel
from wagtail.admin.panels import FieldPanel, ObjectList from wagtail.admin.panels import FieldPanel, ObjectList
from wagtail.models import Page from wagtail.models import Page
from wagtail.admin.widgets.slug import SlugInput
class StrictHierarchyPage(Page): class StrictHierarchyPage(Page):
@ -41,4 +42,4 @@ def wagtail_parent_filter(parent_cls, child_cls):
def get_default_settings(): def get_default_settings():
return ObjectList([FieldPanel("slug", read_only=True), CommentPanel()], heading="Settings") return ObjectList([FieldPanel("slug", widget=SlugInput), CommentPanel()], heading="Settings")