Fix Module slug generation
This commit is contained in:
parent
101670355e
commit
f398839765
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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, )
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue