From bb9eab24c3f7a059861e231e728876f668c2dbe6 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Wed, 29 Nov 2023 14:24:25 +0100 Subject: [PATCH 01/10] Fix: page content extension toolbar naming and use latest_content filter --- cms/extensions/toolbar.py | 41 +++++++++++++++++++++++------------- cms/tests/test_extensions.py | 2 +- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/cms/extensions/toolbar.py b/cms/extensions/toolbar.py index 62deb766052..0644d14eb8b 100644 --- a/cms/extensions/toolbar.py +++ b/cms/extensions/toolbar.py @@ -1,3 +1,5 @@ +import warnings + from django.urls import NoReverseMatch from cms.toolbar_base import CMSToolbar @@ -59,40 +61,49 @@ def get_page_extension_admin(self): return page_extension, admin_url def get_title_extension_admin(self, language=None): + warnings.warn( + "get_title_extension_admin has been renamed to get_page_content_extension_admin and is deprecated", + DeprecationWarning, stacklevel=2, + ) + return self.get_page_content_extension_admin(language) + + def get_page_content_extension_admin(self, language=None): """ - Get the admin urls for the title extensions menu items, depending on whether a TitleExtension instance exists - for each PageContent in the current page. - A single language can be passed to only work on a single title. + Get the admin urls for the page content extensions menu items, depending on whether a + :class:`~cms.extensions.models.PageContentExtension` instance exists for each + :class:`~cms.models.contentmodels.PageContent` in the current page. + A single language can be passed to only work on a single page content object. - Return a list of tuples of the title extension and the url; the extension is None if no instance exists, - the url is None is no admin is registered for the extension. + Return a list of tuples of the page content extension and the url; the extension is None + if no instance exists, the url is None is no admin is registered for the extension. """ page = self._get_page() urls = [] + page_contents = page.pagecontent_set(manager="admin_manager").latest_content() if language: - titles = page.get_content_obj(language), + page_contents = page_contents.filter(language=language) else: - titles = page.pagecontent_set.filter(language__in=get_language_list(page.node.site_id)) - # Titles - for title in titles: + page_contents = page_contents.filter(language__in=get_language_list(page.node.site_id)) + # PageContent + for page_content in page_contents: try: - title_extension = self.model.objects.get(extended_object_id=title.pk) + pagecontent_extension = self.model.objects.get(extended_object_id=page_content.pk) except self.model.DoesNotExist: - title_extension = None + pagecontent_extension = None try: model_name = self.model.__name__.lower() - if title_extension: + if pagecontent_extension: admin_url = admin_reverse( '%s_%s_change' % (self.model._meta.app_label, model_name), - args=(title_extension.pk,)) + args=(pagecontent_extension.pk,)) else: admin_url = "%s?extended_object=%s" % ( admin_reverse('%s_%s_add' % (self.model._meta.app_label, model_name)), - title.pk) + page_content.pk) except NoReverseMatch: # pragma: no cover admin_url = None if admin_url: - urls.append((title_extension, admin_url)) + urls.append((pagecontent_extension, admin_url)) return urls def _get_sub_menu(self, current_menu, key, label, position=None): diff --git a/cms/tests/test_extensions.py b/cms/tests/test_extensions.py index 121b02f9b9f..56c74067264 100644 --- a/cms/tests/test_extensions.py +++ b/cms/tests/test_extensions.py @@ -407,7 +407,7 @@ def populate(self): current_page_menu = self._setup_extension_toolbar() if current_page_menu: position = 0 - urls = self.get_title_extension_admin() + urls = self.get_page_content_extension_admin() for title_extension, url in urls: current_page_menu.add_modal_item( 'TestItem', From c58fa7358b820c02845e6ef4ac17e90d0194d261 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Wed, 29 Nov 2023 14:32:22 +0100 Subject: [PATCH 02/10] Align page content extension menu with shown page content --- cms/admin/pageadmin.py | 4 ++-- cms/extensions/toolbar.py | 14 +++++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/cms/admin/pageadmin.py b/cms/admin/pageadmin.py index 9adb9225873..652652a1109 100644 --- a/cms/admin/pageadmin.py +++ b/cms/admin/pageadmin.py @@ -372,10 +372,10 @@ def delete_view(self, request, object_id, extra_context=None): ) # This is bad and I should feel bad. - if 'placeholder' in perms_needed: + if _('placeholder') in perms_needed: perms_needed.remove('placeholder') - if 'page content' in perms_needed: + if _('page content') in perms_needed: perms_needed.remove('page content') if request.POST and not protected: # The user has confirmed the deletion. diff --git a/cms/extensions/toolbar.py b/cms/extensions/toolbar.py index 0644d14eb8b..41222d17c15 100644 --- a/cms/extensions/toolbar.py +++ b/cms/extensions/toolbar.py @@ -2,6 +2,7 @@ from django.urls import NoReverseMatch +from cms.models import PageContent from cms.toolbar_base import CMSToolbar from cms.utils import get_language_list from cms.utils.page_permissions import user_can_change_page @@ -11,6 +12,7 @@ class ExtensionToolbar(CMSToolbar): model = None page = None + page_content = None def _setup_extension_toolbar(self): """ @@ -29,7 +31,13 @@ def _setup_extension_toolbar(self): def _get_page(self): if not self.page: - self.page = self.request.current_page + if self.toolbar.obj and isinstance(self.toolbar.obj, PageContent): + self.page = self.toolbar.obj.page + self.page_content = self.toolbar.obj + else: + self.page = self.request.current_page + self.page_content = self.page.get_content_obj(self.current_lang) + return self.page def get_page_extension_admin(self): @@ -79,10 +87,10 @@ def get_page_content_extension_admin(self, language=None): """ page = self._get_page() urls = [] - page_contents = page.pagecontent_set(manager="admin_manager").latest_content() if language: - page_contents = page_contents.filter(language=language) + page_contents = self.page_content, else: + page_contents = page.pagecontent_set(manager="admin_manager").latest_content() page_contents = page_contents.filter(language__in=get_language_list(page.node.site_id)) # PageContent for page_content in page_contents: From 1304ae9576280f2f4e3fd68139046e8c59600e05 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Wed, 29 Nov 2023 15:18:36 +0100 Subject: [PATCH 03/10] Deprecate the use for more than one page content object --- cms/extensions/toolbar.py | 71 +++++++++++++++++++++--------------- cms/tests/test_extensions.py | 40 +++++++++++++++----- 2 files changed, 72 insertions(+), 39 deletions(-) diff --git a/cms/extensions/toolbar.py b/cms/extensions/toolbar.py index 41222d17c15..5ea052f0e43 100644 --- a/cms/extensions/toolbar.py +++ b/cms/extensions/toolbar.py @@ -69,13 +69,6 @@ def get_page_extension_admin(self): return page_extension, admin_url def get_title_extension_admin(self, language=None): - warnings.warn( - "get_title_extension_admin has been renamed to get_page_content_extension_admin and is deprecated", - DeprecationWarning, stacklevel=2, - ) - return self.get_page_content_extension_admin(language) - - def get_page_content_extension_admin(self, language=None): """ Get the admin urls for the page content extensions menu items, depending on whether a :class:`~cms.extensions.models.PageContentExtension` instance exists for each @@ -85,35 +78,53 @@ def get_page_content_extension_admin(self, language=None): Return a list of tuples of the page content extension and the url; the extension is None if no instance exists, the url is None is no admin is registered for the extension. """ + warnings.warn( + "get_title_extension_admin has been deprecated and replaced by get_page_content_extension_admin", + DeprecationWarning, stacklevel=2, + ) page = self._get_page() + + page_contents = page.pagecontent_set(manager="admin_manager").latest_content()\ + .filter(language__in=get_language_list(page.node.site_id)) urls = [] - if language: - page_contents = self.page_content, - else: - page_contents = page.pagecontent_set(manager="admin_manager").latest_content() - page_contents = page_contents.filter(language__in=get_language_list(page.node.site_id)) - # PageContent + for page_content in page_contents: - try: - pagecontent_extension = self.model.objects.get(extended_object_id=page_content.pk) - except self.model.DoesNotExist: - pagecontent_extension = None - try: - model_name = self.model.__name__.lower() - if pagecontent_extension: - admin_url = admin_reverse( - '%s_%s_change' % (self.model._meta.app_label, model_name), - args=(pagecontent_extension.pk,)) - else: - admin_url = "%s?extended_object=%s" % ( - admin_reverse('%s_%s_add' % (self.model._meta.app_label, model_name)), - page_content.pk) - except NoReverseMatch: # pragma: no cover - admin_url = None + admin_url = self.get_page_content_extension_admin(page_content) if admin_url: - urls.append((pagecontent_extension, admin_url)) + urls.append(admin_url) return urls + return self.get_page_content_extension_admin(language) + + def get_page_content_extension_admin(self, page_content_obj=None): + """ + Get the admin url for the page content extensions menu item, depending on whether a + :class:`~cms.extensions.models.PageContentExtension` instance exists for the + :class:`~cms.models.contentmodels.PageContent` displayed. + + Return a tuple of the page content extension and the url; the extension is None + if no instance exists, the url is None is no admin is registered for the extension. + """ + page = self._get_page() + page_content = page_content_obj or self.page_content + try: + pagecontent_extension = self.model.objects.get(extended_object_id=page_content.pk) + except self.model.DoesNotExist: + pagecontent_extension = None + try: + model_name = self.model.__name__.lower() + if pagecontent_extension: + admin_url = admin_reverse( + '%s_%s_change' % (self.model._meta.app_label, model_name), + args=(pagecontent_extension.pk,)) + else: + admin_url = "%s?extended_object=%s" % ( + admin_reverse('%s_%s_add' % (self.model._meta.app_label, model_name)), + page_content.pk) + except NoReverseMatch: # pragma: no cover + admin_url = None + return pagecontent_extension, admin_url + def _get_sub_menu(self, current_menu, key, label, position=None): """ Utility function to get a submenu of the current menu diff --git a/cms/tests/test_extensions.py b/cms/tests/test_extensions.py index 56c74067264..91def2d7c8b 100644 --- a/cms/tests/test_extensions.py +++ b/cms/tests/test_extensions.py @@ -397,7 +397,7 @@ def populate(self): self.assertIn("TestItem", response.rendered_content) toolbar_pool.toolbars = old_toolbars - def test_toolbar_title_extension(self): + def test_toolbar_page_content_extension(self): old_toolbars = deepcopy(toolbar_pool.toolbars) class SampleExtension(ExtensionToolbar): @@ -407,20 +407,42 @@ def populate(self): current_page_menu = self._setup_extension_toolbar() if current_page_menu: position = 0 - urls = self.get_page_content_extension_admin() - for title_extension, url in urls: - current_page_menu.add_modal_item( - 'TestItem', - url=url, - disabled=not self.toolbar.edit_mode_active, - position=position - ) + pagecontent_extension, url = self.get_page_content_extension_admin() + current_page_menu.add_modal_item( + 'TestItem', + url=url, + disabled=not self.toolbar.edit_mode_active, + position=position + ) toolbar_pool.register(SampleExtension) with self.login_user_context(self.admin): response = self.client.get('{}?edit'.format(self.page.get_absolute_url())) self.assertIn("TestItem", response.rendered_content) toolbar_pool.toolbars = old_toolbars + def test_deprecated_title_extension(self): + urls = [] + old_toolbars = deepcopy(toolbar_pool.toolbars) + + class SampleExtensionToolbar2(ExtensionToolbar): + model = MyPageContentExtension + def populate(self): + nonlocal urls + urls = self.get_title_extension_admin() + + toolbar_pool.register(SampleExtensionToolbar2) + + message = "get_title_extension_admin has been deprecated and replaced by get_page_content_extension_admin" + with self.login_user_context(self.admin): + self.assertWarns( + DeprecationWarning, + message, + lambda: self.client.get(self.page.get_absolute_url()), + ) + + self.assertEqual(len(urls), 2) + toolbar_pool.toolbars = old_toolbars + def test_admin_title_extension(self): with self.login_user_context(self.admin): # add a new extension From 79f7ff2419b35fb8c43b937d778cdfd6499ad6e7 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Wed, 29 Nov 2023 15:22:41 +0100 Subject: [PATCH 04/10] fix linting issue --- cms/extensions/toolbar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cms/extensions/toolbar.py b/cms/extensions/toolbar.py index 5ea052f0e43..aff8e1c23b7 100644 --- a/cms/extensions/toolbar.py +++ b/cms/extensions/toolbar.py @@ -105,7 +105,7 @@ def get_page_content_extension_admin(self, page_content_obj=None): Return a tuple of the page content extension and the url; the extension is None if no instance exists, the url is None is no admin is registered for the extension. """ - page = self._get_page() + self._get_page() page_content = page_content_obj or self.page_content try: pagecontent_extension = self.model.objects.get(extended_object_id=page_content.pk) From fb31b04af851761879ef8ed6ba4b06acf8c8842a Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Wed, 29 Nov 2023 17:12:52 +0100 Subject: [PATCH 05/10] Accommodate review feedback --- cms/extensions/toolbar.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cms/extensions/toolbar.py b/cms/extensions/toolbar.py index aff8e1c23b7..3e324b6aa29 100644 --- a/cms/extensions/toolbar.py +++ b/cms/extensions/toolbar.py @@ -58,11 +58,11 @@ def get_page_extension_admin(self): model_name = self.model.__name__.lower() if page_extension: admin_url = admin_reverse( - '%s_%s_change' % (self.model._meta.app_label, model_name), + '{}_{}_change'.format(self.model._meta.app_label, model_name), args=(page_extension.pk,)) else: - admin_url = "%s?extended_object=%s" % ( - admin_reverse('%s_%s_add' % (self.model._meta.app_label, model_name)), + admin_url = "{}?extended_object={}".format( + admin_reverse('{}_{}_add'.format(self.model._meta.app_label, model_name)), self.page.pk) except NoReverseMatch: # pragma: no cover admin_url = None @@ -112,14 +112,14 @@ def get_page_content_extension_admin(self, page_content_obj=None): except self.model.DoesNotExist: pagecontent_extension = None try: - model_name = self.model.__name__.lower() + app_label, model_name = self.model._meta.app_label, self.model.__name__.lower() if pagecontent_extension: admin_url = admin_reverse( - '%s_%s_change' % (self.model._meta.app_label, model_name), + '{}_{}_change'.format(app_label, model_name), args=(pagecontent_extension.pk,)) else: - admin_url = "%s?extended_object=%s" % ( - admin_reverse('%s_%s_add' % (self.model._meta.app_label, model_name)), + admin_url = "{}?extended_object={}".format( + admin_reverse('{}_{}_add'.format(app_label, model_name)), page_content.pk) except NoReverseMatch: # pragma: no cover admin_url = None From 8cb4905d171f6577961d345460745724d6b65adb Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Wed, 29 Nov 2023 17:19:56 +0100 Subject: [PATCH 06/10] Add some comments --- cms/extensions/toolbar.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cms/extensions/toolbar.py b/cms/extensions/toolbar.py index 3e324b6aa29..bfad7f8f41a 100644 --- a/cms/extensions/toolbar.py +++ b/cms/extensions/toolbar.py @@ -31,13 +31,13 @@ def _setup_extension_toolbar(self): def _get_page(self): if not self.page: - if self.toolbar.obj and isinstance(self.toolbar.obj, PageContent): - self.page = self.toolbar.obj.page - self.page_content = self.toolbar.obj + obj = self.toolbar.get_object() # Try getting the PageContent object from the toolbar + if isinstance(obj, PageContent): + self.page = obj.page + self.page_content = obj else: - self.page = self.request.current_page + self.page = self.request.current_page # Otherwise get Page object from the request self.page_content = self.page.get_content_obj(self.current_lang) - return self.page def get_page_extension_admin(self): From 7125cbe58e550b33cf344a3c7101053b0a8e06da Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Wed, 29 Nov 2023 17:28:32 +0100 Subject: [PATCH 07/10] Readability improvement `ruff format` --- cms/extensions/toolbar.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/cms/extensions/toolbar.py b/cms/extensions/toolbar.py index bfad7f8f41a..cff92b7c393 100644 --- a/cms/extensions/toolbar.py +++ b/cms/extensions/toolbar.py @@ -26,7 +26,7 @@ def _setup_extension_toolbar(self): page = self._get_page() if page and user_can_change_page(self.request.user, page=page): - return self.toolbar.get_or_create_menu('page') + return self.toolbar.get_or_create_menu("page") return def _get_page(self): @@ -58,12 +58,12 @@ def get_page_extension_admin(self): model_name = self.model.__name__.lower() if page_extension: admin_url = admin_reverse( - '{}_{}_change'.format(self.model._meta.app_label, model_name), - args=(page_extension.pk,)) + f"{self.model._meta.app_label}_{model_name}_change", args=(page_extension.pk,) + ) else: admin_url = "{}?extended_object={}".format( - admin_reverse('{}_{}_add'.format(self.model._meta.app_label, model_name)), - self.page.pk) + admin_reverse(f"{self.model._meta.app_label}_{model_name}_add"), self.page.pk + ) except NoReverseMatch: # pragma: no cover admin_url = None return page_extension, admin_url @@ -80,12 +80,16 @@ def get_title_extension_admin(self, language=None): """ warnings.warn( "get_title_extension_admin has been deprecated and replaced by get_page_content_extension_admin", - DeprecationWarning, stacklevel=2, + DeprecationWarning, + stacklevel=2, ) page = self._get_page() - page_contents = page.pagecontent_set(manager="admin_manager").latest_content()\ + page_contents = ( + page.pagecontent_set(manager="admin_manager") + .latest_content() .filter(language__in=get_language_list(page.node.site_id)) + ) urls = [] for page_content in page_contents: @@ -114,13 +118,11 @@ def get_page_content_extension_admin(self, page_content_obj=None): try: app_label, model_name = self.model._meta.app_label, self.model.__name__.lower() if pagecontent_extension: - admin_url = admin_reverse( - '{}_{}_change'.format(app_label, model_name), - args=(pagecontent_extension.pk,)) + admin_url = admin_reverse(f"{app_label}_{model_name}_change", args=(pagecontent_extension.pk,)) else: admin_url = "{}?extended_object={}".format( - admin_reverse('{}_{}_add'.format(app_label, model_name)), - page_content.pk) + admin_reverse(f"{app_label}_{model_name}_add"), page_content.pk + ) except NoReverseMatch: # pragma: no cover admin_url = None return pagecontent_extension, admin_url @@ -129,6 +131,5 @@ def _get_sub_menu(self, current_menu, key, label, position=None): """ Utility function to get a submenu of the current menu """ - extension_menu = current_menu.get_or_create_menu( - key, label, position=position) + extension_menu = current_menu.get_or_create_menu(key, label, position=position) return extension_menu From 1203d6e409d8cc3fe3a1827eec43afcab62af5ad Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Wed, 29 Nov 2023 17:30:21 +0100 Subject: [PATCH 08/10] One more readability improvement --- cms/extensions/toolbar.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cms/extensions/toolbar.py b/cms/extensions/toolbar.py index cff92b7c393..2e684fb680c 100644 --- a/cms/extensions/toolbar.py +++ b/cms/extensions/toolbar.py @@ -55,14 +55,12 @@ def get_page_extension_admin(self): except self.model.DoesNotExist: page_extension = None try: - model_name = self.model.__name__.lower() + app_label, model_name = self.model._meta.app_label, self.model.__name__.lower() if page_extension: - admin_url = admin_reverse( - f"{self.model._meta.app_label}_{model_name}_change", args=(page_extension.pk,) - ) + admin_url = admin_reverse(f"{app_label}_{model_name}_change", args=(page_extension.pk,)) else: admin_url = "{}?extended_object={}".format( - admin_reverse(f"{self.model._meta.app_label}_{model_name}_add"), self.page.pk + admin_reverse(f"{app_label}_{model_name}_add"), self.page.pk ) except NoReverseMatch: # pragma: no cover admin_url = None From ed1e036d5da19d6ef44160646938d5a31ddeea13 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Wed, 29 Nov 2023 17:44:20 +0100 Subject: [PATCH 09/10] Doc-string update --- cms/extensions/toolbar.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cms/extensions/toolbar.py b/cms/extensions/toolbar.py index 2e684fb680c..f81e1f56a9e 100644 --- a/cms/extensions/toolbar.py +++ b/cms/extensions/toolbar.py @@ -10,6 +10,8 @@ class ExtensionToolbar(CMSToolbar): + """"Offers simplified API for providing the user access to the admin of page extensions and + page content extensions through the toolbar.""" model = None page = None page_content = None @@ -68,6 +70,10 @@ def get_page_extension_admin(self): def get_title_extension_admin(self, language=None): """ + Deprecated. + + Reflects now obsolete behavior in django CMS 3.x: + Get the admin urls for the page content extensions menu items, depending on whether a :class:`~cms.extensions.models.PageContentExtension` instance exists for each :class:`~cms.models.contentmodels.PageContent` in the current page. @@ -96,8 +102,6 @@ def get_title_extension_admin(self, language=None): urls.append(admin_url) return urls - return self.get_page_content_extension_admin(language) - def get_page_content_extension_admin(self, page_content_obj=None): """ Get the admin url for the page content extensions menu item, depending on whether a From a6846147aade98208b6aa86ae8cf7648142decdb Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Wed, 29 Nov 2023 17:55:30 +0100 Subject: [PATCH 10/10] Fix typo --- cms/extensions/toolbar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cms/extensions/toolbar.py b/cms/extensions/toolbar.py index f81e1f56a9e..b43f12f8d51 100644 --- a/cms/extensions/toolbar.py +++ b/cms/extensions/toolbar.py @@ -10,7 +10,7 @@ class ExtensionToolbar(CMSToolbar): - """"Offers simplified API for providing the user access to the admin of page extensions and + """Offers simplified API for providing the user access to the admin of page extensions and page content extensions through the toolbar.""" model = None page = None