From 4c1e21cddaec2912b98e1e35d8a1142bbbfa0cce Mon Sep 17 00:00:00 2001 From: David THENON Date: Mon, 16 Sep 2024 20:14:31 +0000 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9C=A8(backend)=20added=20new=20page=20e?= =?UTF-8?q?xtension=20'IndexPage'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This new extension cover the need of some options for pages in main menu. These options are: - Enable to show a dropdown of children page; - Add a custom color class for the page on hover event in menu; This implementation brings a new Page extension, new admin, new menu modifier, new extension toolbar, new settings, menu template changes and some CSS adjustments. --- Makefile | 2 +- src/frontend/scss/components/_header.scss | 15 ++- .../apps/core/templates/menu/header_menu.html | 26 ++-- .../apps/core/templates/richie/base.html | 2 +- src/richie/apps/courses/admin.py | 16 +++ src/richie/apps/courses/cms_menus.py | 79 +++++++++++ src/richie/apps/courses/cms_toolbars.py | 52 +++++++- src/richie/apps/courses/cms_wizards.py | 70 ++++++++++ src/richie/apps/courses/defaults.py | 22 ++++ src/richie/apps/courses/factories.py | 23 ++++ .../courses/migrations/0035_add_indexpage.py | 74 +++++++++++ src/richie/apps/courses/models/__init__.py | 1 + src/richie/apps/courses/models/index.py | 51 +++++++ src/richie/apps/courses/settings/__init__.py | 11 ++ tests/apps/courses/test_cms_toolbars.py | 124 ++++++++++++++++++ 15 files changed, 553 insertions(+), 15 deletions(-) create mode 100644 src/richie/apps/courses/cms_menus.py create mode 100644 src/richie/apps/courses/migrations/0035_add_indexpage.py create mode 100644 src/richie/apps/courses/models/index.py diff --git a/Makefile b/Makefile index 1b999397c5..3402c88b8a 100644 --- a/Makefile +++ b/Makefile @@ -269,7 +269,7 @@ superuser: ## Create an admin user with password "admin" test-back: ## run back-end tests, or specific test like `make test-back tests/apps/core/test_web_analytics.py` @args="$(filter-out $@,$(MAKECMDGOALS))" && \ - DB_PORT=$(DB_PORT) bin/pytest $${args:-${1}} + DB_PORT=$(DB_PORT) bin/pytest --reuse-db $${args:-${1}} .PHONY: test-back # -- Internationalization diff --git a/src/frontend/scss/components/_header.scss b/src/frontend/scss/components/_header.scss index c96a37181d..93f0a654ed 100644 --- a/src/frontend/scss/components/_header.scss +++ b/src/frontend/scss/components/_header.scss @@ -208,6 +208,18 @@ @include sv-flex(0, 0, auto); } + // Define color variable for default item color on hover then available variants + @if r-theme-val(topbar, item-hover-color) { + --r--menu--item--hover--color: #{r-theme-val(topbar, item-hover-color)}; + } + // WARNING: Those are currently some variant samples during development + &--primary { + --r--menu--item--hover--color: #{$primary}; + } + &--warning { + --r--menu--item--hover--color: #{$warning}; + } + & > a { @include sv-flex(1, 0, 100%); display: flex; @@ -228,6 +240,7 @@ @include media-breakpoint-up($r-topbar-breakpoint) { position: relative; + // If there is no default hover color we assume there is also no variant @if r-theme-val(topbar, item-hover-color) { &::after { content: ''; @@ -236,7 +249,7 @@ left: 0; right: 0; height: 8px; - background-color: r-theme-val(topbar, item-hover-color); + background-color: var(--r--menu--item--hover--color); border-top-left-radius: 0.2rem; border-top-right-radius: 0.2rem; } diff --git a/src/richie/apps/core/templates/menu/header_menu.html b/src/richie/apps/core/templates/menu/header_menu.html index 96bb7d72a4..9289d9e7a7 100644 --- a/src/richie/apps/core/templates/menu/header_menu.html +++ b/src/richie/apps/core/templates/menu/header_menu.html @@ -1,19 +1,23 @@ -{% load cms_tags %}{% spaceless %} +{% load menu_tags %}{% spaceless %} {% for child in children %} - {% with children_slug=child.get_menu_title|slugify %} - {% endwith %} {% endfor %} diff --git a/src/richie/apps/core/templates/richie/base.html b/src/richie/apps/core/templates/richie/base.html index daacadf5ba..d38c7c2757 100644 --- a/src/richie/apps/core/templates/richie/base.html +++ b/src/richie/apps/core/templates/richie/base.html @@ -135,7 +135,7 @@ diff --git a/src/richie/apps/courses/admin.py b/src/richie/apps/courses/admin.py index 1378b8972d..41b80d8f24 100644 --- a/src/richie/apps/courses/admin.py +++ b/src/richie/apps/courses/admin.py @@ -280,6 +280,21 @@ def snapshot(self, request, course_id, *args, **kwargs): return JsonResponse({"id": new_page.course.id}) +class IndexPageAdmin(PageExtensionAdmin): + """ + Admin class for the IndexPage model + """ + + list_display = ["title", "allow_submenu"] + + # pylint: disable=no-self-use + def title(self, obj): + """ + Get the page title from the related page + """ + return obj.extended_object.get_title() + + class OrganizationAdmin(PageExtensionAdmin): """ Admin class for the Organization model @@ -349,6 +364,7 @@ class LicenceAdmin(TranslatableAdmin): admin.site.register(models.Course, CourseAdmin) admin.site.register(models.CourseRun, CourseRunAdmin) admin.site.register(models.Licence, LicenceAdmin) +admin.site.register(models.IndexPage, IndexPageAdmin) admin.site.register(models.Organization, OrganizationAdmin) admin.site.register(models.PageRole, PageRoleAdmin) admin.site.register(models.Person, PersonAdmin) diff --git a/src/richie/apps/courses/cms_menus.py b/src/richie/apps/courses/cms_menus.py new file mode 100644 index 0000000000..9a8d19f311 --- /dev/null +++ b/src/richie/apps/courses/cms_menus.py @@ -0,0 +1,79 @@ +""" +Menu modifier to add feature for menu template context. +""" + +from django.conf import settings + +from menus.base import Modifier +from menus.menu_pool import menu_pool + +from .models import IndexPage + + +class MenuWithIndexPage(Modifier): + """ + Menu modifier to include IndexPage extension data in menu template context. + + In menu template you will be able to reach possible extension data from node + attribute ``menu_extension``. If node page has no extension it will have an empty + dict. Only a specific node level is processedn nodes with a different level won't + have the attribute ``menu_extension`` at all. + """ + + # pylint: disable=too-many-arguments + def modify(self, request, nodes, namespace, root_id, post_cut, breadcrumb): + """ + Patch navigation nodes to include data from possible extension + ``IndexPage``. + + For performance: + + * This does not work for breadcrumb navigation (all extension options are mean + for menu only); + * This works only on the menu top level, it means the one defined as first + argument from tag ``{% show_menu .. %}``; + + Then to avoid making a query for each node item to retrieve its possible + extension object, we get the extensions in bulk as values instead of objects. + + Finally we add the data on nodes so they can used from menu template. + """ + # We are not altering breadcrumb menu, this is only for navigation menu and + # only for the visible menu (not the whole processed tree) + if not nodes or breadcrumb or not post_cut: + return nodes + + # Get the page ids to process, only for the allowed node level + page_ids = [ + node.id + for node in nodes + if node.level == settings.RICHIE_PAGEINDEX_MENU_ALLOWED_LEVEL + ] + + # No need to continue if we don't have any valid node + if not page_ids: + return nodes + + # We directly get the extensions from their related page id and serialized + # as a dict instead of model object + extension_queryset = IndexPage.objects.filter( + extended_object_id__in=page_ids + ).values("extended_object_id", "allow_submenu", "menu_color") + + # Pack extensions data into proper structure + extension_datas = { + item["extended_object_id"]: { + "allow_submenu": item["allow_submenu"], + "menu_color": item["menu_color"], + } + for item in extension_queryset + } + + # Attach each possible extension data to its relative node + for node in nodes: + node.menu_extension = extension_datas.get(node.id, {}) + + return nodes + + +menu_pool.register_modifier(MenuWithIndexPage) diff --git a/src/richie/apps/courses/cms_toolbars.py b/src/richie/apps/courses/cms_toolbars.py index 66dac30f43..1acbbcbc6f 100644 --- a/src/richie/apps/courses/cms_toolbars.py +++ b/src/richie/apps/courses/cms_toolbars.py @@ -2,6 +2,7 @@ Toolbar extension for the courses application """ +from django.conf import settings from django.utils.text import capfirst from django.utils.translation import gettext_lazy as _ @@ -12,7 +13,7 @@ from cms.utils.urlutils import admin_reverse from .defaults import PAGE_EXTENSION_TOOLBAR_ITEM_POSITION -from .models import Category, Course, Organization, Person +from .models import Category, Course, IndexPage, Organization, Person class BaseExtensionToolbar(ExtensionToolbar): @@ -131,3 +132,52 @@ class PersonExtensionToolbar(BaseExtensionToolbar): """ model = Person + + +@toolbar_pool.register +class IndexPageExtensionToolbar(BaseExtensionToolbar): + """ + This extension class customizes the toolbar for the IndexPage page extension. + """ + + model = IndexPage + + def populate(self): + """ + Specific extension populate method. + + This extension entry only appears in toolbar if page already have extension or + if setting ``RICHIE_PAGEINDEX_ALLOW_CREATION`` is true. Finally the page level + must also match the allowed level from setting + ``RICHIE_PAGEINDEX_MENU_ALLOWED_LEVEL``. + """ + # always use draft if we have a page + self.page = get_page_draft(self.request.current_page) + if not self.page: + # Nothing to do + return + + # setup the extension toolbar with permissions and sanity checks + page_menu = self._setup_extension_toolbar() + + if user_can_change_page(user=self.request.user, page=self.page): + # Retrieves extension instance (if any) and toolbar URL + page_extension, admin_url = self.get_page_extension_admin() + # Get the page node level + level = self.page.node.get_depth() - 1 + allowed = page_extension is not None or ( + page_extension is None + and settings.RICHIE_PAGEINDEX_ALLOW_CREATION is True + ) + if ( + allowed + and level == settings.RICHIE_PAGEINDEX_MENU_ALLOWED_LEVEL + and admin_url + ): + # Adds a toolbar item in position 0 (at the top of the menu) + page_menu.add_modal_item( + _("Index Page settings"), + url=admin_url, + disabled=not self.toolbar.edit_mode_active, + position=PAGE_EXTENSION_TOOLBAR_ITEM_POSITION, + ) diff --git a/src/richie/apps/courses/cms_wizards.py b/src/richie/apps/courses/cms_wizards.py index 063e778a60..5763c1d8c9 100644 --- a/src/richie/apps/courses/cms_wizards.py +++ b/src/richie/apps/courses/cms_wizards.py @@ -577,3 +577,73 @@ def user_has_add_permission(self, user, **kwargs): weight=200, ) ) + + +class IndexPageWizardForm(BaseWizardForm): + """ + Opposed to other extensions, this wizard form allow for a null reverse id since + index pages are commonly at the root of the page tree. + """ + + model = models.IndexPage + + def clean(self): + """ + Bypass ``BasePageExtension.clean`` method in case of null reverse id + since it would lead to an exception. + """ + if self.model.PAGE["reverse_id"]: + cleaned_data = super().clean() + else: + cleaned_data = super(BaseWizardForm, self).clean() + + # If the slug is not explicitly set, generate it from the title + if cleaned_data.get("title") and not cleaned_data.get("slug"): + cleaned_data["slug"] = slugify(cleaned_data["title"])[:200] + + if len(cleaned_data["slug"]) > 255: + raise forms.ValidationError( + {"slug": [_("This slug is too long, it should be less than 255")]} + ) + + if Page.objects.filter(title_set__slug=cleaned_data["slug"]).exists(): + raise forms.ValidationError( + {"slug": [_("This slug is already in use")]} + ) + + return cleaned_data + + @cached_property + def parent_page(self): + """ + By pass ``BasePageExtension.parent_page`` property in case of null reverse id + since it would lead to exception. + """ + if self.model.PAGE["reverse_id"]: + return super().parent_page + + return None + + def save(self): + """ + The parent form from BasePageExtension created the page. + And this method creates the associated index extension. + """ + page = super().save() + models.IndexPage.objects.create(extended_object=page) + return page + + +class IndexPageWizard(Wizard): + """A page wizard to create a page with an IndexPage extension object.""" + + +wizard_pool.register( + IndexPageWizard( + title="New IndexPage", + weight=200, + model=models.IndexPage, + form=IndexPageWizardForm, + description="Create a new IndexPage instance", + ) +) diff --git a/src/richie/apps/courses/defaults.py b/src/richie/apps/courses/defaults.py index bea2f7d631..1881849cc7 100644 --- a/src/richie/apps/courses/defaults.py +++ b/src/richie/apps/courses/defaults.py @@ -312,6 +312,10 @@ "reverse_id": "organizations", "template": "courses/cms/organization_detail.html", } +INDEXES_PAGE = { + "reverse_id": None, + "template": "richie/single_column.html", +} PERSONS_PAGE = {"reverse_id": "persons", "template": "courses/cms/person_detail.html"} PROGRAMS_PAGE = { "reverse_id": "programs", @@ -340,6 +344,11 @@ "in_navigation": True, "template": "search/search.html", }, + INDEXES_PAGE["reverse_id"]: { + "title": "Indexes", + "in_navigation": True, + "template": "richie/single_column.html", + }, ORGANIZATIONS_PAGE["reverse_id"]: { "title": "Organizations", "in_navigation": True, @@ -382,3 +391,16 @@ # Maximum number of archived course runs displayed by default on course detail page. # The additional runs can be viewed by clicking on `View more` link. RICHIE_MAX_ARCHIVED_COURSE_RUNS = 10 + +# Define possible hover color that can be choosen for an IndexPage and to apply on +# its menu item +INDEX_MENU_COLOR_CLASSES = getattr( + settings, + "RICHIE_INDEX_MENU_COLOR_CLASSES", + # (("", "None"),) + ( + ("", "None"), + ("primary", "Primary"), + ("warning", "Warning"), + ), +) diff --git a/src/richie/apps/courses/factories.py b/src/richie/apps/courses/factories.py index be9b319d05..a1fc0ef204 100644 --- a/src/richie/apps/courses/factories.py +++ b/src/richie/apps/courses/factories.py @@ -910,3 +910,26 @@ def fill_excerpt(self, create, extracted, **kwargs): plugin_type="PlainTextPlugin", body=text, ) + + +class IndexPageFactory(BLDPageExtensionDjangoModelFactory): + """ + A factory to automatically generate random yet meaningful index page extensions + and their related page in our tests. + """ + + class Meta: + model = models.IndexPage + exclude = [ + "page_in_navigation", + "page_languages", + "page_parent", + "page_reverse_id", + "page_template", + "page_title", + ] + + # fields concerning the related page + page_template = models.IndexPage.PAGE["template"] + allow_submenu = False + menu_color = "" diff --git a/src/richie/apps/courses/migrations/0035_add_indexpage.py b/src/richie/apps/courses/migrations/0035_add_indexpage.py new file mode 100644 index 0000000000..9c68ac1b65 --- /dev/null +++ b/src/richie/apps/courses/migrations/0035_add_indexpage.py @@ -0,0 +1,74 @@ +# Generated by Django 4.2.14 on 2024-09-09 15:06 + +import django.db.models.deletion +from django.db import migrations, models + +from ..defaults import INDEX_MENU_COLOR_CLASSES + + +class Migration(migrations.Migration): + + dependencies = [ + ("cms", "0022_auto_20180620_1551"), + ("courses", "0034_auto_20230817_1736"), + ] + + operations = [ + migrations.CreateModel( + name="IndexPage", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "allow_submenu", + models.BooleanField( + default=False, + help_text="If enabled the page entry in menu will be a dropdown for its possible children.", + verbose_name="Allow submenu", + ), + ), + ( + "menu_color", + models.CharField( + blank=True, + choices=INDEX_MENU_COLOR_CLASSES, + default="", + help_text="A color used to display page in menu", + max_length=10, + verbose_name="Color in menu", + ), + ), + ( + "extended_object", + models.OneToOneField( + editable=False, + on_delete=django.db.models.deletion.CASCADE, + to="cms.page", + ), + ), + ( + "public_extension", + models.OneToOneField( + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="draft_extension", + to="courses.indexpage", + ), + ), + ], + options={ + "verbose_name": "index", + "verbose_name_plural": "indexes", + "db_table": "richie_index", + "ordering": ["-pk"], + }, + ), + ] diff --git a/src/richie/apps/courses/models/__init__.py b/src/richie/apps/courses/models/__init__.py index 15b1903e68..dc237ef91a 100644 --- a/src/richie/apps/courses/models/__init__.py +++ b/src/richie/apps/courses/models/__init__.py @@ -6,6 +6,7 @@ from .blog import * from .category import * from .course import * +from .index import * from .organization import * from .person import * from .program import * diff --git a/src/richie/apps/courses/models/index.py b/src/richie/apps/courses/models/index.py new file mode 100644 index 0000000000..b6c1714bd6 --- /dev/null +++ b/src/richie/apps/courses/models/index.py @@ -0,0 +1,51 @@ +""" +Declare and configure the models for the index part +""" + +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from cms.extensions.extension_pool import extension_pool + +from ...core.models import BasePageExtension +from .. import defaults + + +class IndexPage(BasePageExtension): + """ + The IndexPage extension defines some options for a page entry in the main menu. + """ + + PAGE = defaults.INDEXES_PAGE + + class Meta: + db_table = "richie_index" + ordering = ["-pk"] + verbose_name = _("index") + verbose_name_plural = _("indexes") + + def __str__(self): + """Human representation of an index page""" + model = self._meta.verbose_name.title() + name = self.extended_object.get_title() + return f"{model:s}: {name:s}" + + allow_submenu = models.BooleanField( + _("Allow submenu"), + default=False, + help_text=_( + "If enabled the page entry in menu will be a dropdown for its possible " + "children." + ), + ) + menu_color = models.CharField( + _("Color in menu"), + max_length=10, + default="", + blank=True, + choices=defaults.INDEX_MENU_COLOR_CLASSES, + help_text=_("A color used to display page in menu"), + ) + + +extension_pool.register(IndexPage) diff --git a/src/richie/apps/courses/settings/__init__.py b/src/richie/apps/courses/settings/__init__.py index 877e0f10b4..8e6e09cfb8 100644 --- a/src/richie/apps/courses/settings/__init__.py +++ b/src/richie/apps/courses/settings/__init__.py @@ -578,3 +578,14 @@ def richie_placeholder_conf(name): "sizes": "60px", }, } + +# If true the toolbar item will already be showed. If false only a page which already +# have the extension will have the toolbar item and users won't be able to add +# pageindex extension on existing page, only create new page with index extension +# through the wizard. +RICHIE_PAGEINDEX_ALLOW_CREATION = False + +# Define which node level can be processed to search for pageindex extension. You can +# set it to 'None' for never processing any node. +# This is a limit against performance issues to avoid making querysets for nothing. +RICHIE_PAGEINDEX_MENU_ALLOWED_LEVEL = 0 diff --git a/tests/apps/courses/test_cms_toolbars.py b/tests/apps/courses/test_cms_toolbars.py index 2bdc03fd0f..7d046e6988 100644 --- a/tests/apps/courses/test_cms_toolbars.py +++ b/tests/apps/courses/test_cms_toolbars.py @@ -14,6 +14,7 @@ from richie.apps.core.factories import UserFactory from richie.apps.courses.factories import ( CourseFactory, + IndexPageFactory, OrganizationFactory, PersonFactory, ) @@ -211,6 +212,114 @@ def test_cms_toolbars_no_page_extension(self): results = page_menu.find_items(ModalItem, name="Person settings...") self.assertEqual(results, []) + # Check that the index page item is absent with default settings + results = page_menu.find_items(ModalItem, name="Index Page settings...") + self.assertEqual(results, []) + + @override_settings(CMS_PERMISSION=False) + def test_cms_toolbars_indexpage_extension_availability(self): + """ + IndexPage extension has advanced toolbar behaviors depending from settings. + """ + # Testing with a superuser proves our point + superuser = UserFactory(is_staff=True, is_superuser=True) + + # Create a page not related to any page extension + page = create_page( + "A page", template="richie/single_column.html", language="en" + ) + # Create a page on level 0 and with existing extension + indexpage = IndexPageFactory(page_parent=page) + + cases = [[False, False], [False, True], [True, False]] + + for args in cases: + # Extension should not be created from toolbar for any level but could be + # edited from tree level 0 + with self.settings( + RICHIE_PAGEINDEX_ALLOW_CREATION=False, + RICHIE_PAGEINDEX_MENU_ALLOWED_LEVEL=0, + ): + toolbar = self.get_toolbar_for_page(page, superuser, *args) + page_menu = toolbar.find_items(Menu, name="Page")[0].item + + # indexpage page entry is present + results = page_menu.find_items(ModalItem, name="Index Page settings...") + self.assertEqual(results, []) + + toolbar = self.get_toolbar_for_page( + indexpage.extended_object, superuser, *args + ) + page_menu = toolbar.find_items(Menu, name="Page")[0].item + + # indexpage page entry is present + results = page_menu.find_items(ModalItem, name="Index Page settings...") + self.assertEqual(results, []) + + # Extension should not be created from toolbar for any level but could be + # edited from tree level 1 + with self.settings( + RICHIE_PAGEINDEX_ALLOW_CREATION=False, + RICHIE_PAGEINDEX_MENU_ALLOWED_LEVEL=1, + ): + toolbar = self.get_toolbar_for_page(page, superuser, *args) + page_menu = toolbar.find_items(Menu, name="Page")[0].item + + # indexpage page entry is present + results = page_menu.find_items(ModalItem, name="Index Page settings...") + self.assertEqual(results, []) + + toolbar = self.get_toolbar_for_page( + indexpage.extended_object, superuser, *args + ) + page_menu = toolbar.find_items(Menu, name="Page")[0].item + + # indexpage page entry is present + results = page_menu.find_items(ModalItem, name="Index Page settings...") + self.assertEqual(len(results), 1) + + # Extension should be created or edited from toolbar for tree level 0 + with self.settings( + RICHIE_PAGEINDEX_ALLOW_CREATION=True, + RICHIE_PAGEINDEX_MENU_ALLOWED_LEVEL=0, + ): + toolbar = self.get_toolbar_for_page(page, superuser, *args) + page_menu = toolbar.find_items(Menu, name="Page")[0].item + + # indexpage page entry is present + results = page_menu.find_items(ModalItem, name="Index Page settings...") + self.assertEqual(len(results), 1) + + toolbar = self.get_toolbar_for_page( + indexpage.extended_object, superuser, *args + ) + page_menu = toolbar.find_items(Menu, name="Page")[0].item + + # indexpage page entry is present + results = page_menu.find_items(ModalItem, name="Index Page settings...") + self.assertEqual(results, []) + + # Extension should be created or edited from toolbar for tree level 1 + with self.settings( + RICHIE_PAGEINDEX_ALLOW_CREATION=True, + RICHIE_PAGEINDEX_MENU_ALLOWED_LEVEL=1, + ): + toolbar = self.get_toolbar_for_page(page, superuser, *args) + page_menu = toolbar.find_items(Menu, name="Page")[0].item + + # indexpage page entry is present + results = page_menu.find_items(ModalItem, name="Index Page settings...") + self.assertEqual(results, []) + + toolbar = self.get_toolbar_for_page( + indexpage.extended_object, superuser, *args + ) + page_menu = toolbar.find_items(Menu, name="Page")[0].item + + # indexpage page entry is present + results = page_menu.find_items(ModalItem, name="Index Page settings...") + self.assertEqual(len(results), 1) + @override_settings(CMS_PERMISSION=False) # pylint: disable=too-many-locals def test_cms_toolbars_person_has_page_extension_settings_item(self): @@ -262,3 +371,18 @@ def test_cms_toolbars_person_has_page_extension_settings_item(self): item = method(toolbar, "Person settings...") if item: self.assertEqual(item.url, url) + + @override_settings(CMS_PERMISSION=False) + def test_cms_toolbars_indexpage_has_page_extension_settings_item(self): + """ + Validate that a new item to edit the organization is available only when visiting the page + in edit mode and for users with permission to edit the page. + """ + indexpage = IndexPageFactory() + url = f"/en/admin/courses/indexpage/{indexpage.id:d}/change/" + + for args, method in self.get_cases_for_page_change(): + toolbar = self.get_toolbar_for_page(indexpage.extended_object, *args) + item = method(toolbar, "Index Page settings...") + if item: + self.assertEqual(item.url, url) From d32b6303e30adf05e93014be87044da847775664 Mon Sep 17 00:00:00 2001 From: David THENON Date: Wed, 18 Sep 2024 13:44:28 +0000 Subject: [PATCH 2/4] [CHG] Finally removed cms wizard for indexpage since it is reported useless and confusing --- src/richie/apps/courses/cms_wizards.py | 70 -------------------------- 1 file changed, 70 deletions(-) diff --git a/src/richie/apps/courses/cms_wizards.py b/src/richie/apps/courses/cms_wizards.py index 5763c1d8c9..063e778a60 100644 --- a/src/richie/apps/courses/cms_wizards.py +++ b/src/richie/apps/courses/cms_wizards.py @@ -577,73 +577,3 @@ def user_has_add_permission(self, user, **kwargs): weight=200, ) ) - - -class IndexPageWizardForm(BaseWizardForm): - """ - Opposed to other extensions, this wizard form allow for a null reverse id since - index pages are commonly at the root of the page tree. - """ - - model = models.IndexPage - - def clean(self): - """ - Bypass ``BasePageExtension.clean`` method in case of null reverse id - since it would lead to an exception. - """ - if self.model.PAGE["reverse_id"]: - cleaned_data = super().clean() - else: - cleaned_data = super(BaseWizardForm, self).clean() - - # If the slug is not explicitly set, generate it from the title - if cleaned_data.get("title") and not cleaned_data.get("slug"): - cleaned_data["slug"] = slugify(cleaned_data["title"])[:200] - - if len(cleaned_data["slug"]) > 255: - raise forms.ValidationError( - {"slug": [_("This slug is too long, it should be less than 255")]} - ) - - if Page.objects.filter(title_set__slug=cleaned_data["slug"]).exists(): - raise forms.ValidationError( - {"slug": [_("This slug is already in use")]} - ) - - return cleaned_data - - @cached_property - def parent_page(self): - """ - By pass ``BasePageExtension.parent_page`` property in case of null reverse id - since it would lead to exception. - """ - if self.model.PAGE["reverse_id"]: - return super().parent_page - - return None - - def save(self): - """ - The parent form from BasePageExtension created the page. - And this method creates the associated index extension. - """ - page = super().save() - models.IndexPage.objects.create(extended_object=page) - return page - - -class IndexPageWizard(Wizard): - """A page wizard to create a page with an IndexPage extension object.""" - - -wizard_pool.register( - IndexPageWizard( - title="New IndexPage", - weight=200, - model=models.IndexPage, - form=IndexPageWizardForm, - description="Create a new IndexPage instance", - ) -) From 3e0f6548e2a434c77e1c22a8b326ba8a01df35fd Mon Sep 17 00:00:00 2001 From: David THENON Date: Wed, 18 Sep 2024 16:12:53 +0000 Subject: [PATCH 3/4] [CHG] Ported new IndexPage settings to the sandbox and the cookiecutter template --- .../src/backend/{{cookiecutter.site}}/settings.py | 7 +++++++ sandbox/settings.py | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/cookiecutter/{{cookiecutter.organization}}-richie-site-factory/template/{{cookiecutter.site}}/src/backend/{{cookiecutter.site}}/settings.py b/cookiecutter/{{cookiecutter.organization}}-richie-site-factory/template/{{cookiecutter.site}}/src/backend/{{cookiecutter.site}}/settings.py index b54d9ce7c2..72899457ee 100644 --- a/cookiecutter/{{cookiecutter.organization}}-richie-site-factory/template/{{cookiecutter.site}}/src/backend/{{cookiecutter.site}}/settings.py +++ b/cookiecutter/{{cookiecutter.organization}}-richie-site-factory/template/{{cookiecutter.site}}/src/backend/{{cookiecutter.site}}/settings.py @@ -659,6 +659,13 @@ class Base(StyleguideMixin, DRFMixin, RichieCoursesConfigurationMixin, Configura "body > svg, #main-menu, .body-footer, .body-mentions" ) + # Wheither you can create PageIndex extension on page through toolbar if true or + # just editing existing extension if false + RICHIE_PAGEINDEX_ALLOW_CREATION = False + + # Define which node level can be processed to search for pageindex extension + RICHIE_PAGEINDEX_MENU_ALLOWED_LEVEL = 0 + # pylint: disable=invalid-name @property def ENVIRONMENT(self): diff --git a/sandbox/settings.py b/sandbox/settings.py index b584e5a304..b939819f16 100644 --- a/sandbox/settings.py +++ b/sandbox/settings.py @@ -598,6 +598,13 @@ class Base(StyleguideMixin, DRFMixin, RichieCoursesConfigurationMixin, Configura environ_prefix=None, ) + # Wheither you can create PageIndex extension on page through toolbar if true or + # just editing existing extension if false + RICHIE_PAGEINDEX_ALLOW_CREATION = False + + # Define which node level can be processed to search for pageindex extension + RICHIE_PAGEINDEX_MENU_ALLOWED_LEVEL = 0 + @classmethod def _get_environment(cls): """Environment in which the application is launched.""" From 0b8c545b38e5e3aef72a8cf15ed384b2fc16d0a2 Mon Sep 17 00:00:00 2001 From: David THENON Date: Thu, 19 Sep 2024 01:24:06 +0000 Subject: [PATCH 4/4] [CHG] Renamed model 'IndexPage' to 'MainMenuEntry' and adapted every other mentions --- CHANGELOG.md | 1 + Makefile | 2 +- .../backend/{{cookiecutter.site}}/settings.py | 4 +- sandbox/settings.py | 4 +- src/richie/apps/courses/admin.py | 6 +- src/richie/apps/courses/cms_menus.py | 20 ++--- src/richie/apps/courses/cms_toolbars.py | 20 ++--- src/richie/apps/courses/defaults.py | 20 ++--- src/richie/apps/courses/factories.py | 8 +- ...add_indexpage.py => 0035_add_menuentry.py} | 18 ++--- src/richie/apps/courses/models/__init__.py | 2 +- .../courses/models/{index.py => menuentry.py} | 22 ++--- src/richie/apps/courses/settings/__init__.py | 10 +-- tests/apps/courses/test_cms_toolbars.py | 80 +++++++++---------- 14 files changed, 104 insertions(+), 113 deletions(-) rename src/richie/apps/courses/migrations/{0035_add_indexpage.py => 0035_add_menuentry.py} (83%) rename src/richie/apps/courses/models/{index.py => menuentry.py} (58%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ec5249932..6b658341f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Versioning](https://semver.org/spec/v2.0.0.html). ### Added +- Added new page extension `MainMenuEntry`. - Grid options for the Section plugin - Make user waive its right of withdrawal when purchasing a course product relation with `is_withdrawable` set to `false` diff --git a/Makefile b/Makefile index 3402c88b8a..1b999397c5 100644 --- a/Makefile +++ b/Makefile @@ -269,7 +269,7 @@ superuser: ## Create an admin user with password "admin" test-back: ## run back-end tests, or specific test like `make test-back tests/apps/core/test_web_analytics.py` @args="$(filter-out $@,$(MAKECMDGOALS))" && \ - DB_PORT=$(DB_PORT) bin/pytest --reuse-db $${args:-${1}} + DB_PORT=$(DB_PORT) bin/pytest $${args:-${1}} .PHONY: test-back # -- Internationalization diff --git a/cookiecutter/{{cookiecutter.organization}}-richie-site-factory/template/{{cookiecutter.site}}/src/backend/{{cookiecutter.site}}/settings.py b/cookiecutter/{{cookiecutter.organization}}-richie-site-factory/template/{{cookiecutter.site}}/src/backend/{{cookiecutter.site}}/settings.py index 72899457ee..518a0fff2b 100644 --- a/cookiecutter/{{cookiecutter.organization}}-richie-site-factory/template/{{cookiecutter.site}}/src/backend/{{cookiecutter.site}}/settings.py +++ b/cookiecutter/{{cookiecutter.organization}}-richie-site-factory/template/{{cookiecutter.site}}/src/backend/{{cookiecutter.site}}/settings.py @@ -661,10 +661,10 @@ class Base(StyleguideMixin, DRFMixin, RichieCoursesConfigurationMixin, Configura # Wheither you can create PageIndex extension on page through toolbar if true or # just editing existing extension if false - RICHIE_PAGEINDEX_ALLOW_CREATION = False + RICHIE_MAINMENUENTRY_ALLOW_CREATION = False # Define which node level can be processed to search for pageindex extension - RICHIE_PAGEINDEX_MENU_ALLOWED_LEVEL = 0 + RICHIE_MAINMENUENTRY_MENU_ALLOWED_LEVEL = 0 # pylint: disable=invalid-name @property diff --git a/sandbox/settings.py b/sandbox/settings.py index b939819f16..787e3edd07 100644 --- a/sandbox/settings.py +++ b/sandbox/settings.py @@ -600,10 +600,10 @@ class Base(StyleguideMixin, DRFMixin, RichieCoursesConfigurationMixin, Configura # Wheither you can create PageIndex extension on page through toolbar if true or # just editing existing extension if false - RICHIE_PAGEINDEX_ALLOW_CREATION = False + RICHIE_MAINMENUENTRY_ALLOW_CREATION = False # Define which node level can be processed to search for pageindex extension - RICHIE_PAGEINDEX_MENU_ALLOWED_LEVEL = 0 + RICHIE_MAINMENUENTRY_MENU_ALLOWED_LEVEL = 0 @classmethod def _get_environment(cls): diff --git a/src/richie/apps/courses/admin.py b/src/richie/apps/courses/admin.py index 41b80d8f24..4b7c3029ba 100644 --- a/src/richie/apps/courses/admin.py +++ b/src/richie/apps/courses/admin.py @@ -280,9 +280,9 @@ def snapshot(self, request, course_id, *args, **kwargs): return JsonResponse({"id": new_page.course.id}) -class IndexPageAdmin(PageExtensionAdmin): +class MainMenuEntryAdmin(PageExtensionAdmin): """ - Admin class for the IndexPage model + Admin class for the MainMenuEntry model """ list_display = ["title", "allow_submenu"] @@ -364,7 +364,7 @@ class LicenceAdmin(TranslatableAdmin): admin.site.register(models.Course, CourseAdmin) admin.site.register(models.CourseRun, CourseRunAdmin) admin.site.register(models.Licence, LicenceAdmin) -admin.site.register(models.IndexPage, IndexPageAdmin) +admin.site.register(models.MainMenuEntry, MainMenuEntryAdmin) admin.site.register(models.Organization, OrganizationAdmin) admin.site.register(models.PageRole, PageRoleAdmin) admin.site.register(models.Person, PersonAdmin) diff --git a/src/richie/apps/courses/cms_menus.py b/src/richie/apps/courses/cms_menus.py index 9a8d19f311..37c5d0a66f 100644 --- a/src/richie/apps/courses/cms_menus.py +++ b/src/richie/apps/courses/cms_menus.py @@ -7,24 +7,24 @@ from menus.base import Modifier from menus.menu_pool import menu_pool -from .models import IndexPage +from .models import MainMenuEntry -class MenuWithIndexPage(Modifier): +class MenuWithMainMenuEntry(Modifier): """ - Menu modifier to include IndexPage extension data in menu template context. + Menu modifier to include MainMenuEntry extension data in menu template context. In menu template you will be able to reach possible extension data from node attribute ``menu_extension``. If node page has no extension it will have an empty - dict. Only a specific node level is processedn nodes with a different level won't - have the attribute ``menu_extension`` at all. + dict. Only a specific node level is processed and nodes with a different level + won't have the attribute ``menu_extension`` at all. """ - # pylint: disable=too-many-arguments + # pylint: disable=too-many-arguments,too-many-positional-arguments def modify(self, request, nodes, namespace, root_id, post_cut, breadcrumb): """ Patch navigation nodes to include data from possible extension - ``IndexPage``. + ``MainMenuEntry``. For performance: @@ -47,7 +47,7 @@ def modify(self, request, nodes, namespace, root_id, post_cut, breadcrumb): page_ids = [ node.id for node in nodes - if node.level == settings.RICHIE_PAGEINDEX_MENU_ALLOWED_LEVEL + if node.level == settings.RICHIE_MAINMENUENTRY_MENU_ALLOWED_LEVEL ] # No need to continue if we don't have any valid node @@ -56,7 +56,7 @@ def modify(self, request, nodes, namespace, root_id, post_cut, breadcrumb): # We directly get the extensions from their related page id and serialized # as a dict instead of model object - extension_queryset = IndexPage.objects.filter( + extension_queryset = MainMenuEntry.objects.filter( extended_object_id__in=page_ids ).values("extended_object_id", "allow_submenu", "menu_color") @@ -76,4 +76,4 @@ def modify(self, request, nodes, namespace, root_id, post_cut, breadcrumb): return nodes -menu_pool.register_modifier(MenuWithIndexPage) +menu_pool.register_modifier(MenuWithMainMenuEntry) diff --git a/src/richie/apps/courses/cms_toolbars.py b/src/richie/apps/courses/cms_toolbars.py index 1acbbcbc6f..1797282c18 100644 --- a/src/richie/apps/courses/cms_toolbars.py +++ b/src/richie/apps/courses/cms_toolbars.py @@ -13,7 +13,7 @@ from cms.utils.urlutils import admin_reverse from .defaults import PAGE_EXTENSION_TOOLBAR_ITEM_POSITION -from .models import Category, Course, IndexPage, Organization, Person +from .models import Category, Course, MainMenuEntry, Organization, Person class BaseExtensionToolbar(ExtensionToolbar): @@ -135,21 +135,21 @@ class PersonExtensionToolbar(BaseExtensionToolbar): @toolbar_pool.register -class IndexPageExtensionToolbar(BaseExtensionToolbar): +class MainMenuEntryExtensionToolbar(BaseExtensionToolbar): """ - This extension class customizes the toolbar for the IndexPage page extension. + This extension class customizes the toolbar for the MainMenuEntry page extension. """ - model = IndexPage + model = MainMenuEntry def populate(self): """ Specific extension populate method. This extension entry only appears in toolbar if page already have extension or - if setting ``RICHIE_PAGEINDEX_ALLOW_CREATION`` is true. Finally the page level - must also match the allowed level from setting - ``RICHIE_PAGEINDEX_MENU_ALLOWED_LEVEL``. + if setting ``RICHIE_MAINMENUENTRY_ALLOW_CREATION`` is true. Finally the page + level must also match the allowed level from setting + ``RICHIE_MAINMENUENTRY_MENU_ALLOWED_LEVEL``. """ # always use draft if we have a page self.page = get_page_draft(self.request.current_page) @@ -167,16 +167,16 @@ def populate(self): level = self.page.node.get_depth() - 1 allowed = page_extension is not None or ( page_extension is None - and settings.RICHIE_PAGEINDEX_ALLOW_CREATION is True + and settings.RICHIE_MAINMENUENTRY_ALLOW_CREATION is True ) if ( allowed - and level == settings.RICHIE_PAGEINDEX_MENU_ALLOWED_LEVEL + and level == settings.RICHIE_MAINMENUENTRY_MENU_ALLOWED_LEVEL and admin_url ): # Adds a toolbar item in position 0 (at the top of the menu) page_menu.add_modal_item( - _("Index Page settings"), + _("Main menu settings"), url=admin_url, disabled=not self.toolbar.edit_mode_active, position=PAGE_EXTENSION_TOOLBAR_ITEM_POSITION, diff --git a/src/richie/apps/courses/defaults.py b/src/richie/apps/courses/defaults.py index 1881849cc7..ae542ac420 100644 --- a/src/richie/apps/courses/defaults.py +++ b/src/richie/apps/courses/defaults.py @@ -312,7 +312,7 @@ "reverse_id": "organizations", "template": "courses/cms/organization_detail.html", } -INDEXES_PAGE = { +MENUENTRIES_PAGE = { "reverse_id": None, "template": "richie/single_column.html", } @@ -344,11 +344,6 @@ "in_navigation": True, "template": "search/search.html", }, - INDEXES_PAGE["reverse_id"]: { - "title": "Indexes", - "in_navigation": True, - "template": "richie/single_column.html", - }, ORGANIZATIONS_PAGE["reverse_id"]: { "title": "Organizations", "in_navigation": True, @@ -392,15 +387,10 @@ # The additional runs can be viewed by clicking on `View more` link. RICHIE_MAX_ARCHIVED_COURSE_RUNS = 10 -# Define possible hover color that can be choosen for an IndexPage and to apply on +# Define possible hover color that can be choosen for an MainMenuEntry and to apply on # its menu item -INDEX_MENU_COLOR_CLASSES = getattr( +MENU_ENTRY_COLOR_CLASSES = getattr( settings, - "RICHIE_INDEX_MENU_COLOR_CLASSES", - # (("", "None"),) - ( - ("", "None"), - ("primary", "Primary"), - ("warning", "Warning"), - ), + "RICHIE_MENU_ENTRY_COLOR_CLASSES", + (("", _("None")),), ) diff --git a/src/richie/apps/courses/factories.py b/src/richie/apps/courses/factories.py index a1fc0ef204..d428151442 100644 --- a/src/richie/apps/courses/factories.py +++ b/src/richie/apps/courses/factories.py @@ -912,14 +912,14 @@ def fill_excerpt(self, create, extracted, **kwargs): ) -class IndexPageFactory(BLDPageExtensionDjangoModelFactory): +class MainMenuEntryFactory(BLDPageExtensionDjangoModelFactory): """ - A factory to automatically generate random yet meaningful index page extensions + A factory to automatically generate random yet meaningful menu entry page extensions and their related page in our tests. """ class Meta: - model = models.IndexPage + model = models.MainMenuEntry exclude = [ "page_in_navigation", "page_languages", @@ -930,6 +930,6 @@ class Meta: ] # fields concerning the related page - page_template = models.IndexPage.PAGE["template"] + page_template = models.MainMenuEntry.PAGE["template"] allow_submenu = False menu_color = "" diff --git a/src/richie/apps/courses/migrations/0035_add_indexpage.py b/src/richie/apps/courses/migrations/0035_add_menuentry.py similarity index 83% rename from src/richie/apps/courses/migrations/0035_add_indexpage.py rename to src/richie/apps/courses/migrations/0035_add_menuentry.py index 9c68ac1b65..2f3b8eec5b 100644 --- a/src/richie/apps/courses/migrations/0035_add_indexpage.py +++ b/src/richie/apps/courses/migrations/0035_add_menuentry.py @@ -1,9 +1,9 @@ -# Generated by Django 4.2.14 on 2024-09-09 15:06 +# Generated by Django 4.2.16 on 2024-09-19 00:24 import django.db.models.deletion from django.db import migrations, models -from ..defaults import INDEX_MENU_COLOR_CLASSES +from ..defaults import MENU_ENTRY_COLOR_CLASSES class Migration(migrations.Migration): @@ -15,7 +15,7 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name="IndexPage", + name="MainMenuEntry", fields=[ ( "id", @@ -38,9 +38,9 @@ class Migration(migrations.Migration): "menu_color", models.CharField( blank=True, - choices=INDEX_MENU_COLOR_CLASSES, + choices=MENU_ENTRY_COLOR_CLASSES, default="", - help_text="A color used to display page in menu", + help_text="A color used to display page entry in menu.", max_length=10, verbose_name="Color in menu", ), @@ -60,14 +60,14 @@ class Migration(migrations.Migration): null=True, on_delete=django.db.models.deletion.CASCADE, related_name="draft_extension", - to="courses.indexpage", + to="courses.mainmenuentry", ), ), ], options={ - "verbose_name": "index", - "verbose_name_plural": "indexes", - "db_table": "richie_index", + "verbose_name": "main menu entry", + "verbose_name_plural": "main menu entries", + "db_table": "richie_menuentry", "ordering": ["-pk"], }, ), diff --git a/src/richie/apps/courses/models/__init__.py b/src/richie/apps/courses/models/__init__.py index dc237ef91a..9edc58aa4f 100644 --- a/src/richie/apps/courses/models/__init__.py +++ b/src/richie/apps/courses/models/__init__.py @@ -6,7 +6,7 @@ from .blog import * from .category import * from .course import * -from .index import * +from .menuentry import * from .organization import * from .person import * from .program import * diff --git a/src/richie/apps/courses/models/index.py b/src/richie/apps/courses/models/menuentry.py similarity index 58% rename from src/richie/apps/courses/models/index.py rename to src/richie/apps/courses/models/menuentry.py index b6c1714bd6..cdbccf2ece 100644 --- a/src/richie/apps/courses/models/index.py +++ b/src/richie/apps/courses/models/menuentry.py @@ -1,5 +1,5 @@ """ -Declare and configure the models for the index part +Declare and configure the models for the menu entry part """ from django.db import models @@ -11,21 +11,21 @@ from .. import defaults -class IndexPage(BasePageExtension): +class MainMenuEntry(BasePageExtension): """ - The IndexPage extension defines some options for a page entry in the main menu. + The MainMenuEntry extension defines some options for a page entry in the main menu. """ - PAGE = defaults.INDEXES_PAGE + PAGE = defaults.MENUENTRIES_PAGE class Meta: - db_table = "richie_index" + db_table = "richie_menuentry" ordering = ["-pk"] - verbose_name = _("index") - verbose_name_plural = _("indexes") + verbose_name = _("main menu entry") + verbose_name_plural = _("main menu entries") def __str__(self): - """Human representation of an index page""" + """Human representation of an main menu entry page""" model = self._meta.verbose_name.title() name = self.extended_object.get_title() return f"{model:s}: {name:s}" @@ -43,9 +43,9 @@ def __str__(self): max_length=10, default="", blank=True, - choices=defaults.INDEX_MENU_COLOR_CLASSES, - help_text=_("A color used to display page in menu"), + choices=defaults.MENU_ENTRY_COLOR_CLASSES, + help_text=_("A color used to display page entry in menu."), ) -extension_pool.register(IndexPage) +extension_pool.register(MainMenuEntry) diff --git a/src/richie/apps/courses/settings/__init__.py b/src/richie/apps/courses/settings/__init__.py index 8e6e09cfb8..d9203c74e2 100644 --- a/src/richie/apps/courses/settings/__init__.py +++ b/src/richie/apps/courses/settings/__init__.py @@ -581,11 +581,11 @@ def richie_placeholder_conf(name): # If true the toolbar item will already be showed. If false only a page which already # have the extension will have the toolbar item and users won't be able to add -# pageindex extension on existing page, only create new page with index extension +# MainMenuEntry extension on existing page, only create new page with index extension # through the wizard. -RICHIE_PAGEINDEX_ALLOW_CREATION = False +RICHIE_MAINMENUENTRY_ALLOW_CREATION = False -# Define which node level can be processed to search for pageindex extension. You can -# set it to 'None' for never processing any node. +# Define which node level can be processed to search for MainMenuEntry extension. You +# can set it to 'None' for never processing any node. # This is a limit against performance issues to avoid making querysets for nothing. -RICHIE_PAGEINDEX_MENU_ALLOWED_LEVEL = 0 +RICHIE_MAINMENUENTRY_MENU_ALLOWED_LEVEL = 0 diff --git a/tests/apps/courses/test_cms_toolbars.py b/tests/apps/courses/test_cms_toolbars.py index 7d046e6988..b08fc411a7 100644 --- a/tests/apps/courses/test_cms_toolbars.py +++ b/tests/apps/courses/test_cms_toolbars.py @@ -14,7 +14,7 @@ from richie.apps.core.factories import UserFactory from richie.apps.courses.factories import ( CourseFactory, - IndexPageFactory, + MainMenuEntryFactory, OrganizationFactory, PersonFactory, ) @@ -213,13 +213,13 @@ def test_cms_toolbars_no_page_extension(self): self.assertEqual(results, []) # Check that the index page item is absent with default settings - results = page_menu.find_items(ModalItem, name="Index Page settings...") + results = page_menu.find_items(ModalItem, name="Main menu settings...") self.assertEqual(results, []) @override_settings(CMS_PERMISSION=False) - def test_cms_toolbars_indexpage_extension_availability(self): + def test_cms_toolbars_menuentry_extension_availability(self): """ - IndexPage extension has advanced toolbar behaviors depending from settings. + MainMenuEntry extension has advanced toolbar behaviors depending from settings. """ # Testing with a superuser proves our point superuser = UserFactory(is_staff=True, is_superuser=True) @@ -229,7 +229,7 @@ def test_cms_toolbars_indexpage_extension_availability(self): "A page", template="richie/single_column.html", language="en" ) # Create a page on level 0 and with existing extension - indexpage = IndexPageFactory(page_parent=page) + menuentry = MainMenuEntryFactory(page_parent=page) cases = [[False, False], [False, True], [True, False]] @@ -237,87 +237,87 @@ def test_cms_toolbars_indexpage_extension_availability(self): # Extension should not be created from toolbar for any level but could be # edited from tree level 0 with self.settings( - RICHIE_PAGEINDEX_ALLOW_CREATION=False, - RICHIE_PAGEINDEX_MENU_ALLOWED_LEVEL=0, + RICHIE_MAINMENUENTRY_ALLOW_CREATION=False, + RICHIE_MAINMENUENTRY_MENU_ALLOWED_LEVEL=0, ): toolbar = self.get_toolbar_for_page(page, superuser, *args) page_menu = toolbar.find_items(Menu, name="Page")[0].item - # indexpage page entry is present - results = page_menu.find_items(ModalItem, name="Index Page settings...") + # menuentry page entry is present + results = page_menu.find_items(ModalItem, name="Main menu settings...") self.assertEqual(results, []) toolbar = self.get_toolbar_for_page( - indexpage.extended_object, superuser, *args + menuentry.extended_object, superuser, *args ) page_menu = toolbar.find_items(Menu, name="Page")[0].item - # indexpage page entry is present - results = page_menu.find_items(ModalItem, name="Index Page settings...") + # menuentry page entry is present + results = page_menu.find_items(ModalItem, name="Main menu settings...") self.assertEqual(results, []) # Extension should not be created from toolbar for any level but could be # edited from tree level 1 with self.settings( - RICHIE_PAGEINDEX_ALLOW_CREATION=False, - RICHIE_PAGEINDEX_MENU_ALLOWED_LEVEL=1, + RICHIE_MAINMENUENTRY_ALLOW_CREATION=False, + RICHIE_MAINMENUENTRY_MENU_ALLOWED_LEVEL=1, ): toolbar = self.get_toolbar_for_page(page, superuser, *args) page_menu = toolbar.find_items(Menu, name="Page")[0].item - # indexpage page entry is present - results = page_menu.find_items(ModalItem, name="Index Page settings...") + # menuentry page entry is present + results = page_menu.find_items(ModalItem, name="Main menu settings...") self.assertEqual(results, []) toolbar = self.get_toolbar_for_page( - indexpage.extended_object, superuser, *args + menuentry.extended_object, superuser, *args ) page_menu = toolbar.find_items(Menu, name="Page")[0].item - # indexpage page entry is present - results = page_menu.find_items(ModalItem, name="Index Page settings...") + # menuentry page entry is present + results = page_menu.find_items(ModalItem, name="Main menu settings...") self.assertEqual(len(results), 1) # Extension should be created or edited from toolbar for tree level 0 with self.settings( - RICHIE_PAGEINDEX_ALLOW_CREATION=True, - RICHIE_PAGEINDEX_MENU_ALLOWED_LEVEL=0, + RICHIE_MAINMENUENTRY_ALLOW_CREATION=True, + RICHIE_MAINMENUENTRY_MENU_ALLOWED_LEVEL=0, ): toolbar = self.get_toolbar_for_page(page, superuser, *args) page_menu = toolbar.find_items(Menu, name="Page")[0].item - # indexpage page entry is present - results = page_menu.find_items(ModalItem, name="Index Page settings...") + # menuentry page entry is present + results = page_menu.find_items(ModalItem, name="Main menu settings...") self.assertEqual(len(results), 1) toolbar = self.get_toolbar_for_page( - indexpage.extended_object, superuser, *args + menuentry.extended_object, superuser, *args ) page_menu = toolbar.find_items(Menu, name="Page")[0].item - # indexpage page entry is present - results = page_menu.find_items(ModalItem, name="Index Page settings...") + # menuentry page entry is present + results = page_menu.find_items(ModalItem, name="Main menu settings...") self.assertEqual(results, []) # Extension should be created or edited from toolbar for tree level 1 with self.settings( - RICHIE_PAGEINDEX_ALLOW_CREATION=True, - RICHIE_PAGEINDEX_MENU_ALLOWED_LEVEL=1, + RICHIE_MAINMENUENTRY_ALLOW_CREATION=True, + RICHIE_MAINMENUENTRY_MENU_ALLOWED_LEVEL=1, ): toolbar = self.get_toolbar_for_page(page, superuser, *args) page_menu = toolbar.find_items(Menu, name="Page")[0].item - # indexpage page entry is present - results = page_menu.find_items(ModalItem, name="Index Page settings...") + # menuentry page entry is present + results = page_menu.find_items(ModalItem, name="Main menu settings...") self.assertEqual(results, []) toolbar = self.get_toolbar_for_page( - indexpage.extended_object, superuser, *args + menuentry.extended_object, superuser, *args ) page_menu = toolbar.find_items(Menu, name="Page")[0].item - # indexpage page entry is present - results = page_menu.find_items(ModalItem, name="Index Page settings...") + # menuentry page entry is present + results = page_menu.find_items(ModalItem, name="Main menu settings...") self.assertEqual(len(results), 1) @override_settings(CMS_PERMISSION=False) @@ -373,16 +373,16 @@ def test_cms_toolbars_person_has_page_extension_settings_item(self): self.assertEqual(item.url, url) @override_settings(CMS_PERMISSION=False) - def test_cms_toolbars_indexpage_has_page_extension_settings_item(self): + def test_cms_toolbars_menuentry_has_page_extension_settings_item(self): """ - Validate that a new item to edit the organization is available only when visiting the page - in edit mode and for users with permission to edit the page. + Validate that a new item to edit the menu entry is available only when + visiting the page in edit mode and for users with permission to edit the page. """ - indexpage = IndexPageFactory() - url = f"/en/admin/courses/indexpage/{indexpage.id:d}/change/" + menuentry = MainMenuEntryFactory() + url = f"/en/admin/courses/mainmenuentry/{menuentry.id:d}/change/" for args, method in self.get_cases_for_page_change(): - toolbar = self.get_toolbar_for_page(indexpage.extended_object, *args) - item = method(toolbar, "Index Page settings...") + toolbar = self.get_toolbar_for_page(menuentry.extended_object, *args) + item = method(toolbar, "Main menu settings...") if item: self.assertEqual(item.url, url)