diff --git a/README.rst b/README.rst index 4c369a0d..87ad7ec5 100644 --- a/README.rst +++ b/README.rst @@ -52,7 +52,7 @@ Testing To run all the tests the only thing you need to do is run:: - pip install -r tests/requirements.txt + pip install -r tests/requirements/requirements_base.txt python test_settings.py diff --git a/djangocms_versioning/cms_toolbars.py b/djangocms_versioning/cms_toolbars.py index f98388f8..30ff203f 100644 --- a/djangocms_versioning/cms_toolbars.py +++ b/djangocms_versioning/cms_toolbars.py @@ -55,9 +55,7 @@ def _is_versioned(self): versioning """ versioning_extension = apps.get_app_config("djangocms_versioning").cms_extension - return versioning_extension.is_content_model_versioned( - self.toolbar.obj.__class__ - ) + return versioning_extension.is_content_model_versioned(self.toolbar.obj.__class__) def _get_proxy_model(self): """Helper method to get the proxy model class for the content @@ -66,8 +64,7 @@ def _get_proxy_model(self): return self._get_versionable().version_model_proxy def _add_publish_button(self): - """Helper method to add a publish button to the toolbar - """ + """Helper method to add a publish button to the toolbar""" # Check if object is registered with versioning otherwise don't add if not self._is_versioned(): return @@ -99,8 +96,7 @@ def add_edit_button(self): self._add_unlock_button() def _add_edit_button(self, disabled=False): - """Helper method to add an edit button to the toolbar - """ + """Helper method to add an edit button to the toolbar""" item = ButtonList(side=self.toolbar.RIGHT) proxy_model = self._get_proxy_model() version = Version.objects.get_for_content(self.toolbar.obj) @@ -109,9 +105,9 @@ def _add_edit_button(self, disabled=False): f"admin:{proxy_model._meta.app_label}_{proxy_model.__name__.lower()}_edit_redirect", args=(version.pk,), ) - pks_for_grouper = version.versionable.for_content_grouping_values( - version.content - ).values_list("pk", flat=True) + pks_for_grouper = version.versionable.for_content_grouping_values(version.content).values_list( + "pk", flat=True + ) content_type = ContentType.objects.get_for_model(version.content) draft_exists = Version.objects.filter( object_id__in=pks_for_grouper, content_type=content_type, state=DRAFT @@ -125,8 +121,7 @@ def _add_edit_button(self, disabled=False): self.toolbar.add_item(item) def _add_unlock_button(self): - """Helper method to add an edit button to the toolbar - """ + """Helper method to add an edit button to the toolbar""" if LOCK_VERSIONS and self._is_versioned(): item = ButtonList(side=self.toolbar.RIGHT) proxy_model = self._get_proxy_model() @@ -164,8 +159,7 @@ def _add_lock_message(self): self.toolbar.add_item(lock_message, position=0) def _add_revert_button(self, disabled=False): - """Helper method to add a revert button to the toolbar - """ + """Helper method to add a revert button to the toolbar""" # Check if object is registered with versioning otherwise don't add if not self._is_versioned(): return @@ -186,8 +180,7 @@ def _add_revert_button(self, disabled=False): self.toolbar.add_item(item) def _add_versioning_menu(self): - """ Helper method to add version menu in the toolbar - """ + """Helper method to add version menu in the toolbar""" # Check if object is registered with versioning otherwise don't add if not self._is_versioned(): return @@ -215,13 +208,15 @@ def _add_versioning_menu(self): proxy_model = self._get_proxy_model() url = reverse( f"admin:{proxy_model._meta.app_label}_{proxy_model.__name__.lower()}_compare", - args=(version.source.pk,) + args=(version.source.pk,), ) - url += "?" + urlencode({ - "compare_to": version.pk, - "back": self.toolbar.request_path, - }) + url += "?" + urlencode( + { + "compare_to": version.pk, + "back": self.toolbar.request_path, + } + ) versioning_menu.add_link_item(name, url=url) # Discard changes menu entry (wrt to source) if version.check_discard.as_bool(self.request.user): # pragma: no cover @@ -230,26 +225,22 @@ def _add_versioning_menu(self): _("Discard Changes"), url=reverse( f"admin:{proxy_model._meta.app_label}_{proxy_model.__name__.lower()}_discard", - args=(version.pk,) - ) + args=(version.pk,), + ), ) def _get_published_page_version(self): - """Returns a published page if one exists for the toolbar object - """ + """Returns a published page if one exists for the toolbar object""" language = self.current_lang # Exit the current toolbar object is not a Page / PageContent instance if not isinstance(self.toolbar.obj, PageContent) or not self.page: return - return PageContent.objects.filter( - page=self.page, language=language - ).select_related("page").first() + return PageContent.objects.filter(page=self.page, language=language).select_related("page").first() def _add_view_published_button(self): - """Helper method to add a publish button to the toolbar - """ + """Helper method to add a publish button to the toolbar""" # Check if object is registered with versioning otherwise don't add if not self._is_versioned(): return @@ -347,12 +338,12 @@ def override_language_menu(self): language_menu.add_link_item(name, url=url, active=self.current_lang == code) def change_language_menu(self): - if self.toolbar.edit_mode_active and self.page: - can_change = page_permissions.user_can_change_page( + can_change = ( + self.page + and page_permissions.user_can_change_page( user=self.request.user, page=self.page, site=self.current_site ) - else: - can_change = False + ) if can_change: language_menu = self.toolbar.get_menu(LANGUAGE_MENU_IDENTIFIER) @@ -360,37 +351,25 @@ def change_language_menu(self): return None languages = get_language_dict(self.current_site.pk) - remove = [ - (code, languages.get(code, code)) - for code in self.page.get_languages() - if code in languages - ] - add = [ - code - for code in languages.items() - if code not in remove - ] + remove = [(code, languages.get(code, code)) for code in self.page.get_languages() if code in languages] + add = [code for code in languages.items() if code not in remove] copy = [ - (code, name) - for code, name in languages.items() - if code != self.current_lang and (code, name) in remove + (code, name) for code, name in languages.items() if code != self.current_lang and (code, name) in remove ] + # ADD TRANSLATION — only if user has change permission if add: language_menu.add_break(ADD_PAGE_LANGUAGE_BREAK) - add_plugins_menu = language_menu.get_or_create_menu( - f"{LANGUAGE_MENU_IDENTIFIER}-add", _("Add Translation") - ) + add_plugins_menu = language_menu.get_or_create_menu(f"{LANGUAGE_MENU_IDENTIFIER}-add", _("Add Translation")) # noqa: E501 page_add_url = admin_reverse("cms_pagecontent_add") for code, name in add: - url = add_url_parameters( - page_add_url, cms_page=self.page.pk, language=code - ) + url = add_url_parameters(page_add_url, cms_page=self.page.pk, language=code) add_plugins_menu.add_modal_item(name, url=url) + # DELETE TRANSLATION — only if user has change permission if remove and ALLOW_DELETING_VERSIONS and CMS_SUPPORTS_DELETING_TRANSLATIONS: remove_plugins_menu = language_menu.get_or_create_menu( f"{LANGUAGE_MENU_IDENTIFIER}-del", _("Delete Translation") @@ -404,12 +383,17 @@ def change_language_menu(self): on_close = REFRESH_PAGE if self.toolbar.get_object() == pagecontent and not disabled: other_content = next( - (self.page.get_admin_content(lang) for lang in self.page.get_languages() - if lang != pagecontent.language and lang in languages), None) + ( + self.page.get_admin_content(lang) + for lang in self.page.get_languages() + if lang != pagecontent.language and lang in languages + ), + None, + ) on_close = get_object_preview_url(other_content) remove_plugins_menu.add_modal_item(name, url=url, disabled=disabled, on_close=on_close) - - if copy: + # COPY ALL PLUGINS — only if user can change AND in edit mode + if self.toolbar.edit_mode_active and copy: copy_plugins_menu = language_menu.get_or_create_menu( f"{LANGUAGE_MENU_IDENTIFIER}-copy", _("Copy all plugins") ) @@ -422,9 +406,11 @@ def change_language_menu(self): if page_content: # Only offer to copy if content for source language exists page_copy_url = admin_reverse("cms_pagecontent_copy_language", args=(page_content.pk,)) copy_plugins_menu.add_ajax_item( - title % name, action=page_copy_url, + title % name, + action=page_copy_url, data={"source_language": code, "target_language": self.current_lang}, - question=question % name, on_success=self.toolbar.REFRESH_PAGE + question=question % name, + on_success=self.toolbar.REFRESH_PAGE, ) item_added = True if not item_added: # pragma: no cover @@ -467,8 +453,7 @@ def replace_toolbar(old, new): new_name = ".".join((new.__module__, new.__name__)) old_name = ".".join((old.__module__, old.__name__)) toolbar_pool.toolbars = OrderedDict( - (new_name, new) if name == old_name else (name, toolbar) - for name, toolbar in toolbar_pool.toolbars.items() + (new_name, new) if name == old_name else (name, toolbar) for name, toolbar in toolbar_pool.toolbars.items() ) diff --git a/tests/test_toolbars.py b/tests/test_toolbars.py index 13d2da2a..df17bcac 100644 --- a/tests/test_toolbars.py +++ b/tests/test_toolbars.py @@ -12,6 +12,7 @@ from django.utils.text import slugify from packaging.version import Version +from djangocms_versioning import cms_toolbars from djangocms_versioning.cms_config import VersioningCMSConfig from djangocms_versioning.cms_toolbars import VersioningPageToolbar from djangocms_versioning.constants import ARCHIVED, DRAFT, PUBLISHED @@ -38,27 +39,18 @@ class VersioningToolbarTestCase(CMSTestCase): def _get_publish_url(self, version, versionable=PollsCMSConfig.versioning[0]): - """Helper method to return the expected publish url - """ - admin_url = self.get_admin_url( - versionable.version_model_proxy, "publish", version.pk - ) + """Helper method to return the expected publish url""" + admin_url = self.get_admin_url(versionable.version_model_proxy, "publish", version.pk) return admin_url def _get_edit_url(self, version, versionable=PollsCMSConfig.versioning[0]): - """Helper method to return the expected edit redirect url - """ - admin_url = self.get_admin_url( - versionable.version_model_proxy, "edit_redirect", version.pk - ) + """Helper method to return the expected edit redirect url""" + admin_url = self.get_admin_url(versionable.version_model_proxy, "edit_redirect", version.pk) return admin_url def _get_revert_url(self, version, versionable=PollsCMSConfig.versioning[0]): - """Helper method to return the expected publish url - """ - admin_url = self.get_admin_url( - versionable.version_model_proxy, "revert", version.pk - ) + """Helper method to return the expected publish url""" + admin_url = self.get_admin_url(versionable.version_model_proxy, "revert", version.pk) return admin_url def test_publish_in_toolbar_in_edit_mode(self): @@ -96,7 +88,9 @@ def test_revert_in_toolbar_in_preview_mode(self): self.assertFalse(revert_button.disabled) self.assertListEqual( revert_button.extra_classes, - ["cms-btn-action", ], + [ + "cms-btn-action", + ], ) def test_publish_not_in_toolbar_in_preview_mode(self): @@ -136,9 +130,7 @@ def test_url_for_publish_uses_version_id_not_content_id(self): # Now create a poll version - the poll content and version id # will be different. version = PollVersionFactory() - toolbar = get_toolbar( - version.content, user=self.get_superuser(), edit_mode=True - ) + toolbar = get_toolbar(version.content, user=self.get_superuser(), edit_mode=True) toolbar.post_template_populate() publish_button = find_toolbar_buttons("Publish", toolbar.toolbar)[0] @@ -146,9 +138,7 @@ def test_url_for_publish_uses_version_id_not_content_id(self): def test_edit_in_toolbar_in_preview_mode(self): version = PageVersionFactory(content__template="") - toolbar = get_toolbar( - version.content, user=self.get_superuser(), preview_mode=True - ) + toolbar = get_toolbar(version.content, user=self.get_superuser(), preview_mode=True) toolbar.post_template_populate() edit_button = find_toolbar_buttons("Edit", toolbar.toolbar)[0] @@ -160,15 +150,12 @@ def test_edit_in_toolbar_in_preview_mode(self): ) self.assertFalse(edit_button.disabled) self.assertListEqual( - edit_button.extra_classes, - ["cms-btn-action", "cms-form-post-method", "cms-versioning-js-edit-btn"] + edit_button.extra_classes, ["cms-btn-action", "cms-form-post-method", "cms-versioning-js-edit-btn"] ) def test_edit_not_in_toolbar_in_edit_mode(self): version = PollVersionFactory() - toolbar = get_toolbar( - version.content, user=self.get_superuser(), edit_mode=True - ) + toolbar = get_toolbar(version.content, user=self.get_superuser(), edit_mode=True) toolbar.post_template_populate() @@ -176,9 +163,7 @@ def test_edit_not_in_toolbar_in_edit_mode(self): def test_edit_not_in_toolbar_in_structure_mode(self): version = PollVersionFactory() - toolbar = get_toolbar( - version.content, user=self.get_superuser(), structure_mode=True - ) + toolbar = get_toolbar(version.content, user=self.get_superuser(), structure_mode=True) toolbar.post_template_populate() @@ -188,9 +173,7 @@ def test_dont_add_edit_for_models_not_registered_with_versioning(self): # User objects are not registered with versioning, so attempting # to populate a user toolbar should not attempt to add a edit # button - toolbar = get_toolbar( - UserFactory(), user=self.get_superuser(), preview_mode=True - ) + toolbar = get_toolbar(UserFactory(), user=self.get_superuser(), preview_mode=True) toolbar.post_template_populate() @@ -207,9 +190,7 @@ def test_url_for_edit_uses_version_id_not_content_id(self): # Now create a page version - the page content and version id # will be different. version = PageVersionFactory(content__template="") - toolbar = get_toolbar( - version.content, user=self.get_superuser(), preview_mode=True - ) + toolbar = get_toolbar(version.content, user=self.get_superuser(), preview_mode=True) edit_url = self._get_edit_url(version, VersioningCMSConfig.versioning[0]) toolbar.post_template_populate() @@ -225,9 +206,7 @@ def test_default_cms_edit_button_is_replaced_by_versioning_edit_button(self): page = PageVersionFactory(content__template="", content__language="en") url = get_object_preview_url(page.content) - edit_url = self._get_edit_url( - page, VersioningCMSConfig.versioning[0] - ) + edit_url = self._get_edit_url(page, VersioningCMSConfig.versioning[0]) with self.login_user_context(self.get_superuser()): response = self.client.post(url) @@ -261,9 +240,7 @@ def test_default_edit_button_from_cms_exists(self): The default toolbar Edit button exists. """ pagecontent = PageVersionFactory(content__template="") - edit_url = self._get_edit_url( - pagecontent.content, VersioningCMSConfig.versioning[0] - ) + edit_url = self._get_edit_url(pagecontent.content, VersioningCMSConfig.versioning[0]) toolbar = get_toolbar( pagecontent.content, @@ -322,23 +299,18 @@ def test_version_menu_for_none_version(self): def test_version_menu_and_url_for_version_content(self): # Versioned item should have versioning menu and url should be version list url version = PollVersionFactory() - toolbar = get_toolbar( - version.content, user=self.get_superuser(), preview_mode=True - ) + toolbar = get_toolbar(version.content, user=self.get_superuser(), preview_mode=True) toolbar.post_template_populate() version_menu = toolbar.toolbar.get_menu("version") self.assertIsNotNone(version_menu) - self.assertEqual( - version_menu.get_items()[0].url, version_list_url(version.content) - ) + self.assertEqual(version_menu.get_items()[0].url, version_list_url(version.content)) def test_version_menu_label(self): # Versioned item should have correct version menu label from djangocms_versioning.constants import VERSION_STATES + version = PollVersionFactory() - toolbar = get_toolbar( - version.content, user=self.get_superuser(), preview_mode=True - ) + toolbar = get_toolbar(version.content, user=self.get_superuser(), preview_mode=True) toolbar.post_template_populate() version_menu = toolbar.toolbar.get_menu("version") @@ -488,7 +460,6 @@ def test_view_published_in_toolbar_in_preview_mode_button_url(self): class VersioningPageToolbarTestCase(CMSTestCase): - def _get_toolbar_item_by_name(self, menu, name): for item in menu.items: if hasattr(item, "name") and item.name == name: @@ -532,10 +503,7 @@ def test_change_language_menu_page_toolbar(self): # 3 out of 4 populated languages, Break, Add Translation menu, Copy all plugins self.assertEqual(language_menu.get_item_count(), 6) - language_menu_dict = { - menu.name: list(menu.items) - for key, menu in language_menu.menus.items() - } + language_menu_dict = {menu.name: list(menu.items) for key, menu in language_menu.menus.items()} self.assertIn("Add Translation", language_menu_dict.keys()) self.assertIn("Copy all plugins", language_menu_dict.keys()) self.assertNotIn("Delete Translation", language_menu_dict.keys()) @@ -556,6 +524,34 @@ def test_change_language_menu_page_toolbar(self): lang_code = "fr" if "Française" in item.name else "it" self.assertIn(f"language={lang_code}", item.url) + def test_language_menu_in_non_edit_mode(self): + with patch.object(cms_toolbars, "ALLOW_DELETING_VERSIONS", True): + with patch.object(cms_toolbars, "CMS_SUPPORTS_DELETING_TRANSLATIONS", True): + version = PageVersionFactory(content__language="en") + PageContentWithVersionFactory(page=version.content.page, language="de") + PageContentWithVersionFactory(page=version.content.page, language="it") + page = version.content.page + page.update_languages(["en", "de", "it"]) + + request = self.get_page_request( + page=page, + path=get_object_preview_url(version.content), + user=self.get_superuser(), + ) + request.toolbar.set_object(version.content) + request.toolbar.populate() + request.toolbar.post_template_populate() + + language_menu = request.toolbar.get_menu(LANGUAGE_MENU_IDENTIFIER) + # 3 out of 4 populated languages, Break, Add Translation menu, Delete Translation + self.assertEqual(language_menu.get_item_count(), 6) + + language_menu_dict = {menu.name: list(menu.items) for key, menu in language_menu.menus.items()} + self.assertIn("Add Translation", language_menu_dict.keys()) + self.assertNotIn("Copy all plugins", language_menu_dict.keys()) + self.assertIn("Delete Translation", language_menu_dict.keys()) + + @skipIf(cms_version <= Version("4.1.4"), "For CMS 4.1.5 and bove: Add delete translation menu") def test_change_language_menu_page_toolbar_including_delete(self): """Check that patched PageToolbar.change_language_menu also provides @@ -583,10 +579,7 @@ def test_change_language_menu_page_toolbar_including_delete(self): # 3 out of 4 populated languages, Break, Add Translation menu, Copy all plugins self.assertEqual(language_menu.get_item_count(), 7) - language_menu_dict = { - menu.name: list(menu.items) - for key, menu in language_menu.menus.items() - } + language_menu_dict = {menu.name: list(menu.items) for key, menu in language_menu.menus.items()} self.assertIn("Add Translation", language_menu_dict.keys()) self.assertIn("Copy all plugins", language_menu_dict.keys()) self.assertIn("Delete Translation", language_menu_dict.keys())