import datetime import json import re import sys from unittest import skipUnless from unittest.mock import patch from django.conf import settings from django.contrib import admin from django.contrib.sites.models import Site from django.core.cache import cache from django.db import IntegrityError, connection, transaction from django.forms.models import model_to_dict from django.http import HttpRequest, HttpResponse from django.test.html import HTMLParseError, Parser from django.test.utils import CaptureQueriesContext, override_settings from django.urls import clear_url_caches from django.utils.encoding import force_str from django.utils.timezone import now as tz_now from django.utils.translation import override as force_language from djangocms_text.models import Text from cms import constants from cms.admin.pageadmin import PAGE_SMART_LINK_MAX_RESULTS, PageContentAdmin from cms.api import add_plugin, create_page, create_page_content from cms.appresolver import clear_app_resolvers from cms.cache.permissions import get_permission_cache, set_permission_cache from cms.middleware.user import CurrentUserMiddleware from cms.models import PageContent from cms.models.pagemodel import Page, PageUrl from cms.models.permissionmodels import PagePermission from cms.models.pluginmodel import CMSPlugin from cms.test_utils.project.sampleapp.models import SampleAppConfig from cms.test_utils.testcases import URL_CMS_PAGE_MOVE, CMSTestCase from cms.test_utils.util.context_managers import ( LanguageOverride, UserLoginContext, override_placeholder_conf, ) from cms.toolbar.utils import get_object_edit_url from cms.utils.compat.dj import installed_apps from cms.utils.conf import get_cms_setting from cms.utils.page import get_page_from_request from cms.utils.urlutils import admin_reverse class PageTreeLiParser(Parser): def handle_starttag(self, tag, attrs): # We have to strip out attributes from the
  • # tags in order to compare the values only # Otherwise we'd have to include all attributes # which in this case is not optimal because there's too many # and would require us to hardcode a bunch of stuff here if tag == "li": attrs = [] Parser.handle_starttag(self, tag, attrs) class PageTreeOptionsParser(Parser): def handle_starttag(self, tag, attrs): # This parser only cares about the options on the right side # of the page tree for each page. if tag == "li" and attrs and attrs[-1][0] == "data-coloptions": attrs = [attrs[-1]] Parser.handle_starttag(self, tag, attrs) class PageTestBase(CMSTestCase): """ The purpose of this class is to provide some basic functionality to test methods of the Page admin. """ placeholderconf = { "body": { "limits": { "global": 2, "TextPlugin": 1, } } } def _add_plugin_to_page(self, page, plugin_type="LinkPlugin", language="en", publish=True): plugin_data = { "TextPlugin": {"body": "

    text

    "}, "LinkPlugin": {"name": "A Link", "external_link": "https://www.django-cms.org"}, } placeholder = page.get_placeholders(language).get(slot="body") plugin = add_plugin(placeholder, plugin_type, language, **plugin_data[plugin_type]) return plugin def _translation_exists(self, slug=None, title=None): if not slug: slug = "permissions-de" lookup = PageContent.objects.filter(page__urls__slug=slug) if title: lookup = lookup.filter(title=title) return lookup.exists() def _get_add_plugin_uri(self, page, language="en"): placeholder = page.get_placeholders(language).get(slot="body") uri = self.get_add_plugin_uri( placeholder=placeholder, plugin_type="LinkPlugin", language=language, ) return uri def _get_page_data(self, **kwargs): site = Site.objects.get_current() data = { "title": "permissions", "slug": "permissions", "language": "en", "site": site.pk, "template": "nav_playground.html", } data.update(**kwargs) return data def _get_move_data(self, plugin, position, placeholder=None, parent=None): try: placeholder_id = placeholder.pk except AttributeError: placeholder_id = "" try: parent_id = parent.pk except AttributeError: parent_id = "" data = { "placeholder_id": placeholder_id, "target_language": "en", "target_position": position, "plugin_id": plugin.pk, "plugin_parent": parent_id, } return data def get_page(self, parent=None, site=None, language=None, template="nav_playground.html"): page_data = self.get_new_page_data_dbfields() return create_page(**page_data) def get_post_request(self, data): return self.get_request(post_data=data) def create_page(self, title=None, **kwargs): return create_page(title or self._testMethodName, "nav_playground.html", "en", **kwargs) class PageTest(PageTestBase): def tearDown(self): cache.clear() def test_add_page(self): """ Test that the add admin page could be displayed via the admin """ superuser = self.get_superuser() with self.login_user_context(superuser): response = self.client.get(self.get_page_add_uri("en")) self.assertEqual(response.status_code, 200) self.assertContains(response, "Add a page", html=True) def test_add_page_different_site(self): """ Test that the add admin page could be displayed via the admin """ superuser = self.get_superuser() Site.objects.create(id=2, name="example-2.com", domain="example-2.com") with self.login_user_context(superuser): response = self.client.get(self.get_page_add_uri("en", site_id=2)) self.assertEqual(response.status_code, 200) self.assertContains(response, "Add a page", html=True) self.assertContains(response, "&site=2") def test_create_page_admin(self): """ Test that a page can be created via the admin """ page_data = self.get_new_page_data() superuser = self.get_superuser() with self.login_user_context(superuser): self.assertEqual(PageContent.objects.all().count(), 0) self.assertEqual(Page.objects.all().count(), 0) # create home response = self.client.post(self.get_page_add_uri("en"), page_data) self.assertRedirects(response, self.get_pages_admin_list_uri("en")) page_url = PageUrl.objects.get(slug=page_data["slug"]) self.assertEqual(page_url.page.get_title(), page_data["title"]) self.assertEqual(page_url.page.get_slug("en"), page_data["slug"]) self.assertEqual(page_url.page.get_placeholders("en").count(), 2) def test_create_page_admin_different_site(self): """ Test that a page can be created via the admin """ Site.objects.create(id=2, name="example-2.com", domain="example-2.com") with self.settings(CMS_LANGUAGES={2: [{"code": "en", "name": "English"}]}): page_data = self.get_new_page_data() superuser = self.get_superuser() with self.login_user_context(superuser): self.assertEqual(PageContent.objects.all().count(), 0) self.assertEqual(Page.objects.all().count(), 0) # create home response = self.client.post(self.get_page_add_uri("en", site_id=2), page_data) self.assertRedirects(response, self.get_pages_admin_list_uri("en", site_id=2)) page_url = PageUrl.objects.get(slug=page_data["slug"]) self.assertEqual(page_url.page.site_id, 2) self.assertEqual(page_url.page.get_title(), page_data["title"]) self.assertEqual(page_url.page.get_slug("en"), page_data["slug"]) self.assertEqual(page_url.page.get_placeholders("en").count(), 2) def test_create_page_with_unconfigured_language(self): """ Test that a page can be created via the admin with the request language pointing to a language not configured for the current site """ from django.contrib.auth import get_user_model from django.test import Client client = Client() superuser = self.get_superuser() Site.objects.create(id=2, name="example-2.com", domain="example-2.com") client.login( username=getattr(superuser, get_user_model().USERNAME_FIELD), password=getattr(superuser, get_user_model().USERNAME_FIELD), ) self.assertEqual(PageContent.objects.all().count(), 0) self.assertEqual(Page.objects.all().count(), 0) # create home with self.settings(SITE_ID=2): endpoint = self.get_page_add_uri("en") # url uses "en" as the request language # but the site is configured to use "de" and "fr" response = client.post(endpoint, self.get_new_page_data()) self.assertRedirects(response, self.get_pages_admin_list_uri("de")) self.assertEqual(Page.objects.filter(site=2).count(), 1) self.assertEqual(PageContent.objects.filter(language="de").count(), 1) # The user is on site #1 but switches to using site #2. # url uses "en" as the request language # but the site is configured to use "de" and "fr" endpoint = self.get_page_add_uri("en", site_id=2) response = client.post(endpoint, self.get_new_page_data()) self.assertRedirects(response, self.get_pages_admin_list_uri("de", site_id=2)) self.assertEqual(Page.objects.filter(site=2).count(), 2) self.assertEqual(PageContent.objects.filter(language="de").count(), 2) Site.objects.clear_cache() client.logout() def test_create_tree_admin(self): """ Test that a tree can be created via the admin """ page_1 = self.get_new_page_data() superuser = self.get_superuser() with self.login_user_context(superuser): # create home and auto publish response = self.client.post(self.get_page_add_uri("en"), page_1) self.assertRedirects(response, self.get_pages_admin_list_uri("en")) home_url = PageUrl.objects.get(slug=page_1["slug"]) page_2 = self.get_new_page_data(parent_id=home_url.page.pk) page_3 = self.get_new_page_data(parent_id=home_url.page.pk) page_4 = self.get_new_page_data(parent_id=home_url.page.pk) response = self.client.post(self.get_page_add_uri("en"), page_2) self.assertRedirects(response, self.get_pages_admin_list_uri("en")) response = self.client.post(self.get_page_add_uri("en"), page_3) self.assertRedirects(response, self.get_pages_admin_list_uri("en")) page2_url = PageUrl.objects.get(slug=page_2["slug"]) add_endpoint = self.get_page_add_uri("en") response = self.client.post(add_endpoint + "&target=%s&position=right" % page2_url.page.pk, page_4) self.assertRedirects(response, self.get_pages_admin_list_uri("en")) def test_slug_collision(self): """ Test a slug collision """ page_data = self.get_new_page_data() # create first page superuser = self.get_superuser() with self.login_user_context(superuser): add_endpoint = self.get_page_add_uri("en") # Home response = self.client.post(add_endpoint, page_data) self.assertRedirects(response, self.get_pages_admin_list_uri("en")) # Second root response = self.client.post(add_endpoint, page_data) self.assertRedirects(response, self.get_pages_admin_list_uri("en")) response = self.client.post(add_endpoint, page_data) new_page = Page.objects.only("id").latest("id") expected_error = ( '" ).format(self.get_page_change_uri("en", new_page)) self.assertEqual(response.status_code, 200) self.assertContains(response, expected_error) def test_child_slug_collision(self): """ Test a slug collision """ root = create_page("home", "nav_playground.html", "en") page = create_page("page", "nav_playground.html", "en") sub_page = create_page("subpage", "nav_playground.html", "en", parent=page) child_page = create_page("child-page", "nav_playground.html", "en", parent=root) root.set_as_homepage() superuser = self.get_superuser() add_endpoint = self.get_page_add_uri("en") with self.login_user_context(superuser): with self.subTest("Slug collision between two child pages of the same node"): page_data = self.get_new_page_data(page.pk) page_data["slug"] = "subpage" response = self.client.post(add_endpoint, page_data) expected_markup = ( '" ).format(self.get_page_change_uri("en", sub_page)) self.assertEqual(response.status_code, 200) self.assertContains(response, expected_markup) with self.subTest("Slug collision between page with no parent and a child page of home-page"): page_data = self.get_new_page_data() page_data["slug"] = "child-page" response = self.client.post(add_endpoint, page_data) expected_markup = ( '" ).format(self.get_page_change_uri("en", child_page)) self.assertEqual(response.status_code, 200) self.assertContains(response, expected_markup) with self.subTest("Slug collision between two top-level pages"): page_data = self.get_new_page_data() page_data["slug"] = "page" response = self.client.post(add_endpoint, page_data) expected_markup = ( '" ).format(self.get_page_change_uri("en", page)) self.assertEqual(response.status_code, 200) self.assertContains(response, expected_markup) def test_change_child_slug_to_sibling_slug(self): """Changing a child page's slug to a sibling's slug is rejected (#8902)""" parent = create_page("parent", "nav_playground.html", "en") create_page("a", "nav_playground.html", "en", parent=parent, slug="a") page_b = create_page("b", "nav_playground.html", "en", parent=parent, slug="b") superuser = self.get_superuser() page_data = self.get_new_page_data() page_data.update({"title": "b", "slug": "a", "template": page_b.get_template("en")}) with self.login_user_context(superuser): response = self.client.post(self.get_page_change_uri("en", page_b), page_data) self.assertEqual(response.status_code, 200) self.assertContains(response, "has the same url 'parent/a'") self.assertEqual(PageUrl.objects.get(page=page_b, language="en").path, "parent/b") def test_edit_page(self): """ Test that a page can edited via the admin """ superuser = self.get_superuser() with self.login_user_context(superuser): page_data = self.get_new_page_data() endpoint = self.get_page_add_uri("en") self.client.post(endpoint, page_data) page = Page.objects.get(urls__slug=page_data["slug"]) response = self.client.get(self.get_page_change_uri("en", page)) self.assertEqual(response.status_code, 200) self.assertContains(response, "Change a page", html=True) page_data["title"] = "changed title" page_data["template"] = page.get_template("en") response = self.client.post(self.get_page_change_uri("en", page), page_data) page._clear_internal_cache() self.assertRedirects(response, self.get_pages_admin_list_uri("en")) self.assertEqual(page.get_title(), "changed title") def test_page_redirect_field_validation(self): superuser = self.get_superuser() data = self.get_new_page_data() with self.login_user_context(superuser): self.client.post(self.get_page_add_uri("en"), data) page = Page.objects.get(urls__slug=data["slug"]) data["template"] = page.template endpoint = self.get_page_change_uri("en", page) redirect_to = self.get_pages_admin_list_uri("en") validation_error = '' # Values carrying markup are rejected by ``validate_url()`` itself, before it picks # the relative or the absolute branch, so they carry its own message. markup_error = ( '" ) with self.subTest("Test that a redirect to the root page (valid)"): with self.login_user_context(superuser): data["redirect"] = "/" response = self.client.post(endpoint, data) self.assertRedirects(response, redirect_to) with self.subTest("Test that a redirect to a page with an absolute URL (valid)"): with self.login_user_context(superuser): data["redirect"] = "/hello" response = self.client.post(endpoint, data) self.assertRedirects(response, redirect_to) with self.subTest("Test that a redirect to a page with an absolute URL ending with a slash (valid)"): with self.login_user_context(superuser): data["redirect"] = "/hello/" response = self.client.post(endpoint, data) self.assertRedirects(response, redirect_to) with self.subTest("Test that a redirect to a page with a relative URL (valid)"): with self.login_user_context(superuser): data["redirect"] = "../hello" response = self.client.post(endpoint, data) self.assertRedirects(response, redirect_to) with self.subTest("Test that a redirect to a page with a relative URL ending with a slash (valid)"): with self.login_user_context(superuser): data["redirect"] = "../hello/" response = self.client.post(endpoint, data) self.assertRedirects(response, redirect_to) with self.subTest("Test that a redirect to a page with a javascript: URL (invalid)"): with self.login_user_context(superuser): data["redirect"] = "javascript:alert(1)" # Asserts users can't insert javascript call response = self.client.post(endpoint, data) self.assertContains(response, validation_error, html=True) with self.subTest("Test that a redirect to a page with script tag as URL (invalid)"): with self.login_user_context(superuser): data["redirect"] = '' # Asserts users can't insert javascript call response = self.client.post(endpoint, data) self.assertContains(response, markup_error, html=True) def test_meta_description_fields_from_admin(self): """ Test that description and keywords tags can be set via the admin """ superuser = self.get_superuser() with self.login_user_context(superuser): page_data = self.get_new_page_data() page_data["meta_description"] = "I am a page" self.client.post(self.get_page_add_uri("en"), page_data) page = Page.objects.get(urls__slug=page_data["slug"]) response = self.client.get(self.get_page_change_uri("en", page)) self.assertEqual(response.status_code, 200) page_data["template"] = page.get_template("en") page_data["meta_description"] = "I am a duck" response = self.client.post(self.get_page_change_uri("en", page), page_data) self.assertRedirects(response, self.get_pages_admin_list_uri("en")) page = Page.objects.get(urls__slug=page_data["slug"]) self.assertEqual(page.get_meta_description(), "I am a duck") def test_meta_description_from_template_tags(self): from django import template superuser = self.get_superuser() with self.login_user_context(superuser): page_data = self.get_new_page_data() page_data["title"] = "Hello" page_data["meta_description"] = "I am a page" self.client.post(self.get_page_add_uri("en"), page_data) page = Page.objects.get(urls__slug=page_data["slug"]) self.client.post(self.get_page_change_uri("en", page), page_data) t = template.Template( "{% load cms_tags %}{% page_attribute title %} {% page_attribute meta_description %}" ) req = HttpRequest() page.save() req.current_page = page req.GET = {} self.assertEqual(t.render(template.Context({"request": req})), "Hello I am a page") def test_page_obj_change_data_from_template_tags(self): from django import template superuser = self.get_superuser() with self.login_user_context(superuser): page_data = self.get_new_page_data() change_user = str(superuser) # some databases don't store microseconds, so move the start flag # back by 1 second before_change = tz_now() + datetime.timedelta(seconds=-1) self.client.post(self.get_page_add_uri("en"), page_data) page = Page.objects.get(urls__slug=page_data["slug"]) self.client.post(self.get_page_change_uri("en", page), page_data) t = template.Template( "{% load cms_tags %}{% page_attribute changed_by %} changed " "on {% page_attribute changed_date as page_change %}" "{{ page_change|date:'Y-m-d\\TH:i:s' }}" ) req = HttpRequest() page.save() after_change = tz_now() req.current_page = page req.GET = {} actual_result = t.render(template.Context({"request": req})) desired_result = f"{change_user} changed on {actual_result[-19:]}" save_time = datetime.datetime.strptime(actual_result[-19:], "%Y-%m-%dT%H:%M:%S").replace( tzinfo=datetime.timezone.utc if settings.USE_TZ else None ) self.assertEqual(actual_result, desired_result) # direct time comparisons are flaky, so we just check if the # page's changed_date is within the time range taken by this test self.assertLessEqual(before_change, save_time) self.assertLessEqual(save_time, after_change) def test_delete_page_confirmation(self): superuser = self.get_superuser() page_a = create_page("page_a", "nav_playground.html", "en") create_page("page_a_a", "nav_playground.html", "en", parent=page_a) page_a_b = create_page("page_a_b", "nav_playground.html", "en", parent=page_a) create_page("page_a_b_a", "nav_playground.html", "en", parent=page_a_b) endpoint = self.get_admin_url(Page, "delete", page_a.pk) page_tree = [page_a] + list(page_a.get_descendant_pages()) row_markup = "
  • %s
  • " with self.login_user_context(superuser): response = self.client.get(endpoint) for page in page_tree: page_url = page.urls.filter(language="en").first() page_markup = row_markup % str(page_url) self.assertContains(response, page_markup, html=True) def test_delete_page_confirmation_escapes_related_objects(self): """The confirmation screen lists page titles and page urls. Both are author-controlled (a page url can be set verbatim through "Overwrite URL"), so neither may reach the response as markup (CWE-79).""" superuser = self.get_superuser() homepage = create_page("home", "nav_playground.html", "en") homepage.set_as_homepage() page = create_page("", "nav_playground.html", "en", parent=homepage) # An overwrite url without a "/" bypasses ``validate_url`` and is stored verbatim content = page.get_content_obj("en") content.overwrite_url = "" content.save() page.update_urls_from_content("en") page._clear_internal_cache() with self.login_user_context(superuser): response = self.client.get(self.get_admin_url(Page, "delete", page.pk)) self.assertContains(response, "<img src=x onerror=alert('url')> (en)") self.assertContains(response, "<script>alert('title')</script>") self.assertNotContains(response, "alert(") def test_homepage_with_children(self): homepage = create_page("home", "nav_playground.html", "en") homepage.set_as_homepage() pending_child_1 = create_page( "child-1", "nav_playground.html", language="en", parent=homepage, ) pending_child_2 = create_page( "child-2", "nav_playground.html", language="en", parent=homepage, ) expected_tree = [ (homepage, ""), (pending_child_1, "child-1"), (pending_child_2, "child-2"), ] for page, url_path in expected_tree: page._clear_internal_cache() self.assertEqual(page.get_path("en"), url_path) def test_copy_page(self): """ Test that a page can be copied via the admin """ page_a = create_page("page_a", "nav_playground.html", "en") page_a_a = create_page("page_a_a", "nav_playground.html", "en", parent=page_a, reverse_id="hello") create_page("page_a_a_a", "nav_playground.html", "en", parent=page_a_a) page_b = create_page("page_b", "nav_playground.html", "en") page_b_a = create_page("page_b_b", "nav_playground.html", "en", parent=page_b) count = Page.objects.count() superuser = self.get_superuser() with self.login_user_context(superuser): self.copy_page(page_a, page_b_a) self.assertEqual(Page.objects.count() - count, 3) def test_copy_page_preserves_login_required(self): source = create_page("source", "nav_playground.html", "en", login_required=True) source_child = create_page("source-child", "nav_playground.html", "en", parent=source, login_required=True) target = create_page("target", "nav_playground.html", "en") with self.login_user_context(self.get_superuser()): copied_root = self.copy_page(source, target) copied_root.refresh_from_db() copied_child = copied_root.get_child_pages().get() copied_child.refresh_from_db() self.assertTrue(copied_root.login_required) self.assertTrue(copied_child.login_required) self.assertTrue(source_child.login_required) def test_copy_page_under_home(self): """ Users should be able to copy a page and paste under the home page. """ homepage = create_page("home", "nav_playground.html", "en") homepage.set_as_homepage() root_page_a = create_page("root-a", "nav_playground.html", "en") with self.login_user_context(self.get_superuser()): self.copy_page(root_page_a, homepage) def test_copy_page_with_plugins(self): """ Copying a page with plugins should copy all plugins for the translation being copied into the respective translation in the new page. """ cms_page = create_page("page_a_en", "nav_playground.html", "en") placeholder = cms_page.get_placeholders("en").get(slot="body") add_plugin( placeholder, plugin_type="LinkPlugin", language="en", name="Link {}".format("en"), external_link="https://www.django-cms.org", ) with self.login_user_context(self.get_superuser()): new_page = self.copy_page(cms_page, cms_page, position=1) new_placeholder = new_page.get_placeholders("en").get(slot="body") self.assertTrue(new_placeholder.get_plugins("en").exists()) plugin = new_placeholder.get_plugins("en")[0].get_bound_plugin() self.assertEqual(plugin.name, "Link en") def test_copy_page_to_root(self): """ When a page is copied and its slug matches that of another page, add "-copy-2" at the end. """ data = { "position": 2, "copy_permissions": "on", "copy_moderation": "on", } superuser = self.get_superuser() cms_page = create_page("page_a", "nav_playground.html", "en") with self.login_user_context(superuser): endpoint = self.get_admin_url(Page, "copy_page", cms_page.pk) response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 200) new_slug = cms_page.get_path("en") + "-copy-2" new_path = cms_page.get_slug("en") + "-copy-2" self.assertEqual( PageUrl.objects.filter(slug=new_slug, path=new_path).count(), 1, ) def test_copy_page_to_different_site(self): superuser = self.get_superuser() site_2 = Site.objects.create(id=2, domain="example-2.com", name="example-2.com") site_1_root = create_page("site 1 root", "nav_playground.html", "de") site_2_parent = create_page("parent", "nav_playground.html", "de", site=site_2) child_0002 = create_page( "child-0002", template="nav_playground.html", language="de", parent=site_2_parent, site=site_2, ) child_0003 = create_page( "child-0003", template="nav_playground.html", language="de", parent=site_2_parent, site=site_2, ) child_0005 = create_page( "child-0005", template="nav_playground.html", language="de", parent=site_2_parent, site=site_2, ) with self.login_user_context(superuser): # Copy the root page from site 1 and insert it as first child # of the site 2 parent. child_0001 = self.copy_page(site_1_root, site_2_parent, position=0) with self.login_user_context(superuser): # Copy the root page from site 1 and insert it as fourth child # of the site 2 parent. child_0004 = self.copy_page(site_1_root, site_2_parent, position=3) tree = ( (site_2_parent, "0002"), (child_0001, "00020001"), (child_0002, "00020002"), (child_0003, "00020003"), (child_0004, "00020004"), (child_0005, "00020005"), ) for page, path in tree: page.refresh_from_db() self.assertEqual(page.path, path) self.assertEqual(page.site_id, 2) def test_copy_page_to_different_site_fails_with_untranslated_page(self): data = { "position": 0, "copy_permissions": "on", "copy_moderation": "on", } superuser = self.get_superuser() site_2 = Site.objects.create(id=2, domain="example-2.com", name="example-2.com") site_1_root = create_page("site 1 root", "nav_playground.html", "en") expected_response = { "status": 400, "content": "Error! The page you're pasting is not translated in " "any of the languages configured by the target site.", } with self.settings(SITE_ID=2): with self.login_user_context(superuser): # Simulate the copy-dialog endpoint = self.get_admin_url(Page, "get_copy_dialog", site_1_root.pk) response = self.client.get(endpoint) self.assertEqual(response.status_code, 200) # Copy the root page from site 1 and insert it as the first root page # on site 2. endpoint = self.get_admin_url(Page, "copy_page", site_1_root.pk) response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 200) self.assertObjectDoesNotExist(Page.objects.all(), site=site_2) self.assertEqual( json.loads(response.content.decode("utf8")), expected_response, ) def test_copy_page_to_different_site_with_no_pages(self): data = { "position": 0, "copy_permissions": "on", "copy_moderation": "on", } superuser = self.get_superuser() site_2 = Site.objects.create(id=2, domain="example-2.com", name="example-2.com") site_1_root = create_page("site 1 root", "nav_playground.html", "de") with self.settings(SITE_ID=2): with self.login_user_context(superuser): # Simulate the copy-dialog endpoint = self.get_admin_url(Page, "get_copy_dialog", site_1_root.pk) response = self.client.get(endpoint) self.assertEqual(response.status_code, 200) # Copy the root page from site 1 and insert it as the first root page # on site 2. endpoint = self.get_admin_url(Page, "copy_page", site_1_root.pk) response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 200) site_2_root = self.assertObjectExist(Page.objects.all(), site=site_2) tree = ( (site_1_root, "0001"), (site_2_root, "0002"), ) for page, path in tree: page.refresh_from_db() self.assertEqual(page.path, path) def test_copy_page_to_explicit_position(self): """ User should be able to copy a single page and paste it in a specific location on another page tree. """ superuser = self.get_superuser() parent = create_page("parent", "nav_playground.html", "en") child_0002 = create_page("child-0002", "nav_playground.html", "en", parent=parent) child_0003 = create_page("child-0003", "nav_playground.html", "en", parent=parent) child_0005 = create_page("child-0005", "nav_playground.html", "en", parent=parent) child_0004 = create_page("child-0004", "nav_playground.html", "en") with self.login_user_context(superuser): # Copy the 0005 page and insert it as first child of parent child_0001 = self.copy_page(child_0005, parent, position=0) with self.login_user_context(superuser): # Copy the 0004 page and insert it as fourth child of parent child_0004 = self.copy_page(child_0004, parent, position=3) tree = ( (parent, "0001"), (child_0001, "00010001"), (child_0002, "00010002"), (child_0003, "00010003"), (child_0004, "00010004"), (child_0005, "00010005"), ) for page, path in tree: page.refresh_from_db() self.assertEqual(page.path, path) def test_copy_page_tree_to_explicit_position(self): """ User should be able to copy a page with descendants and paste it in a specific location on another page tree. """ superuser = self.get_superuser() parent = create_page("parent", "nav_playground.html", "en") child_0002 = create_page("child-0002", "nav_playground.html", "en", parent=parent) child_0003 = create_page("child-0003", "nav_playground.html", "en", parent=parent) child_0005 = create_page("child-0005", "nav_playground.html", "en", parent=parent) create_page("child-00050001", "nav_playground.html", "en", parent=child_0005) create_page("child-00050002", "nav_playground.html", "en", parent=child_0005) create_page("child-00050003", "nav_playground.html", "en", parent=child_0005) child_0004 = create_page("child-0004", "nav_playground.html", "en") create_page("child-00040001", "nav_playground.html", "en", parent=child_0004) create_page("child-00040002", "nav_playground.html", "en", parent=child_0004) create_page("child-00040003", "nav_playground.html", "en", parent=child_0004) with self.login_user_context(superuser): # Copy the 0005 page and insert it as first child of parent child_0001 = self.copy_page(child_0005, parent, position=0) child_pages = list(child_0001.get_child_pages()) child_00010001 = child_pages[0] child_00010002 = child_pages[1] child_00010003 = child_pages[2] with self.login_user_context(superuser): # Copy the 0004 page and insert it as fourth child of parent child_0004 = self.copy_page(child_0004, parent, position=3) child_pages = list(child_0004.get_child_pages()) child_00040001 = child_pages[0] child_00040002 = child_pages[1] child_00040003 = child_pages[2] tree = ( (parent, "0001"), (child_0001, "00010001"), (child_00010001, "000100010001"), (child_00010002, "000100010002"), (child_00010003, "000100010003"), (child_0002, "00010002"), (child_0003, "00010003"), (child_0004, "00010004"), (child_00040001, "000100040001"), (child_00040002, "000100040002"), (child_00040003, "000100040003"), (child_0005, "00010005"), ) for page, path in tree: page.refresh_from_db() self.assertEqual(page.path, path) def test_copy_self_page(self): """ Test that a page can be copied via the admin """ page_a = create_page("page_a", "nav_playground.html", "en") page_b = create_page("page_b", "nav_playground.html", "en", parent=page_a) page_c = create_page("page_c", "nav_playground.html", "en", parent=page_b) with self.login_user_context(self.get_superuser()): self.copy_page(page_b, page_b, position=1) self.assertEqual(Page.objects.all().count(), 5) self.assertEqual(page_b.get_child_pages().count(), 2) page_d = page_b.get_child_pages()[1] page_e = page_d.get_child_pages()[0] self.assertEqual(page_d.path, "000100010002") self.assertEqual(page_e.path, "0001000100020001") page_e.delete() page_d.delete() with self.login_user_context(self.get_superuser()): self.copy_page(page_b, page_c) page_c.refresh_from_db() self.assertEqual(page_c.get_child_pages().count(), 1) self.assertEqual(page_b.get_child_pages().count(), 1) page_ids = list(page_c.get_descendant_pages().values_list("pk", flat=True)) page_c.get_descendant_pages().delete() Page.objects.filter(pk__in=page_ids).delete() self.assertEqual(Page.objects.all().count(), 3) page_b = page_b.reload() page_c = page_c.reload() with self.login_user_context(self.get_superuser()): self.copy_page(page_b, page_c, position=0) def test_copy_page_tree_with_apphook(self): """ Regression test for issue #5899. Copying a page tree where a page has an apphook should succeed, with the apphook fields cleared on the copied pages. """ root_page = create_page("root", "nav_playground.html", "en") child_with_apphook = create_page( "child_with_apphook", "nav_playground.html", "en", parent=root_page, ) create_page( "grandchild", "nav_playground.html", "en", parent=child_with_apphook, ) child_with_apphook.application_urls = "SampleApp" child_with_apphook.application_namespace = "sample_app_instance" child_with_apphook.save() target_page = create_page("target", "nav_playground.html", "en") initial_count = Page.objects.count() superuser = self.get_superuser() with self.login_user_context(superuser): copied_root = self.copy_page(root_page, target_page, position=0) self.assertEqual(Page.objects.count(), initial_count + 3) copied_children = list(copied_root.get_child_pages()) self.assertEqual(len(copied_children), 1) copied_child = copied_children[0] self.assertIsNone(copied_child.application_urls) self.assertIsNone(copied_child.application_namespace) self.assertIsNone(copied_child.navigation_extenders) child_with_apphook.refresh_from_db() self.assertEqual(child_with_apphook.application_urls, "SampleApp") self.assertEqual(child_with_apphook.application_namespace, "sample_app_instance") def test_move_page(self): superuser = self.get_superuser() add_endpoint = self.get_page_add_uri("en") with self.login_user_context(superuser): page_home = self.get_new_page_data() self.client.post(add_endpoint, page_home) page_data1 = self.get_new_page_data() self.client.post(add_endpoint, page_data1) page_data2 = self.get_new_page_data() self.client.post(add_endpoint, page_data2) page_data3 = self.get_new_page_data() self.client.post(add_endpoint, page_data3) pages = list(Page.objects.order_by("path")) home = pages[0] page1 = pages[1] page2 = pages[2] page3 = pages[3] # move pages response = self.client.post(URL_CMS_PAGE_MOVE % page3.pk, {"target": page2.pk, "position": "0"}) self.assertEqual(response.status_code, 200) page3 = Page.objects.get(pk=page3.pk) response = self.client.post(URL_CMS_PAGE_MOVE % page2.pk, {"target": page1.pk, "position": "0"}) self.assertEqual(response.status_code, 200) # check page2 path and url page2 = Page.objects.get(pk=page2.pk) self.assertEqual(page2.get_path("en"), page_data1["slug"] + "/" + page_data2["slug"]) self.assertEqual( page2.get_absolute_url(), self.get_pages_root() + page_data1["slug"] + "/" + page_data2["slug"] + "/" ) # check page3 path and url page3 = Page.objects.get(pk=page3.pk) self.assertEqual( page3.get_path("en"), page_data1["slug"] + "/" + page_data2["slug"] + "/" + page_data3["slug"] ) self.assertEqual( page3.get_absolute_url(), self.get_pages_root() + page_data1["slug"] + "/" + page_data2["slug"] + "/" + page_data3["slug"] + "/", ) # Remove home page home.delete() # Promote page1 to be the new homepage page1.set_as_homepage() self.assertEqual(page1.get_path("en"), "") # check that page2 and page3 url have changed page2 = Page.objects.get(pk=page2.pk) page3 = Page.objects.get(pk=page3.pk) # set page2 as root and check path of 1 and 3 response = self.client.post(URL_CMS_PAGE_MOVE % page2.pk, {"position": "0"}) self.assertEqual(response.status_code, 200) page1 = Page.objects.get(pk=page1.pk) self.assertEqual(page1.get_path("en"), "") page2 = Page.objects.get(pk=page2.pk) self.assertFalse(page2.is_home) self.assertEqual(page2.get_path("en"), page_data2["slug"]) page3 = Page.objects.get(pk=page3.pk) self.assertEqual(page3.get_path("en"), page_data2["slug"] + "/" + page_data3["slug"]) def test_user_cant_nest_home_page(self): """ Users should not be able to move the home-page inside another node of the tree. """ homepage = create_page("home", "nav_playground.html", "en") homepage.set_as_homepage() home_sibling_1 = create_page("root-1", "nav_playground.html", "en") payload = {"id": homepage.pk, "position": 0, "target": home_sibling_1} with self.login_user_context(self.get_superuser()): endpoint = self.get_admin_url(Page, "move_page", homepage.pk) response = self.client.post(endpoint, payload) self.assertEqual(response.status_code, 200) self.assertEqual(response.json().get("status", 400), 400) def test_move_home_page(self): """ Users should be able to move the home-page anywhere on the root of the tree. """ homepage = create_page("home", "nav_playground.html", "en") homepage.set_as_homepage() home_child_1 = create_page( "child-1", "nav_playground.html", language="en", parent=homepage, ) home_child_2 = create_page( "child-2", "nav_playground.html", language="en", parent=homepage, ) home_sibling_1 = create_page("root-1", "nav_playground.html", "en") expected_tree = [ # Sadly treebeard doesn't switch the paths (home_sibling_1, "0002", "root-1"), (homepage, "0003", ""), (home_child_1, "00030001", "child-1"), (home_child_2, "00030002", "child-2"), ] with self.login_user_context(self.get_superuser()): # Moves the homepage to the second position in the tree data = {"id": homepage.pk, "position": 1} endpoint = self.get_admin_url(Page, "move_page", homepage.pk) response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 200) for page, page_path, url_path in expected_tree: page._clear_internal_cache() page.refresh_from_db() self.assertEqual(page.path, page_path) self.assertEqual(page.get_path("en"), url_path) def test_move_page_integrity(self): superuser = self.get_superuser() with self.login_user_context(superuser): page_home = self.get_new_page_data() self.client.post(self.get_page_add_uri("en"), page_home) # Create parent page page_root = create_page("Parent", "col_three.html", "en") # Create child pages create_page( "Child 1", template=constants.TEMPLATE_INHERITANCE_MAGIC, language="en", parent=page_root, ) create_page( "Child 2", template=constants.TEMPLATE_INHERITANCE_MAGIC, language="en", parent=page_root, ) # Create two root pages that ware meant as child pages page_child_3 = create_page("Child 3", "col_three.html", "en") page_child_4 = create_page("Child 4", "col_three.html", "en") # Correct our mistake. # Move page_child_3 to be child of parent page data = { "id": page_child_3.pk, "target": page_root.pk, "position": "0", } response = self.client.post( URL_CMS_PAGE_MOVE % page_child_3.pk, data, ) self.assertEqual(response.status_code, 200) # Move page_child_4 to be child of parent page data = { "id": page_child_4.pk, "target": page_root.pk, "position": "0", } response = self.client.post( URL_CMS_PAGE_MOVE % page_child_4.pk, data, ) self.assertEqual(response.status_code, 200) page_root = page_root.reload() page_child_4 = page_child_4.reload() # Ensure move worked self.assertEqual(page_root.get_descendants().count(), 4) def test_edit_page_other_site_and_language(self): """ Test that a page can edited via the admin when your current site is different from the site you are editing and the language isn't available for the current site. """ self.assertEqual(Site.objects.all().count(), 1) site = Site.objects.create(domain="otherlang", name="otherlang", pk=2) # Change site for this session page_data = self.get_new_page_data() page_data["title"] = "changed title" self.assertEqual(site.pk, 2) TESTLANG = get_cms_setting("LANGUAGES")[site.pk][0]["code"] TESTSITE = site.pk page_data["language"] = TESTLANG page_data["site"] = TESTSITE superuser = self.get_superuser() with self.login_user_context(superuser): response = self.client.post(self.get_page_add_uri("en"), page_data) self.assertRedirects(response, self.get_pages_admin_list_uri(TESTLANG, site_id=TESTSITE)) page = Page.objects.get(urls__slug=page_data["slug"]) with LanguageOverride(TESTLANG): self.assertEqual(page.get_title(), "changed title") def test_get_page_from_request_cached(self): mock_page = "hello world" request = self.get_request(admin_reverse("sampleapp_category_change", args=(1,))) request._current_page_cache = mock_page page = get_page_from_request(request) self.assertEqual(page, mock_page) @override_settings(CMS_PERMISSION=False) def test_set_overwrite_url(self): superuser = self.get_superuser() cms_page = create_page("page", "nav_playground.html", "en") translation = cms_page.get_content_obj("en", fallback=False) expected = ( '' ) changelist = self.get_pages_admin_list_uri() endpoint = self.get_page_change_uri("en", cms_page) with self.login_user_context(superuser): page_data = { "title": translation.title, "slug": cms_page.get_slug("en"), "overwrite_url": "/new-url/", "template": translation.template, } response = self.client.post(endpoint, page_data) self.assertRedirects(response, changelist) with self.login_user_context(superuser): response = self.client.get(endpoint) self.assertContains(response, expected, html=True) @override_settings(CMS_PERMISSION=False) def test_set_existing_overwrite_url(self): superuser = self.get_superuser() create_page("home", "nav_playground.html", "en") boo = create_page("boo", "nav_playground.html", "en") hoo = create_page("hoo", "nav_playground.html", "en") translation = hoo.get_content_obj("en", fallback=False) expected_error = ( '" ).format(self.get_page_change_uri("en", boo)) with self.login_user_context(superuser): endpoint = self.get_page_change_uri("en", hoo) page_data = { "title": translation.title, "slug": hoo.get_slug("en"), "overwrite_url": "/boo/", "template": translation.template, } response = self.client.post(endpoint, page_data) self.assertEqual(response.status_code, 200) self.assertContains(response, expected_error, html=True) @override_settings(CMS_PERMISSION=False) def test_remove_overwrite_url(self): superuser = self.get_superuser() cms_page = create_page( "page", "nav_playground.html", language="en", overwrite_url="/new-url/", ) translation = cms_page.get_content_obj("en", fallback=False) expected = ( '' ) changelist = self.get_pages_admin_list_uri() endpoint = self.get_page_change_uri("en", cms_page) # control test self.assertTrue(cms_page.urls.filter(path="new-url").exists()) with self.login_user_context(superuser): page_data = { "title": translation.title, "slug": cms_page.get_slug("en"), "overwrite_url": "", "template": translation.template, } response = self.client.post(endpoint, page_data) self.assertRedirects(response, changelist) with self.login_user_context(superuser): response = self.client.get(endpoint) self.assertContains(response, expected, html=True) @override_settings(CMS_PERMISSION=False) def test_slug_stored_on_page_content(self): # The slug and overwrite URL are authored on the PageContent object; # the PageUrl is derived from it. superuser = self.get_superuser() cms_page = create_page("page", "nav_playground.html", "en") translation = cms_page.get_content_obj("en", fallback=False) changelist = self.get_pages_admin_list_uri() endpoint = self.get_page_change_uri("en", cms_page) self.assertEqual(translation.slug, "page") with self.login_user_context(superuser): page_data = { "title": translation.title, "slug": "new-slug", "overwrite_url": "", "template": translation.template, } response = self.client.post(endpoint, page_data) self.assertRedirects(response, changelist) translation.refresh_from_db() self.assertEqual(translation.slug, "new-slug") # The content is public (no versioning package), so the URL follows. self.assertEqual(cms_page.reload().get_slug("en"), "new-slug") @override_settings(CMS_PERMISSION=False) def test_url_not_synced_for_non_public_content(self): # When the edited content is not publicly visible (as with a draft # version created by a versioning package), saving a new slug is stored # on the content but must not touch the page's published URL. superuser = self.get_superuser() cms_page = create_page("page", "nav_playground.html", "en") translation = cms_page.get_content_obj("en", fallback=False) changelist = self.get_pages_admin_list_uri() endpoint = self.get_page_change_uri("en", cms_page) with self.login_user_context(superuser): page_data = { "title": "A new title", "slug": "a-changed-slug", "overwrite_url": "", "template": translation.template, } with patch.object(PageContent, "is_public", return_value=False): response = self.client.post(endpoint, page_data) self.assertRedirects(response, changelist) # The published URL is untouched ... self.assertEqual(cms_page.get_slug("en"), "page") self.assertFalse(cms_page.urls.filter(slug="a-changed-slug").exists()) # ... while the content carries the new slug and other saved fields. translation.refresh_from_db() self.assertEqual(translation.slug, "a-changed-slug") self.assertEqual(translation.title, "A new title") @override_settings(CMS_PERMISSION=False) def test_update_urls_from_content(self): # The publish-time hook of versioning packages: derives the PageUrl # from the publicly visible content and updates descendant paths. cms_page = create_page("page", "nav_playground.html", "en", slug="page") child = create_page("child", "nav_playground.html", "en", parent=cms_page, slug="child") translation = cms_page.get_content_obj("en", fallback=False) translation.update(slug="renamed") cms_page.update_urls_from_content("en") self.assertEqual(cms_page.reload().get_slug("en"), "renamed") self.assertEqual(child.reload().get_path("en"), "renamed/child") # An overwrite URL on the content wins over the derived path. translation.update(overwrite_url="somewhere/else") cms_page.update_urls_from_content("en") url = cms_page.reload().get_url("en") self.assertEqual(url.path, "somewhere/else") self.assertFalse(url.managed) @override_settings(CMS_PERMISSION=False) def test_update_urls_from_content_without_public_content(self): # Without publicly visible content the path is invalidated so the page # stops resolving, but the slug stays reserved. cms_page = create_page("page", "nav_playground.html", "en", slug="page") with patch.object(PageContent.objects, "filter", return_value=PageContent.objects.none()): cms_page.update_urls_from_content("en") url = cms_page.urls.get(language="en") self.assertIsNone(url.path) self.assertEqual(url.slug, "page") @override_settings(CMS_PERMISSION=False) def test_update_urls_from_content_updates_deep_descendants(self): # The level-based descendant update covers the whole subtree; pages # below an overwritten URL derive from its fixed path. cms_page = create_page("page", "nav_playground.html", "en", slug="page") child = create_page("child", "nav_playground.html", "en", parent=cms_page, slug="child") grandchild = create_page("grandchild", "nav_playground.html", "en", parent=child, slug="grandchild") overwritten = create_page( "fixed", "nav_playground.html", "en", parent=child, slug="fixed", overwrite_url="fixed/url" ) under_overwritten = create_page("leaf", "nav_playground.html", "en", parent=overwritten, slug="leaf") cms_page.get_content_obj("en", fallback=False).update(slug="renamed") cms_page.update_urls_from_content("en") self.assertEqual(child.reload().get_path("en"), "renamed/child") self.assertEqual(grandchild.reload().get_path("en"), "renamed/child/grandchild") self.assertEqual(overwritten.reload().get_path("en"), "fixed/url") self.assertEqual(under_overwritten.reload().get_path("en"), "fixed/url/leaf") @override_settings(CMS_PERMISSION=False) def test_page_url_path_unique_per_site_in_database(self): # The database enforces path uniqueness per site and language ... create_page("page", "nav_playground.html", "en", slug="page") other = create_page("other", "nav_playground.html", "en", slug="other") with self.assertRaises(IntegrityError), transaction.atomic(): # a queryset update bypasses the application-level checks other.urls.filter(language="en").update(path="page") # ... while any number of unreachable pages (path=None) may coexist third = create_page("third", "nav_playground.html", "en", slug="third") other.urls.filter(language="en").update(path=None) third.urls.filter(language="en").update(path=None) self.assertEqual(PageUrl.objects.filter(path__isnull=True, language="en").count(), 2) @override_settings(CMS_PERMISSION=False) def test_unpublished_parent_invalidates_child_urls(self): # When a parent loses its path (e.g. it is unpublished), its children # become unreachable too: their paths are invalidated and # get_absolute_url returns None instead of a fabricated slug-only url. parent = create_page("parent", "nav_playground.html", "en", slug="parent") child = create_page("child", "nav_playground.html", "en", parent=parent, slug="child") with patch.object(PageContent.objects, "filter", return_value=PageContent.objects.none()): parent.update_urls_from_content("en") child = child.reload() url = child.urls.get(language="en") self.assertIsNone(url.path) self.assertEqual(url.slug, "child") self.assertIsNone(child.get_absolute_url("en")) # Saving or publishing the child while the parent has no path keeps # the child unreachable instead of exposing it at a slug-only path. child.update_urls_from_content("en") url = child.urls.get(language="en") self.assertIsNone(url.path) self.assertIsNone(child.get_absolute_url("en")) @override_settings(CMS_PERMISSION=False) def test_advanced_settings_form_apphook(self): superuser = self.get_superuser() cms_page = create_page("app", "nav_playground.html", "en") cms_pages = Page.objects.filter(pk=cms_page.pk) redirect_to = self.get_pages_admin_list_uri(site_id=cms_page.site_id) endpoint = self.get_admin_url(Page, "advanced", cms_page.pk) page_data = { "reverse_id": "", "navigation_extenders": "", "application_urls": "SampleApp", "application_namespace": "sampleapp", } with self.login_user_context(superuser): # set the apphook response = self.client.post(endpoint, page_data) self.assertRedirects(response, redirect_to) self.assertEqual( cms_pages.filter( application_urls="SampleApp", application_namespace="sampleapp", ).count(), 1, ) with self.login_user_context(superuser): # remove the apphook page_data["application_urls"] = "" page_data["application_namespace"] = "" response = self.client.post(endpoint, page_data) self.assertRedirects(response, redirect_to) self.assertEqual( cms_pages.filter( application_urls="", application_namespace=None, ).count(), 1, ) @override_settings( CMS_APPHOOKS=[ "cms.test_utils.project.sampleapp.cms_apps.SampleApp", "cms.test_utils.project.sampleapp.cms_apps.SampleAppWithConfig", ], CMS_PERMISSION=False, ) def test_advanced_settings_form_apphook_config(self): clear_app_resolvers() clear_url_caches() if "cms.test_utils.project.sampleapp.cms_apps" in sys.modules: del sys.modules["cms.test_utils.project.sampleapp.cms_apps"] self.apphook_clear() superuser = self.get_superuser() app_config = SampleAppConfig.objects.create(namespace="sample") cms_page = create_page("app", "nav_playground.html", "en") cms_pages = Page.objects.filter(pk=cms_page.pk) redirect_to = self.get_pages_admin_list_uri(site_id=cms_page.site_id) endpoint = self.get_admin_url(Page, "advanced", cms_page.pk) page_data = { "reverse_id": "", "navigation_extenders": "", "application_urls": "SampleAppWithConfig", "application_configs": app_config.pk, "application_namespace": "sampleapp", } with self.login_user_context(superuser): # set the apphook config response = self.client.post(endpoint, page_data) self.assertRedirects(response, redirect_to) self.assertEqual( cms_pages.filter( application_urls="SampleAppWithConfig", application_namespace=app_config.namespace, ).count(), 1, ) with self.login_user_context(superuser): # change from apphook with config to normal apphook page_data["application_urls"] = "SampleApp" page_data["application_namespace"] = "sampleapp" response = self.client.post(endpoint, page_data) self.assertRedirects(response, redirect_to) self.assertEqual( cms_pages.filter( application_urls="SampleApp", application_namespace="sampleapp", ).count(), 1, ) with self.login_user_context(superuser): # set the apphook config again page_data["application_urls"] = "SampleAppWithConfig" page_data["application_namespace"] = "sampleapp" response = self.client.post(endpoint, page_data) self.assertRedirects(response, redirect_to) self.assertEqual( cms_pages.filter( application_urls="SampleAppWithConfig", application_namespace=app_config.namespace, ).count(), 1, ) with self.login_user_context(superuser): # change the apphook config to an invalid value expected_error = '' page_data["application_configs"] = "2" response = self.client.post(endpoint, page_data) self.assertEqual(response.status_code, 200) self.assertContains(response, expected_error) self.assertEqual( cms_pages.filter( application_urls="SampleAppWithConfig", application_namespace=app_config.namespace, ).count(), 1, ) with self.login_user_context(superuser): # remove the apphook page_data["application_urls"] = "" page_data["application_namespace"] = "" response = self.client.post(endpoint, page_data) self.assertRedirects(response, redirect_to) self.assertEqual( cms_pages.filter( application_urls="", application_namespace=None, ).count(), 1, ) clear_app_resolvers() clear_url_caches() if "cms.test_utils.project.sampleapp.cms_apps" in sys.modules: del sys.modules["cms.test_utils.project.sampleapp.cms_apps"] self.apphook_clear() def test_advanced_settings_view_on_site(self): """Advanced Page Settings `View on Site` object tool links to the Page's current language content preview url""" superuser = self.get_superuser() cms_page = create_page("app", "nav_playground.html", "en") cms_page_content = cms_page.get_content_obj(language="en") endpoint = self.get_admin_url(Page, "advanced", cms_page.pk) with self.login_user_context(superuser): response = self.client.get(endpoint) self.assertContains(response, get_object_edit_url(cms_page_content, language="en")) def test_form_url_page_change(self): superuser = self.get_superuser() with self.login_user_context(superuser): content_admin = PageContentAdmin(PageContent, admin.site) page = self.get_page() content = self.get_pagecontent_obj(page, "en") form_url = self.get_page_change_uri("en", page) # Middleware is needed to correctly setup the environment for the admin request = self.get_request() middleware = CurrentUserMiddleware(lambda req: HttpResponse("")) middleware(request) response = content_admin.change_view( request, str(content.pk), form_url=form_url, ) self.assertTrue("form_url" in response.context_data) self.assertEqual(response.context_data["form_url"], form_url) def test_pagecontent_slug_needs_no_extra_queries(self): # slug and overwrite_url live on the PageContent object itself, so # read-only admin rendering needs no PageUrl lookups. page1 = self.get_page() content1 = self.get_pagecontent_obj(page1, "en") page2 = self.get_page() content2 = self.get_pagecontent_obj(page2, "en") with self.assertNumQueries(0): self.assertNotEqual(content1.slug, content2.slug) self.assertIsNone(content1.overwrite_url) def test_change_view_shows_language_tabs_for_latest_content(self): """The page content change view offers the language selector for the latest content.""" superuser = self.get_superuser() page = self.get_page() content = self.get_pagecontent_obj(page, "en") change_url = admin_reverse("cms_pagecontent_change", args=(content.pk,)) with self.login_user_context(superuser): response = self.client.get(change_url) self.assertContains(response, 'id="page_form_lang_tabs"') def test_change_view_drops_language_tabs_for_non_latest_content(self): """The page content change view drops the language selector when an older (non-latest) content object is shown. Switching languages back and forth would otherwise silently bring up the latest content instead - confusing UX.""" superuser = self.get_superuser() page = self.get_page() content = self.get_pagecontent_obj(page, "en") change_url = admin_reverse("cms_pagecontent_change", args=(content.pk,)) # Simulate a versioning package: the latest content for this language differs from the # content object being shown. fake_latest = type("FakeContent", (), {"pk": content.pk + 1000})() with patch("cms.models.pagemodel.Page.get_admin_content", return_value=fake_latest): with self.login_user_context(superuser): response = self.client.get(change_url) self.assertNotContains(response, 'id="page_form_lang_tabs"') def _parse_page_tree(self, response, parser_class): content = response.content content = content.decode(response.charset) def _parse_html(html): parser = parser_class() parser.feed(html) parser.close() document = parser.root document.finalize() # Removing ROOT element if it's not necessary if len(document.children) == 1: if not isinstance(document.children[0], str): document = document.children[0] return document try: dom = _parse_html(content) except HTMLParseError as e: standardMsg = "{}\n{}".format("Response's content is not valid HTML", e.msg) self.fail(self._formatMessage(None, standardMsg)) return dom def test_page_tree_regression_5892(self): # ref: https://github.com/divio/django-cms/issues/5892 # Tests the escaping of characters for a german translation # in the page tree. superuser = self.get_superuser() create_page("Home", "nav_playground.html", "en") alpha = create_page("Alpha", "nav_playground.html", "en") create_page("Beta", "nav_playground.html", "en", parent=alpha) create_page("Gamma", "nav_playground.html", "en") with self.login_user_context(superuser): with force_language("de"): endpoint = self.get_admin_url(PageContent, "get_tree") response = self.client.get(endpoint) self.assertEqual(response.status_code, 200) parsed = self._parse_page_tree(response, parser_class=PageTreeOptionsParser) content = force_str(parsed) self.assertIn("(Shift-Klick für erweiterte Einstellungen)", content) def test_page_get_tree_endpoint_flat(self): superuser = self.get_superuser() endpoint = self.get_admin_url(PageContent, "get_tree") create_page("Home", "nav_playground.html", "en") alpha = create_page("Alpha", "nav_playground.html", "en") create_page("Beta", "nav_playground.html", "en", parent=alpha) create_page("Gamma", "nav_playground.html", "en") tree = "
  • \nHome\n
  • " "
  • \nAlpha\n
  • " "
  • \nGamma\n
  • " with self.login_user_context(superuser): response = self.client.get(endpoint) self.assertEqual(response.status_code, 200) parsed = self._parse_page_tree(response, parser_class=PageTreeLiParser) content = force_str(parsed) self.assertIn(tree, content) self.assertNotIn("
  • \nBeta\n
  • ", content) def test_page_get_tree_different_site(self): superuser = self.get_superuser() site = Site.objects.create(id=2, domain="example-2.com", name="example-2.com") with self.settings(CMS_LANGUAGES={2: [{"code": "en", "name": "English"}]}): create_page("Home", "nav_playground.html", "en", site=site) alpha = create_page("Alpha", "nav_playground.html", "en", site=site) create_page("Beta", "nav_playground.html", "en", parent=alpha) create_page("Gamma", "nav_playground.html", "en", site=site) tree = "
  • \nHome\n
  • " "
  • \nAlpha\n
  • " "
  • \nGamma\n
  • " endpoint_default = self.get_admin_url(PageContent, "get_tree") endpoint_site = self.get_admin_url(PageContent, "get_tree") + "?site=2" with self.login_user_context(superuser): response = self.client.get(endpoint_default) self.assertEqual(response.status_code, 200) parsed = self._parse_page_tree(response, parser_class=PageTreeLiParser) content = force_str(parsed) self.assertNotIn(tree, content) response = self.client.get(endpoint_site) self.assertEqual(response.status_code, 200) parsed = self._parse_page_tree(response, parser_class=PageTreeLiParser) content = force_str(parsed) self.assertIn(tree, content) def test_page_get_tree_endpoint_nested(self): superuser = self.get_superuser() endpoint = self.get_admin_url(PageContent, "get_tree") create_page("Home", "nav_playground.html", "en") alpha = create_page("Alpha", "nav_playground.html", "en") create_page("Beta", "nav_playground.html", "en", parent=alpha) gamma = create_page("Gamma", "nav_playground.html", "en") create_page("Delta", "nav_playground.html", "en", parent=gamma) create_page("Theta", "nav_playground.html", "en") tree = ( "
  • \nHome\n
  • " "
  • \nAlpha" "\n
  • " "
  • \nGamma" "\n
  • " "
  • \nTheta\n
  • " ) data = {"openNodes[]": [alpha.pk, gamma.pk]} with self.login_user_context(superuser): response = self.client.get(endpoint, data=data) self.assertEqual(response.status_code, 200) parsed = self._parse_page_tree(response, parser_class=PageTreeLiParser) content = force_str(parsed) self.assertIn(tree, content) def test_page_tree_prefetches_page_urls(self): """ Regression test: the page tree endpoints must fetch the ``PageUrl`` objects of all pages in bulk instead of issuing one query per page row (triggered by ``Page.get_url_obj`` filling ``urls_cache`` from ``self.urls.all()`` for every rendered row). """ superuser = self.get_superuser() create_page("Home", "nav_playground.html", "en") for index in range(4): create_page(f"Page {index}", "nav_playground.html", "en") endpoints = ( self.get_admin_url(PageContent, "get_tree"), self.get_admin_url(PageContent, "changelist"), ) with self.login_user_context(superuser): for endpoint in endpoints: with self.subTest(endpoint=endpoint): with CaptureQueriesContext(connection) as queries: response = self.client.get(endpoint) self.assertEqual(response.status_code, 200) page_url_queries = [ query["sql"] for query in queries.captured_queries if '"cms_pageurl"' in query["sql"] ] self.assertLessEqual( len(page_url_queries), 1, "The page tree issued one PageUrl query per page instead " "of prefetching them:\n" + "\n".join(page_url_queries), ) def test_page_tree_does_not_requery_content_less_pages(self): """ Regression test: pages without any (filtered) translations must not trigger a fresh ``set_admin_content_cache`` query per row. An empty admin content cache used to be indistinguishable from an unpopulated one, so ``get_admin_content`` / ``get_languages`` re-queried the database for every content-less page in the tree. """ superuser = self.get_superuser() endpoint = self.get_admin_url(PageContent, "get_tree") def add_content_less_pages(count, prefix): for index in range(count): page = create_page(f"{prefix}-{index}", "nav_playground.html", "en") PageContent.admin_manager.filter(page=page).delete() create_page("Home", "nav_playground.html", "en") add_content_less_pages(2, "empty-a") with self.login_user_context(superuser): self.client.get(endpoint) # warm up caches (content types, permissions, ...) with CaptureQueriesContext(connection) as first: self.assertEqual(self.client.get(endpoint).status_code, 200) add_content_less_pages(3, "empty-b") with CaptureQueriesContext(connection) as second: self.assertEqual(self.client.get(endpoint).status_code, 200) self.assertEqual( len(second.captured_queries), len(first.captured_queries), "The page tree issued extra queries for content-less pages:\n" + "\n".join(query["sql"] for query in second.captured_queries), ) def test_page_tree_redirect_icon_display(self): """Test that redirect icon is displayed in page tree when page has redirect""" superuser = self.get_superuser() endpoint = self.get_admin_url(PageContent, "get_tree") # Create pages create_page("Home", "nav_playground.html", "en") page_with_redirect = create_page("About", "nav_playground.html", "en") create_page("Contact", "nav_playground.html", "en") # Set redirect on one page page_content = page_with_redirect.get_content_obj(language="en") page_content.redirect = "/home/" page_content.save() with self.login_user_context(superuser): response = self.client.get(endpoint) self.assertEqual(response.status_code, 200) content = response.content.decode("utf-8") # Check that redirect icon is present for page with redirect # The icon should have the class "cms-icon-redirect" self.assertIn("cms-icon-redirect", content) # Verify the icon is in the correct part of the page tree # by checking for the redirect tooltip text self.assertIn("data-cms-tooltip", content) self.assertIn("redirect", content.lower()) def test_page_tree_redirect_icon_click_opens_edit(self): """Test that clicking redirect icon in tree opens the page edit view""" superuser = self.get_superuser() # Create a page with redirect page = create_page("About", "nav_playground.html", "en") page_content = page.get_content_obj(language="en") page_content.redirect = "/home/" page_content.save() endpoint = self.get_admin_url(PageContent, "get_tree") with self.login_user_context(superuser): response = self.client.get(endpoint) self.assertEqual(response.status_code, 200) content = response.content.decode("utf-8") # The redirect icon should link to the page edit URL # The link uses the PageContent change admin URL admin_url = self.get_admin_url(PageContent, "change", page_content.pk) self.assertIn(admin_url, content) # Verify the link wraps the redirect icon # by checking for the pattern: self.assertRegex( content, rf']*>\s*' ) def test_page_tree_no_redirect_icon_for_normal_pages(self): """Test that pages without redirects don't show redirect icon""" superuser = self.get_superuser() endpoint = self.get_admin_url(PageContent, "get_tree") # Create a page without redirect create_page("Home", "nav_playground.html", "en") create_page("About", "nav_playground.html", "en") with self.login_user_context(superuser): response = self.client.get(endpoint) self.assertEqual(response.status_code, 200) # Parse the response and verify structure # We expect the redirect icon section to not be present # for pages without redirects parsed = self._parse_page_tree(response, parser_class=PageTreeLiParser) content = force_str(parsed) # The response should contain page structure self.assertIn("Home", content) self.assertIn("About", content) self.assertNotIn("cms-icon-redirect", content) def test_page_changelist_search(self): superuser = self.get_superuser() endpoint = self.get_pages_admin_list_uri() create_page("Home", "nav_playground.html", "en") alpha = create_page("Alpha", "nav_playground.html", "en") create_page("Beta", "nav_playground.html", "en", parent=alpha) create_page("Gamma", "nav_playground.html", "en") with self.login_user_context(superuser): response = self.client.get(endpoint, data={"q": "alpha"}) self.assertEqual(response.status_code, 200) parsed = self._parse_page_tree(response, parser_class=PageTreeLiParser) content = force_str(parsed) self.assertIn("
  • \nAlpha\n
  • ", content) self.assertNotIn("
  • \nHome\n
  • ", content) self.assertNotIn("
  • \nBeta\n
  • ", content) self.assertNotIn("
  • \nGamma\n
  • ", content) def test_global_limit_on_plugin_move(self): superuser = self.get_superuser() cms_page = self.get_page() source_placeholder = cms_page.get_placeholders("en").get(slot="right-column") target_placeholder = cms_page.get_placeholders("en").get(slot="body") data = { "placeholder": source_placeholder, "plugin_type": "LinkPlugin", "language": "en", } plugin_1 = add_plugin(**data) plugin_2 = add_plugin(**data) plugin_3 = add_plugin(**data) with UserLoginContext(self, superuser): with override_placeholder_conf(self.placeholderconf): data = self._get_move_data(plugin_1, position=1, placeholder=target_placeholder) endpoint = self.get_move_plugin_uri(plugin_1) response = self.client.post(endpoint, data) # first self.assertEqual(response.status_code, 200) data = self._get_move_data(plugin_2, position=2, placeholder=target_placeholder) endpoint = self.get_move_plugin_uri(plugin_2) response = self.client.post(endpoint, data) # second self.assertEqual(response.status_code, 200) data = self._get_move_data(plugin_3, position=3, placeholder=target_placeholder) endpoint = self.get_move_plugin_uri(plugin_3) response = self.client.post(endpoint, data) # third self.assertEqual(response.status_code, 400) self.assertEqual(response.content, b"This placeholder already has the maximum number of plugins (2).") def test_type_limit_on_plugin_move(self): superuser = self.get_superuser() cms_page = self.get_page() source_placeholder = cms_page.get_placeholders("en").get(slot="right-column") target_placeholder = cms_page.get_placeholders("en").get(slot="body") data = { "placeholder": source_placeholder, "plugin_type": "TextPlugin", "language": "en", } plugin_1 = add_plugin(**data) plugin_2 = add_plugin(**data) with UserLoginContext(self, superuser): with override_placeholder_conf(self.placeholderconf): data = self._get_move_data(plugin_1, position=1, placeholder=target_placeholder) endpoint = self.get_move_plugin_uri(plugin_1) response = self.client.post(endpoint, data) # first self.assertEqual(response.status_code, 200) data = self._get_move_data(plugin_2, position=2, placeholder=target_placeholder) endpoint = self.get_move_plugin_uri(plugin_1) response = self.client.post(endpoint, data) # second self.assertEqual(response.status_code, 400) self.assertEqual( response.content, b"This placeholder already has the maximum number (1) of allowed Text plugins." ) @skipUnless( "sqlite" in settings.DATABASES.get("default").get("ENGINE").lower(), "This test only works in SQLITE", ) @override_settings(USE_THOUSAND_SEPARATOR=True, USE_L10N=True) def test_page_tree_render_localized_page_ids(self): from django.db import connection # Artificially increment the sequence number on cms_page (below) # to be > 1000, to trigger a THOUSAND_SEPARATOR localization in the # rendered template admin_user = self.get_superuser() root = create_page( "home", "nav_playground.html", "fr", created_by=admin_user, ) with connection.cursor() as c: c.execute('UPDATE SQLITE_SEQUENCE SET seq = 1001 WHERE name="cms_page"') page = create_page( "child-page", "nav_playground.html", "fr", created_by=admin_user, parent=root, slug="child-page", ) sub_page = create_page( "grand-child-page", "nav_playground.html", "fr", created_by=admin_user, parent=page, slug="grand-child-page", ) self.assertTrue(page.id > 1000) self.assertTrue(sub_page.id > 1000) self.assertTrue(page.id > 1000) self.assertTrue(sub_page.id > 1000) # make sure the rendered page tree doesn't # localize page or node ids with self.login_user_context(admin_user): data = {"openNodes[]": [root.pk, page.pk], "language": "fr"} endpoint = self.get_admin_url(PageContent, "get_tree") response = self.client.get(endpoint, data=data) self.assertEqual(response.status_code, 200) self.assertNotContains(response, f"page={page.pk:,}") self.assertContains(response, f"parent_page={page.pk}") # if per chance we have localized node ids in our localstorage, # make sure DjangoCMS doesn't choke on them when they are passed # into the view with self.login_user_context(admin_user): data = {"openNodes[]": [root.pk, f"{page.pk:,}"], "language": "fr"} endpoint = self.get_admin_url(PageContent, "get_tree") response = self.client.get(endpoint, data=data) self.assertEqual(response.status_code, 200) class PageActionsTestCase(PageTestBase): def setUp(self): self.admin = self.get_superuser() self.site = Site.objects.get(pk=1) self.page = create_page( "My Page", "nav_playground.html", "en", slug="ok", site=self.site, created_by=self.admin ) def test_add_page_redirect(self): """When adding the edit parameter to the add page form, the user should be redirected to the edit endpoint of the new page.""" with self.login_user_context(self.admin): # add page page_data = { "title": "another page", "slug": "type1", "template": "nav_playground.html", "language": "en", "edit": 1, } self.assertEqual(Page.objects.all().count(), 1) response = self.client.post( self.get_admin_url(PageContent, "add"), data=page_data, ) redirect_url = get_object_edit_url(PageContent.objects.get(title="another page")) self.assertContains(response, f'href="{redirect_url}"') self.assertEqual(Page.objects.all().count(), 2) def test_add_page_no_redirect(self): with self.login_user_context(self.admin): # add page page_data = { "title": "another page", "slug": "type1", "template": "nav_playground.html", "language": "en", "edit": 0, } self.assertEqual(Page.objects.all().count(), 1) response = self.client.post( self.get_admin_url(PageContent, "add"), data=page_data, ) redirect_url = self.get_admin_url(PageContent, "changelist") + "?language=en" self.assertRedirects(response, redirect_url) self.assertEqual(Page.objects.all().count(), 2) def test_get_list_is_scoped_to_requested_site(self): site2 = Site.objects.create(id=2, name="example-2.com", domain="example-2.com") create_page( "Bravo Site 1", "nav_playground.html", "en", site=self.site, slug="bravo-site-1", created_by=self.admin, ) with self.settings(CMS_LANGUAGES={2: [{"code": "en", "name": "English"}]}): create_page( "Bravo Site 2", "nav_playground.html", "en", site=site2, slug="bravo-site-2", created_by=self.admin, ) endpoint = admin_reverse("cms_page_get_list") with self.login_user_context(self.admin): response = self.client.get( endpoint, data={"site": self.site.pk, "q": "Bravo", "language_code": "en"}, HTTP_X_REQUESTED_WITH="XMLHttpRequest", ) self.assertEqual(response.status_code, 200) results = json.loads(response.content.decode("utf-8")) titles = {result["title"] for result in results} self.assertIn("Bravo Site 1", titles) self.assertNotIn("Bravo Site 2", titles) def test_get_list_ignores_an_empty_query(self): """``icontains`` matches everything for an empty term, so an empty query would turn the autocomplete into a listing of every page on the site.""" create_page("Bravo", "nav_playground.html", "en", site=self.site, created_by=self.admin) endpoint = admin_reverse("cms_page_get_list") with self.login_user_context(self.admin): response = self.client.get( endpoint, data={"site": self.site.pk, "q": "", "language_code": "en"}, HTTP_X_REQUESTED_WITH="XMLHttpRequest", ) self.assertEqual(response.status_code, 200) self.assertEqual(json.loads(response.content.decode("utf-8")), []) def test_get_list_excludes_page_types(self): """Page types are blueprints for new pages, not link targets.""" create_page("Bravo page", "nav_playground.html", "en", site=self.site, created_by=self.admin) root = create_page( "Page Types", "nav_playground.html", "en", site=self.site, reverse_id=constants.PAGE_TYPES_ID ) page_type = create_page("Bravo type", "nav_playground.html", "en", site=self.site, parent=root) Page.objects.filter(pk__in=(root.pk, page_type.pk)).update(is_page_type=True) endpoint = admin_reverse("cms_page_get_list") with self.login_user_context(self.admin): response = self.client.get( endpoint, data={"site": self.site.pk, "q": "Bravo", "language_code": "en"}, HTTP_X_REQUESTED_WITH="XMLHttpRequest", ) self.assertEqual(response.status_code, 200) results = json.loads(response.content.decode("utf-8")) self.assertEqual({result["title"] for result in results}, {"Bravo page"}) def test_get_list_caps_the_number_of_results(self): for index in range(PAGE_SMART_LINK_MAX_RESULTS + 5): create_page( f"Bravo {index}", "nav_playground.html", "en", site=self.site, slug=f"bravo-{index}", created_by=self.admin, ) endpoint = admin_reverse("cms_page_get_list") with self.login_user_context(self.admin): response = self.client.get( endpoint, data={"site": self.site.pk, "q": "Bravo", "language_code": "en"}, HTTP_X_REQUESTED_WITH="XMLHttpRequest", ) self.assertEqual(response.status_code, 200) self.assertEqual( len(json.loads(response.content.decode("utf-8"))), PAGE_SMART_LINK_MAX_RESULTS ) def test_get_list_prefetches_page_urls(self): """ Regression test: the smart-link autocomplete endpoint resolves the path/title/URL of every matching page. It must prefetch ``urls`` (and ``pagecontent_set``) in bulk instead of issuing one ``PageUrl`` query per matched page. """ for index in range(4): create_page( f"Bravo {index}", "nav_playground.html", "en", site=self.site, slug=f"bravo-{index}", created_by=self.admin, ) endpoint = admin_reverse("cms_page_get_list") with self.login_user_context(self.admin): with CaptureQueriesContext(connection) as queries: response = self.client.get( endpoint, data={"site": self.site.pk, "q": "Bravo", "language_code": "en"}, HTTP_X_REQUESTED_WITH="XMLHttpRequest", ) self.assertEqual(response.status_code, 200) self.assertEqual(len(json.loads(response.content.decode("utf-8"))), 4) page_url_queries = [ query["sql"] for query in queries.captured_queries if 'FROM "cms_pageurl"' in query["sql"] ] self.assertLessEqual( len(page_url_queries), 1, "The smart-link endpoint issued one PageUrl query per matched page:\n" + "\n".join(page_url_queries), ) def test_actions_menu_superuser(self): """Test actions_menu view returns correct context for superuser""" with self.login_user_context(self.admin): url = admin_reverse("cms_page_actions_menu", args=[self.page.pk]) response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, "admin/cms/page/tree/actions_dropdown.html") # Check context contains expected keys self.assertIn("page", response.context) self.assertIn("site", response.context) self.assertIn("opts", response.context) self.assertIn("paste_enabled", response.context) self.assertIn("page_is_restricted", response.context) # Check permissions for superuser self.assertEqual(response.context["page"], self.page) self.assertEqual(response.context["site"], self.site) self.assertTrue(response.context["has_add_permission"]) self.assertTrue(response.context["has_copy_page_permission"]) self.assertTrue(response.context["has_change_permission"]) self.assertTrue(response.context["has_change_advanced_settings_permission"]) self.assertTrue(response.context["has_change_permissions_permission"]) self.assertTrue(response.context["has_move_page_permission"]) self.assertTrue(response.context["has_delete_permission"]) def test_actions_menu_paste_enabled(self): """Test paste_enabled flag is set correctly from GET parameters""" with self.login_user_context(self.admin): url = admin_reverse("cms_page_actions_menu", args=[self.page.pk]) # Test with has_copy parameter response = self.client.get(url, {"has_copy": "1"}) self.assertEqual(response.status_code, 200) self.assertTrue(response.context["paste_enabled"]) # Test with has_cut parameter response = self.client.get(url, {"has_cut": "1"}) self.assertEqual(response.status_code, 200) self.assertTrue(response.context["paste_enabled"]) # Test without parameters response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertFalse(response.context["paste_enabled"]) def test_actions_menu_nonexistent_page(self): """Test actions_menu raises 404 for non-existent page""" with self.login_user_context(self.admin): url = admin_reverse("cms_page_actions_menu", args=[999999]) response = self.client.get(url) self.assertEqual(response.status_code, 404) def test_actions_menu_extra_context(self): """Test that extra_context is merged into the response context""" # This test verifies the extra_context parameter functionality # Since we can't directly call the method with extra_context via URL, # we'll test it by directly calling the admin method from django.contrib.admin.sites import AdminSite from cms.admin.pageadmin import PageAdmin page_admin = PageAdmin(Page, AdminSite()) request = self.get_page_request(self.page, self.admin, "/") request.GET = {} extra = {"custom_key": "custom_value"} response = page_admin.actions_menu(request, self.page.pk, extra_context=extra) # The response should be an HttpResponse with rendered template self.assertEqual(response.status_code, 200) # Note: We can't easily check context on the rendered response, # but we've verified the method accepts and processes extra_context @override_settings(CMS_PERMISSION=False) def test_actions_menu_cms_permission_setting(self): """Test CMS_PERMISSION setting is passed to context""" with self.login_user_context(self.admin): url = admin_reverse("cms_page_actions_menu", args=[self.page.pk]) response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertFalse(response.context["CMS_PERMISSION"]) @override_settings(CMS_PERMISSION=True) def test_get_copy_dialog_permission_denied_source(self): """Test: Permission denied for source page (user cannot view source page)""" from django.contrib.auth import get_user_model User = get_user_model() user = User.objects.create_user(username="noperm", password="noperm", is_staff=True) page = create_page("Secret", "nav_playground.html", "en", site=self.site, created_by=self.admin) with self.login_user_context(user): url = admin_reverse("cms_page_get_copy_dialog", args=[page.pk]) response = self.client.get(url, {"source_site": self.site.pk}) self.assertEqual(response.status_code, 403) @override_settings(CMS_PERMISSION=True) def test_get_copy_dialog_permission_denied_target(self): """Test: Permission denied for target page (user cannot add subpage under target)""" from django.contrib.auth import get_user_model User = get_user_model() user = User.objects.create_user(username="noperm2", password="noperm2", is_staff=True) source_page = create_page("Source", "nav_playground.html", "en", site=self.site, created_by=self.admin) target_page = create_page("Target", "nav_playground.html", "en", site=self.site, created_by=self.admin) with self.login_user_context(user): url = admin_reverse("cms_page_get_copy_dialog", args=[source_page.pk]) response = self.client.get(url, {"source_site": self.site.pk, "target": target_page.pk}) self.assertEqual(response.status_code, 403) class PermissionsTestCase(PageTestBase): def assertContainsPermissions(self, response): try: # Django >= 5.1 self.assertContains( response, '

    Page permissions

    ', html=True, ) except AssertionError: # Django < 5.1 self.assertContains(response, "

    Page permissions

    ", html=True) def assertNotContainsPermissions(self, response): try: # Django >= 5.1 self.assertNotContains( response, '

    Page permissions

    ', html=True, ) except AssertionError: # Django < 5.1 self.assertNotContains(response, "

    Page permissions

    ", html=True) def _add_translation_to_page(self, page): translation = create_page_content( "de", "permissions-de", page.reload(), slug="permissions-de", template="nav_playground.html", ) return translation def _page_exists(self, reverse_id=None): if not reverse_id: reverse_id = "permissions" return Page.objects.filter(reverse_id=reverse_id).exists() def _page_permission_exists(self, **kwargs): return PagePermission.objects.filter(**kwargs).exists() def _get_page_permissions_data(self, **kwargs): if "id" in kwargs: initial = 1 else: initial = 0 data = { "language": "en", "limit_visibility_in_menu": "", "pagepermission_set-TOTAL_FORMS": 0, "pagepermission_set-INITIAL_FORMS": 0, "pagepermission_set-MAX_NUM_FORMS": 0, "pagepermission_set-2-TOTAL_FORMS": 1, "pagepermission_set-2-INITIAL_FORMS": initial, "pagepermission_set-2-MIN_NUM_FORMS": 0, "pagepermission_set-2-MAX_NUM_FORMS": 1000, "pagepermission_set-2-0-id": "", "pagepermission_set-2-0-page": "", "pagepermission_set-2-0-user": "", "pagepermission_set-2-0-group": "", "pagepermission_set-2-0-can_change": "on", "pagepermission_set-2-0-can_change_permissions": "on", "pagepermission_set-2-0-grant_on": 5, } non_inline = ("language", "limit_visibility_in_menu") for attr, value in kwargs.items(): if attr not in non_inline: attr = f"pagepermission_set-2-0-{attr}" data[attr] = value return data def _get_page_view_restrictions_data(self, **kwargs): if "id" in kwargs: initial = 1 else: initial = 0 data = { "language": "en", "limit_visibility_in_menu": "", "pagepermission_set-TOTAL_FORMS": 1, "pagepermission_set-INITIAL_FORMS": initial, "pagepermission_set-MIN_NUM_FORMS": 0, "pagepermission_set-MAX_NUM_FORMS": 1000, "pagepermission_set-0-id": "", "pagepermission_set-0-page": "", "pagepermission_set-0-user": "", "pagepermission_set-0-group": "", "pagepermission_set-0-can_view": "on", "pagepermission_set-0-grant_on": 5, "pagepermission_set-2-TOTAL_FORMS": 0, "pagepermission_set-2-INITIAL_FORMS": 0, "pagepermission_set-2-MIN_NUM_FORMS": 0, "pagepermission_set-2-MAX_NUM_FORMS": 1000, } non_inline = ("language", "limit_visibility_in_menu") for attr, value in kwargs.items(): if attr not in non_inline: attr = f"pagepermission_set-0-{attr}" data[attr] = value return data @override_settings(CMS_PERMISSION=True) class PermissionsOnGlobalTest(PermissionsTestCase): """ Tests all user interactions with the page admin while permissions are set to True and user has global permissions. """ def test_pages_in_admin_index(self): """ User can see the "Pages" section the admin if he has change permissions on the Page model and he has global change permissions. """ endpoint = admin_reverse("app_list", args=["cms"]) staff_user = self.get_staff_user_with_no_permissions() self.add_permission(staff_user, "change_page") self.add_global_permission(staff_user, can_change=True) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertEqual(response.status_code, 200) self.assertContains( response, '
    Page contents', html=True, ) endpoint = self.get_pages_admin_list_uri() with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertEqual(response.status_code, 200) def test_pages_not_in_admin_index(self): """ User can't see the "Pages" section the admin if he does not have change permissions on the Page model and/or does not have global change permissions. """ endpoint = admin_reverse("app_list", args=["cms"]) staff_user = self.get_staff_user_with_no_permissions() self.add_permission(staff_user, "change_page") self.add_global_permission(staff_user, can_change=False) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertEqual(response.status_code, 404) endpoint = self.get_pages_admin_list_uri() with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertEqual(response.status_code, 403) def test_user_can_edit_page_settings(self): """ User can edit page settings if he has change permissions on the Page model and and he has global change permissions. """ page = self.get_permissions_test_page() endpoint = self.get_page_change_uri("en", page) redirect_to = self.get_pages_admin_list_uri() staff_user = self.get_staff_user_with_no_permissions() data = self._get_page_data(slug="permissions-2") self.add_permission(staff_user, "change_page") self.add_global_permission(staff_user, can_change=True) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertRedirects(response, redirect_to) self.assertTrue(self._translation_exists(slug="permissions-2")) def test_user_cant_edit_page_settings(self): """ User can't edit page settings if he does not have change permissions on the Page model and/or does not have global change permissions. """ page = self.get_permissions_test_page() endpoint = self.get_page_change_uri("en", page) staff_user = self.get_staff_user_with_no_permissions() data = self._get_page_data(slug="permissions-2") self.add_permission(staff_user, "change_page") gp = self.add_global_permission(staff_user, can_change=False) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertFalse(self._translation_exists(slug="permissions-2")) self.remove_permission(staff_user, "change_page") gp.can_change = True gp.save(update_fields=["can_change"]) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertFalse(self._translation_exists(slug="permissions-2")) def test_user_can_edit_advanced_page_settings(self): """ User can edit advanced page settings if he has change permissions on the Page model, global change permissions and global change advanced settings permissions. """ page = self.get_permissions_test_page() endpoint = self.get_admin_url(Page, "advanced", page.pk) redirect_to = self.get_pages_admin_list_uri(site_id=page.site_id) staff_user = self.get_staff_user_with_no_permissions() data = self._get_page_data(reverse_id="permissions-2") self.add_permission(staff_user, "change_page") self.add_global_permission( staff_user, can_change=True, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertRedirects(response, redirect_to) self.assertTrue(self._page_exists(reverse_id="permissions-2")) def test_user_cant_edit_advanced_page_settings(self): """ User can't edit advanced page settings if he does not have change permissions on the Page model, does not have global change permissions and/or does not have global change advanced settings permissions. """ page = self.get_permissions_test_page() endpoint = self.get_admin_url(Page, "advanced", page.pk) staff_user = self.get_staff_user_with_no_permissions() data = self._get_page_data(reverse_id="permissions-2") self.add_permission(staff_user, "change_page") gp = self.add_global_permission( staff_user, can_change=True, can_change_advanced_settings=False, ) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertFalse(self._page_exists(reverse_id="permissions-2")) self.remove_permission(staff_user, "change_page") gp.can_change = True gp.save(update_fields=["can_change"]) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertFalse(self._page_exists(reverse_id="permissions-2")) def test_user_can_delete_empty_page(self): """ User can delete an empty page if he has delete & change permissions on the Page model and he has global delete & change permissions. """ page = self.get_permissions_test_page() endpoint = self.get_admin_url(Page, "delete", page.pk) redirect_to = self.get_pages_admin_list_uri() staff_user = self.get_staff_user_with_no_permissions() self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_page") self.add_global_permission(staff_user, can_change=True, can_delete=True) with self.login_user_context(staff_user): data = {"post": "yes"} response = self.client.post(endpoint, data, follow=True) # follow=True, since page changelist redirects to page content changelist self.assertRedirects(response, redirect_to) self.assertFalse(self._page_exists()) def test_user_cant_delete_empty_page(self): """ User can't delete an empty page if he does not have delete permissions on the Page model and/or does not have global delete permissions. """ page = self.get_permissions_test_page() endpoint = self.get_admin_url(Page, "delete", page.pk) staff_user = self.get_staff_user_with_no_permissions() self.add_permission(staff_user, "delete_page") gp = self.add_global_permission(staff_user, can_change=True, can_delete=False) with self.login_user_context(staff_user): data = {"post": "yes"} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertTrue(self._page_exists()) self.remove_permission(staff_user, "delete_page") gp.can_delete = True gp.save(update_fields=["can_delete"]) with self.login_user_context(staff_user): data = {"post": "yes"} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertTrue(self._page_exists()) def test_user_can_delete_non_empty_page(self): """ User can delete a page with plugins if he has delete & change permissions on the Page model, delete permissions on the plugins in the page translations and global delete & change permissions. """ page = self.get_permissions_test_page() endpoint = self.get_admin_url(Page, "delete", page.pk) redirect_to = self.get_pages_admin_list_uri() staff_user = self.get_staff_user_with_no_permissions() self._add_plugin_to_page(page) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_page") self.add_permission(staff_user, "delete_link") self.add_global_permission(staff_user, can_change=True, can_delete=True) with self.login_user_context(staff_user): data = {"post": "yes"} response = self.client.post(endpoint, data, follow=True) # follow=True, since page changelist redirects to page content changelist self.assertRedirects(response, redirect_to) self.assertFalse(self._page_exists()) def test_user_cant_delete_non_empty_page(self): """ User can't delete a page with plugins if he does not have delete permissions on the Page model, does not have delete permissions on the plugins in the page translations, and/or does not have global delete permissions. """ page = self.get_permissions_test_page() endpoint = self.get_admin_url(Page, "delete", page.pk) staff_user = self.get_staff_user_with_no_permissions() self._add_plugin_to_page(page) self.add_permission(staff_user, "change_page") self.remove_permission(staff_user, "delete_page") gp = self.add_global_permission(staff_user, can_change=True, can_delete=True) with self.login_user_context(staff_user): data = {"post": "yes"} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertTrue(self._page_exists()) self.remove_permission(staff_user, "delete_page") gp.can_delete = True gp.save(update_fields=["can_delete"]) with self.login_user_context(staff_user): data = {"post": "yes"} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertTrue(self._page_exists()) def test_user_can_delete_empty_translation(self): """ User can delete an empty translation if he has delete & change permissions on the Page model and he has global delete & change permissions. """ page = self.get_permissions_test_page() translation = self._add_translation_to_page(page) endpoint = self.get_admin_url(PageContent, "delete", translation.pk) redirect_to = self.get_pages_admin_list_uri() staff_user = self.get_staff_user_with_no_permissions() self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_page") self.add_global_permission(staff_user, can_change=True, can_delete=True) with self.login_user_context(staff_user): data = {"language": translation.language} response = self.client.post(endpoint, data) self.assertRedirects(response, redirect_to) self.assertFalse(self._translation_exists()) def test_user_cant_delete_empty_translation(self): """ User can't delete an empty translation if he does not have delete permissions on the Page model and/or does not have global delete permissions. """ page = self.get_permissions_test_page() translation = self._add_translation_to_page(page) endpoint = self.get_admin_url(PageContent, "delete", translation.pk) staff_user = self.get_staff_user_with_no_permissions() self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_page") gp = self.add_global_permission(staff_user, can_change=True, can_delete=False) with self.login_user_context(staff_user): data = {"language": translation.language} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertTrue(self._translation_exists()) self.remove_permission(staff_user, "delete_page") gp.can_delete = True gp.save(update_fields=["can_delete"]) with self.login_user_context(staff_user): data = {"language": translation.language} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertTrue(self._translation_exists()) def test_user_can_delete_non_empty_translation(self): """ User can delete a translation with plugins if he has delete & change permissions on the Page model, delete permissions on the plugins in the translation and global delete & change permissions. """ page = self.get_permissions_test_page() translation = self._add_translation_to_page(page) endpoint = self.get_admin_url(PageContent, "delete", translation.pk) redirect_to = self.get_pages_admin_list_uri() staff_user = self.get_staff_user_with_no_permissions() self._add_plugin_to_page(page, language=translation.language) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_page") self.add_permission(staff_user, "delete_link") self.add_global_permission(staff_user, can_change=True, can_delete=True) with self.login_user_context(staff_user): data = {"language": translation.language} response = self.client.post(endpoint, data) self.assertRedirects(response, redirect_to) self.assertFalse(self._translation_exists()) def test_user_cant_delete_non_empty_translation(self): """ User can't delete a translation with plugins if he does not have delete permissions on the Page model, does not have delete permissions on the plugins in the translation, and/or does not have global delete permissions. """ page = self.get_permissions_test_page() translation = self._add_translation_to_page(page) endpoint = self.get_admin_url(PageContent, "delete", translation.pk) staff_user = self.get_staff_user_with_no_permissions() self._add_plugin_to_page(page, language=translation.language) self.add_permission(staff_user, "change_page") self.remove_permission(staff_user, "delete_page") self.add_global_permission(staff_user, can_change=True, can_delete=True) with self.login_user_context(staff_user): data = {"language": translation.language} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertTrue(self._translation_exists()) def test_user_can_change_template(self): """ The template lives on the page content, so changing it needs the change permission on the page -- not the *change advanced settings* one. """ page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_no_permissions() endpoint = self.get_page_change_template_uri("en", page) self.add_permission(staff_user, "change_page") self.add_global_permission(staff_user, can_change=True, can_change_advanced_settings=False) with self.login_user_context(staff_user): page._clear_internal_cache() data = {"template": "simple.html"} response = self.client.post(endpoint, data) self.assertContains(response, "The template was successfully changed") self.assertEqual(page.get_template(), "simple.html") def test_user_cant_change_template(self): """ User can't change a page's template without the change permission on the Page model or without global change permissions. """ page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_no_permissions() endpoint = self.get_page_change_template_uri("en", page) self.add_global_permission(staff_user, can_change=True) with self.login_user_context(staff_user): data = {"template": "simple.html"} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertEqual(page.get_template(), "nav_playground.html") def test_user_can_change_template_on_change_form(self): """ The template field of the page content change form is governed by the change permission, like every other field of that form. """ page = self.get_permissions_test_page() translation = page.get_admin_content("en") staff_user = self.get_staff_user_with_no_permissions() endpoint = self.get_admin_url(PageContent, "change", translation.pk) + "?language=en" self.add_permission(staff_user, "change_page") self.add_global_permission(staff_user, can_change=True, can_change_advanced_settings=False) with self.login_user_context(staff_user): self.assertContains(self.client.get(endpoint), 'name="template"') response = self.client.post(endpoint, self._get_page_data(template="simple.html")) self.assertEqual(response.status_code, 302) translation.refresh_from_db() self.assertEqual(translation.template, "simple.html") def test_user_can_view_page_permissions_summary(self): """ All staff users can see the permissions summary for a page. """ page = self.get_permissions_test_page() endpoint = self.get_admin_url(Page, "get_permissions", page.pk) staff_user = self.get_staff_user_with_no_permissions() self.add_permission(staff_user, "change_page") self.add_global_permission(staff_user, can_change=True) with self.login_user_context(staff_user): data = {"post": "true"} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 200) self.assertContains( response, 'staff', html=True, ) def test_user_cant_view_page_permissions_summary(self): """ Non staff users can't see the permissions summary for a page. """ page = self.get_permissions_test_page() endpoint = self.get_admin_url(Page, "get_permissions", page.pk) non_staff_user = self.get_standard_user() with self.login_user_context(non_staff_user): data = {"post": "true"} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 302) self.assertRedirects(response, "/en/admin/login/?next=%s" % endpoint) def test_user_can_add_page_permissions(self): """ User can add page permissions if he has change permissions on the Page model, add permissions on the PagePermission model, global change permission and global change permissions permission. """ admin = self.get_superuser() page = self.get_permissions_test_page() endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=admin) data = self._get_page_permissions_data( page=page.pk, user=staff_user_2.pk, ) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "add_pagepermission") self.add_global_permission( staff_user, can_change=True, can_change_permissions=True, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertContainsPermissions(response) response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 302) self.assertRedirects(response, endpoint) self.assertTrue(self._page_permission_exists(user=staff_user_2)) def test_user_cant_add_page_permissions(self): """ User can't add page permissions if he does not have change permissions on the Page model, does not have add permissions on the PagePermission model, does not have global change permission, and/or does not have global change permissions permission. """ admin = self.get_superuser() page = self.get_permissions_test_page() endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=admin) data = self._get_page_permissions_data( page=page.pk, user=staff_user_2.pk, ) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "add_pagepermission") self.add_global_permission( staff_user, can_change=True, can_change_permissions=False, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertNotContainsPermissions(response) self.client.post(endpoint, data) self.assertFalse(self._page_permission_exists(user=staff_user_2)) def test_user_can_edit_page_permissions(self): """ User can edit page permissions if he has change permissions on the Page model, change permissions on the PagePermission model, global change permission and global change permissions permission. """ admin = self.get_superuser() page = self.get_permissions_test_page() endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=admin) permission = self.add_page_permission(user=staff_user_2, page=page, can_change_permissions=True) data = self._get_page_permissions_data( page=page.pk, user=staff_user_2.pk, id=permission.pk, can_change_permissions=False, ) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "change_pagepermission") self.add_global_permission( staff_user, can_change=True, can_change_permissions=True, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertContainsPermissions(response) response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 302) self.assertRedirects(response, endpoint) self.assertTrue( self._page_permission_exists( user=staff_user_2, can_change_permissions=False, ) ) def test_user_cant_edit_page_permissions(self): """ User can't edit page permissions if he does not have change permissions on the Page model, does not have change permissions on the PagePermission model, does not have global change permission, and/or does not have global change permissions permission. """ admin = self.get_superuser() page = self.get_permissions_test_page() endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=admin) permission = self.add_page_permission(user=staff_user_2, page=page, can_change_permissions=True) data = self._get_page_permissions_data( page=page.pk, user=staff_user_2.pk, id=permission.pk, can_change_permissions=False, ) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "change_pagepermission") self.add_global_permission( staff_user, can_change=True, can_change_permissions=False, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertNotContainsPermissions(response) self.client.post(endpoint, data) self.assertFalse( self._page_permission_exists( user=staff_user_2, can_change_permissions=False, ) ) def test_user_can_delete_page_permissions(self): """ User can delete page permissions if he has change permissions on the Page model, delete permissions on the PagePermission model, global change permission and global change permissions permission. """ admin = self.get_superuser() page = self.get_permissions_test_page() endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=admin) permission = self.add_page_permission(user=staff_user_2, page=page) data = self._get_page_permissions_data( page=page.pk, user=staff_user_2.pk, id=permission.pk, DELETE="on", ) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_pagepermission") self.add_global_permission( staff_user, can_change=True, can_change_permissions=True, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertContainsPermissions(response) response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 302) self.assertRedirects(response, endpoint) self.assertFalse(self._page_permission_exists(user=staff_user_2)) def test_user_cant_delete_page_permissions(self): """ User can't delete page permissions if he does not have change permissions on the Page model, does not have delete permissions on the PagePermission model, does not have global change permission, and/or does not have global change permissions permission. """ admin = self.get_superuser() page = self.get_permissions_test_page() endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=admin) permission = self.add_page_permission(user=staff_user_2, page=page) data = self._get_page_permissions_data( page=page.pk, user=staff_user_2.pk, id=permission.pk, DELETE="on", ) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_pagepermission") self.add_global_permission( staff_user, can_change=True, can_change_permissions=False, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertNotContainsPermissions(response) self.client.post(endpoint, data) self.assertTrue(self._page_permission_exists(user=staff_user_2)) def test_user_can_add_page_view_restrictions(self): """ User can add page view restrictions if he has change permissions on the Page model, add permissions on the PagePermission model, global change permission and global change permissions permission. """ admin = self.get_superuser() page = self.get_permissions_test_page() endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=admin) data = self._get_page_view_restrictions_data( page=page.pk, user=staff_user_2.pk, ) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "add_pagepermission") self.add_global_permission( staff_user, can_change=True, can_change_permissions=True, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertContainsPermissions(response) response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 302) self.assertRedirects(response, endpoint) self.assertTrue(self._page_permission_exists(user=staff_user_2, can_view=True)) def test_user_cant_add_page_view_restrictions(self): """ User can't add page view restrictions if he does not have change permissions on the Page model, does not have add permissions on the PagePermission model, does not have global change permission, and/or does not have global change permissions permission. """ admin = self.get_superuser() page = self.get_permissions_test_page() endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=admin) data = self._get_page_view_restrictions_data( page=page.pk, user=staff_user_2.pk, ) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "add_pagepermission") self.add_global_permission( staff_user, can_change=True, can_change_permissions=False, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertNotContainsPermissions(response) self.client.post(endpoint, data) self.assertFalse(self._page_permission_exists(user=staff_user_2, can_view=True)) def test_user_can_edit_page_view_restrictions(self): """ User can edit page view restrictions if he has change permissions on the Page model, change permissions on the PagePermission model, global change permission and global change permissions permission. """ admin = self.get_superuser() page = self.get_permissions_test_page() endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=admin) permission = self.add_page_permission( user=staff_user_2, page=page, can_view=True, grant_on=1, ) data = model_to_dict(permission, exclude=["group"]) data["grant_on"] = 5 data = self._get_page_view_restrictions_data(**data) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "change_pagepermission") self.add_global_permission( staff_user, can_change=True, can_change_permissions=True, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertContainsPermissions(response) response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 302) self.assertRedirects(response, endpoint) self.assertTrue( self._page_permission_exists( user=staff_user_2, grant_on=5, ) ) def test_user_cant_edit_page_view_restrictions(self): """ User can't edit page view restrictions if he does not have change permissions on the Page model, does not have change permissions on the PagePermission model, does not have global change permission, and/or does not have global change permissions permission. """ admin = self.get_superuser() page = self.get_permissions_test_page() endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=admin) permission = self.add_page_permission( user=staff_user_2, page=page, can_view=True, grant_on=1, ) data = model_to_dict(permission, exclude=["group"]) data["grant_on"] = 5 data = self._get_page_view_restrictions_data(**data) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "change_pagepermission") self.add_global_permission( staff_user, can_change=True, can_change_permissions=False, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertNotContainsPermissions(response) self.client.post(endpoint, data) self.assertFalse( self._page_permission_exists( user=staff_user_2, grant_on=5, ) ) def test_user_can_delete_page_view_restrictions(self): """ User can delete view restrictions if he has change permissions on the Page model, delete permissions on the PagePermission model, global change permission and global change permissions permission. """ admin = self.get_superuser() page = self.get_permissions_test_page() endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=admin) permission = self.add_page_permission( user=staff_user_2, page=page, can_view=True, ) data = model_to_dict(permission, exclude=["group"]) data["DELETE"] = True data = self._get_page_view_restrictions_data(**data) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_pagepermission") self.add_global_permission( staff_user, can_change=True, can_change_permissions=True, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertContainsPermissions(response) response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 302) self.assertRedirects(response, endpoint) self.assertFalse(self._page_permission_exists(user=staff_user_2, can_view=True)) def test_user_cant_delete_page_view_restrictions(self): """ User can't delete view restrictions if he does not have change permissions on the Page model, does not have delete permissions on the PagePermission model, does not have global change permission, and/or does not have global change permissions permission. """ admin = self.get_superuser() page = self.get_permissions_test_page() endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=admin) permission = self.add_page_permission( user=staff_user_2, page=page, can_view=True, ) data = model_to_dict(permission, exclude=["group"]) data["DELETE"] = True data = self._get_page_view_restrictions_data(**data) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_pagepermission") self.add_global_permission( staff_user, can_change=True, can_change_permissions=False, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertNotContainsPermissions(response) self.client.post(endpoint, data) self.assertTrue(self._page_permission_exists(user=staff_user_2, can_view=True)) def test_permissions_cache_invalidation(self): """ Permission cache clears on page save """ page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_std_permissions() endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" set_permission_cache(staff_user, page.site, "change_page", [page.pk]) with self.login_user_context(self.get_superuser()): data = self._get_page_permissions_data(page=page.pk, user=staff_user.pk) data["_continue"] = "1" self.client.post(endpoint, data) self.assertIsNone(get_permission_cache(staff_user, page.site, "change_page")) def test_permission_cache_invalidation_on_group_add(self): """ Permissions cache is invalidated if the group relationship of a user is changed """ from django.contrib.auth.models import Group page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_std_permissions() set_permission_cache(staff_user, page.site, "change_page", [page.pk]) group = Group(name="test_group") group.save() staff_user.groups.add(group) self.assertIsNone(get_permission_cache(staff_user, page.site, "change_page")) def test_permission_cache_invalidation_on_group_remove(self): """ Permissions cache is invalidated if the group relationship of a user is changed """ from django.contrib.auth.models import Group page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_std_permissions() group = Group(name="test_group") group.save() staff_user.groups.add(group) set_permission_cache(staff_user, page.site, "change_page", [page.pk]) group.user_set.remove(staff_user) self.assertIsNone(get_permission_cache(staff_user, page.site, "change_page")) def test_user_can_copy_page(self): """ Test that a page can be copied via the admin """ page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_no_permissions() self.add_permission(staff_user, "add_page") self.add_permission(staff_user, "change_page") self.add_global_permission( staff_user, can_add=True, can_change=True, ) count = Page.objects.count() with self.login_user_context(staff_user): endpoint = self.get_admin_url(Page, "get_copy_dialog", page.pk) response = self.client.get(endpoint) self.assertEqual(response.status_code, 200) with self.login_user_context(staff_user): self.copy_page(page, page, position=1) self.assertEqual(count + 1, 3) # Plugin related tests def test_user_can_add_plugin(self): """ User can add a plugin if he has change permissions on the Page model, add permissions on the plugin model and global change permissions. """ page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_no_permissions() placeholder = page.get_placeholders("en").get(slot="body") plugins = placeholder.get_plugins("en").filter(plugin_type="LinkPlugin") endpoint = self._get_add_plugin_uri(page) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "add_link") self.add_global_permission(staff_user, can_change=True) with self.login_user_context(staff_user): data = {"name": "A Link", "external_link": "https://www.django-cms.org"} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 200) self.assertEqual(plugins.count(), 1) def test_user_cant_add_plugin(self): """ User can't add a plugin if he does not have change permissions on the Page model, does not have add permissions on the plugin model and/or does not have global change permissions. """ page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_no_permissions() placeholder = page.get_placeholders("en").get(slot="body") plugins = placeholder.get_plugins("en").filter(plugin_type="LinkPlugin") endpoint = self._get_add_plugin_uri(page) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "add_link") self.add_global_permission(staff_user, can_change=False) with self.login_user_context(staff_user): data = {"name": "A Link", "external_link": "https://www.django-cms.org"} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertEqual(plugins.count(), 0) def test_user_can_edit_plugin(self): """ User can edit a plugin if he has change permissions on the Page model, change permissions on the plugin model and global change permissions. """ page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_no_permissions() plugin = self._add_plugin_to_page(page) endpoint = self.get_change_plugin_uri(plugin) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "change_link") self.add_global_permission(staff_user, can_change=True) with self.login_user_context(staff_user): data = model_to_dict(plugin, fields=["name", "external_link"]) data["name"] = "A link 2" response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 200) plugin.refresh_from_db() self.assertEqual(plugin.name, data["name"]) def test_user_cant_edit_plugin(self): """ User can't edit a plugin if he does not have change permissions on the Page model, does not have change permissions on the plugin model and/or does not have global change permissions. """ page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_no_permissions() plugin = self._add_plugin_to_page(page) endpoint = self.get_change_plugin_uri(plugin) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "change_link") self.add_global_permission(staff_user, can_change=False) with self.login_user_context(staff_user): data = model_to_dict(plugin, fields=["name", "external_link"]) data["name"] = "A link 2" response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) plugin.refresh_from_db() self.assertNotEqual(plugin.name, data["name"]) def test_user_can_delete_plugin(self): """ User can delete a plugin if he has change permissions on the Page model, delete permissions on the plugin model and global change permissions. """ page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_no_permissions() plugin = self._add_plugin_to_page(page) endpoint = self.get_delete_plugin_uri(plugin) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_link") self.add_global_permission(staff_user, can_change=True) with self.login_user_context(staff_user): data = {"post": True} response = self.client.post(endpoint, data) self.assertContains(response, '
    ') self.assertFalse(CMSPlugin.objects.filter(pk=plugin.pk).exists()) def test_user_cant_delete_plugin(self): """ User can't delete a plugin if he does not have change permissions on the Page model, does not have delete permissions on the plugin model and/or does not have global change permissions. """ page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_no_permissions() plugin = self._add_plugin_to_page(page) endpoint = self.get_delete_plugin_uri(plugin) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_link") self.add_global_permission(staff_user, can_change=False) with self.login_user_context(staff_user): data = {"post": True} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertTrue(CMSPlugin.objects.filter(pk=plugin.pk).exists()) def test_user_can_move_plugin(self): """ User can move a plugin if he has change permissions on the Page model, change permissions on the plugin model and global change permissions. """ page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_no_permissions() plugin = self._add_plugin_to_page(page) endpoint = self.get_move_plugin_uri(plugin) source_placeholder = plugin.placeholder target_placeholder = page.get_placeholders("en").get(slot="right-column") data = self._get_move_data(plugin, position=1, placeholder=target_placeholder) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "change_link") self.add_global_permission(staff_user, can_change=True) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 200) self.assertTrue(target_placeholder.get_plugins("en").filter(pk=plugin.pk)) self.assertFalse(source_placeholder.get_plugins("en").filter(pk=plugin.pk)) def test_user_cant_move_plugin(self): """ User can't move a plugin if he does not have change permissions on the Page model, does not have change permissions on the plugin model and/or does not have global change permissions. """ page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_no_permissions() plugin = self._add_plugin_to_page(page) endpoint = self.get_move_plugin_uri(plugin) source_placeholder = plugin.placeholder target_placeholder = page.get_placeholders("en").get(slot="right-column") data = self._get_move_data(plugin, position=1, placeholder=target_placeholder) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "change_link") self.add_global_permission(staff_user, can_change=False) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertFalse(target_placeholder.get_plugins("en").filter(pk=plugin.pk)) self.assertTrue(source_placeholder.get_plugins("en").filter(pk=plugin.pk)) def test_user_can_copy_plugin(self): """ User can copy a plugin if he has change permissions on the Page model, add permissions on the plugin model and global change permissions. """ page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_no_permissions() plugin = self._add_plugin_to_page(page) translation = self._add_translation_to_page(page) endpoint = self.get_copy_plugin_uri(plugin) source_placeholder = plugin.placeholder target_placeholder = page.get_placeholders("en").get(slot="right-column") data = { "source_plugin_id": plugin.pk, "source_placeholder_id": source_placeholder.pk, "source_language": plugin.language, "target_language": translation.language, "target_placeholder_id": target_placeholder.pk, } self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "add_link") self.add_global_permission(staff_user, can_change=True) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 200) self.assertTrue(source_placeholder.get_plugins("en").filter(pk=plugin.pk).exists()) self.assertTrue( target_placeholder.get_plugins(translation.language).filter(plugin_type=plugin.plugin_type).exists() ) def test_user_cant_copy_plugin(self): """ User can't copy a plugin if he does not have change permissions on the Page model, does not have add permissions on the plugin model, and/or does not have global change permissions. """ page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_no_permissions() plugin = self._add_plugin_to_page(page) translation = self._add_translation_to_page(page) endpoint = self.get_copy_plugin_uri(plugin) source_placeholder = plugin.placeholder target_placeholder = page.get_placeholders("en").get(slot="right-column") data = { "source_plugin_id": plugin.pk, "source_placeholder_id": source_placeholder.pk, "source_language": plugin.language, "target_language": translation.language, "target_placeholder_id": target_placeholder.pk, } self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "add_link") self.add_global_permission(staff_user, can_change=False) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertTrue(source_placeholder.get_plugins("en").filter(pk=plugin.pk).exists()) self.assertFalse( target_placeholder.get_plugins(translation.language).filter(plugin_type=plugin.plugin_type).exists() ) def test_user_can_copy_plugins_to_language(self): """ User can copy all plugins to another language if he has change permissions on the Page model, add permissions on the plugins being copied and global change permissions. """ page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_no_permissions() source_translation = self.get_pagecontent_obj(page, "en") target_translation = self._add_translation_to_page(page) endpoint = self.get_admin_url(PageContent, "copy_language", source_translation.pk) plugins = [ self._add_plugin_to_page(page), self._add_plugin_to_page(page), self._add_plugin_to_page(page), self._add_plugin_to_page(page), ] source_placeholder = plugins[0].placeholder target_placeholder = target_translation.get_placeholders().get(slot=source_placeholder.slot) data = { "source_language": "en", "target_language": target_translation.language, } self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "add_link") self.add_global_permission(staff_user, can_change=True) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 200) new_plugins = target_placeholder.get_plugins() self.assertEqual(new_plugins.count(), len(plugins)) def test_copy_language_downcasts_plugins_across_placeholders_in_one_query(self): page = self.get_permissions_test_page() source_translation = self.get_pagecontent_obj(page, "en") target_translation = self._add_translation_to_page(page) source_placeholders = page.get_placeholders("en") add_plugin(source_placeholders.get(slot="body"), "TextPlugin", "en", body="Body") add_plugin(source_placeholders.get(slot="right-column"), "TextPlugin", "en", body="Right column") endpoint = self.get_admin_url(PageContent, "copy_language", source_translation.pk) with self.login_user_context(self.get_superuser()): with CaptureQueriesContext(connection) as queries: response = self.client.post(endpoint, {"target_language": target_translation.language}) plugin_batch_queries = [] concrete_plugin_queries = [] for query in queries: sql = query["sql"].replace('"', "").replace("`", "") if "FROM cms_cmsplugin" in sql and "placeholder_id IN" in sql: plugin_batch_queries.append(query) if f"FROM {Text._meta.db_table}" in sql: concrete_plugin_queries.append(query) self.assertEqual(response.status_code, 200) self.assertEqual(len(plugin_batch_queries), 1) self.assertEqual(len(concrete_plugin_queries), 1) self.assertCountEqual( Text.objects.filter(language="de").values_list("body", flat=True), ["Body", "Right column"] ) def test_user_cant_copy_plugins_to_language(self): """ User can't copy all plugins to another language if he does have change permissions on the Page model, does not have add permissions on the plugins being copied and/or does not have global change permissions. """ page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_no_permissions() translation = self._add_translation_to_page(page) endpoint = self.get_admin_url(PageContent, "copy_language", translation.pk) plugins = [ self._add_plugin_to_page(page), self._add_plugin_to_page(page), self._add_plugin_to_page(page), self._add_plugin_to_page(page), ] placeholder = plugins[0].placeholder data = { "source_language": "en", "target_language": translation.language, } self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "add_link") self.add_global_permission(staff_user, can_change=False) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) new_plugins = placeholder.get_plugins(translation.language) self.assertEqual(new_plugins.count(), 0) def test_copy_plugins_to_language_skips_missing_placeholders(self): """ When copying plugins to another language, placeholders that don't exist in the target template should be skipped without raising DoesNotExist exception. Regression test for copying content when template placeholders have changed between languages. This test ensures the fix at pageadmin.py:1213-1217 properly handles the DoesNotExist exception. """ admin = self.get_superuser() # Create page with nav_playground template (has 'body' and 'right-column' placeholders) page = create_page( "test-page", "nav_playground.html", "en", created_by=admin, ) # Add plugins to both placeholders in English body_placeholder = page.get_placeholders("en").get(slot="body") right_column_placeholder = page.get_placeholders("en").get(slot="right-column") add_plugin(body_placeholder, "LinkPlugin", "en", name="Body Link", external_link="https://example.com") add_plugin( right_column_placeholder, "LinkPlugin", "en", name="Right Column Link", external_link="https://example.com" ) # Create German translation with col_two template (has 'col_sidebar' and 'col_left' placeholders) # This simulates a template change where old placeholders no longer exist de_content = create_page_content( "de", "test-page-de", page, slug="test-page-de", template="col_two.html", ) # Try to copy from English to German - should not raise DoesNotExist endpoint = self.get_admin_url(PageContent, "copy_language", de_content.pk) data = { "source_language": "en", "target_language": "de", } with self.login_user_context(admin): response = self.client.post(endpoint, data) # Should succeed without raising DoesNotExist exception # This is the key behavior being tested - the operation completes # successfully even when source placeholders don't exist in target self.assertEqual(response.status_code, 200) # Verify original English plugins are still intact self.assertEqual(body_placeholder.get_plugins("en").count(), 1) self.assertEqual(right_column_placeholder.get_plugins("en").count(), 1) # Placeholder related tests def test_user_can_clear_empty_placeholder(self): """ User can clear an empty placeholder if he has change permissions on the Page model and global change permissions. """ page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_no_permissions() placeholder = page.get_placeholders("en").get(slot="body") endpoint = self.get_clear_placeholder_url(placeholder) self.add_permission(staff_user, "change_page") self.add_global_permission(staff_user, can_change=True) with self.login_user_context(staff_user): response = self.client.post(endpoint, {"test": 0}) self.assertEqual(response.status_code, 200) def test_user_cant_clear_empty_placeholder(self): """ User can't clear an empty placeholder if he does not have change permissions on the Page model and/or does not have global change permissions. """ page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_no_permissions() placeholder = page.get_placeholders("en").get(slot="body") endpoint = self.get_clear_placeholder_url(placeholder) self.add_permission(staff_user, "change_page") self.add_global_permission(staff_user, can_change=False) with self.login_user_context(staff_user): response = self.client.post(endpoint, {"test": 0}) self.assertEqual(response.status_code, 403) def test_user_can_clear_non_empty_placeholder(self): """ User can clear a placeholder with plugins if he has change permissions on the Page model, delete permissions on the plugin models in the placeholder and global change permissions. """ page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_no_permissions() plugins = [ self._add_plugin_to_page(page, "TextPlugin"), self._add_plugin_to_page(page, "LinkPlugin"), ] placeholder = plugins[0].placeholder endpoint = self.get_clear_placeholder_url(placeholder) self.add_permission(staff_user, "delete_text") self.add_permission(staff_user, "delete_link") self.add_permission(staff_user, "change_page") self.add_global_permission(staff_user, can_change=True) with self.login_user_context(staff_user): response = self.client.post(endpoint, {"test": 0}) self.assertEqual(response.status_code, 200) self.assertEqual(placeholder.get_plugins("en").count(), 0) def test_user_cant_clear_non_empty_placeholder(self): """ User can't clear a placeholder with plugins if he does not have change permissions on the Page model, does not have delete permissions on the plugin models in the placeholder and/or does not have global change permissions. """ page = self.get_permissions_test_page() staff_user = self.get_staff_user_with_no_permissions() plugins = [ self._add_plugin_to_page(page, "TextPlugin"), self._add_plugin_to_page(page, "LinkPlugin"), ] placeholder = plugins[0].placeholder endpoint = self.get_clear_placeholder_url(placeholder) self.add_permission(staff_user, "delete_text") self.add_permission(staff_user, "delete_link") self.add_permission(staff_user, "change_page") self.add_global_permission(staff_user, can_change=False) with self.login_user_context(staff_user): response = self.client.post(endpoint, {"test": 0}) self.assertEqual(response.status_code, 403) self.assertEqual(placeholder.get_plugins("en").count(), 2) @override_settings(CMS_PERMISSION=True) class PermissionsOnPageTest(PermissionsTestCase): """ Tests all user interactions with the page admin while permissions are set to True and user has page permissions. """ def setUp(self): self._permissions_page = self.get_permissions_test_page() def test_pages_in_admin_index(self): """ User can see the "Pages" section the admin if he has change permissions on the Page model and he has global change permissions. """ page = self._permissions_page endpoint = admin_reverse("app_list", args=["cms"]) staff_user = self.get_staff_user_with_no_permissions() self.add_permission(staff_user, "change_page") self.add_page_permission( staff_user, page, can_change=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertEqual(response.status_code, 200) def test_set_home_requires_permission_on_old_home(self): """ Setting a new home page demotes the current one and rewrites its urls, so change permission on the target page alone is not enough. """ old_home = create_page("old-home", "nav_playground.html", "en") old_home.set_as_homepage() new_home = create_page("new-home", "nav_playground.html", "en") endpoint = self.get_admin_url(Page, "set_home", new_home.pk) staff_user = self.get_staff_user_with_no_permissions() self.add_permission(staff_user, "change_page") self.add_page_permission(staff_user, new_home, can_change=True) with self.login_user_context(staff_user): response = self.client.post(endpoint) self.assertEqual(response.status_code, 403) self.assertTrue(old_home.reload().is_home) self.assertFalse(new_home.reload().is_home) def test_set_home_with_permission_on_both_pages(self): """Change permission on both the old and the new home page is enough.""" old_home = create_page("old-home", "nav_playground.html", "en") old_home.set_as_homepage() new_home = create_page("new-home", "nav_playground.html", "en") endpoint = self.get_admin_url(Page, "set_home", new_home.pk) staff_user = self.get_staff_user_with_no_permissions() self.add_permission(staff_user, "change_page") self.add_page_permission(staff_user, new_home, can_change=True) self.add_page_permission(staff_user, old_home, can_change=True) with self.login_user_context(staff_user): response = self.client.post(endpoint) self.assertEqual(response.status_code, 200) self.assertFalse(old_home.reload().is_home) self.assertTrue(new_home.reload().is_home) def test_get_permissions_page_permissions_can_change_without_global_permission(self): """Ensure PageAdmin.get_permissions computes can_change from allowed page paths. This specifically covers the branch where the user cannot change global permissions and can_change is computed via a prefix match against the user's allowed page paths. """ page = self._permissions_page endpoint = self.get_admin_url(Page, "get_permissions", page.pk) staff_user = self.get_staff_user_with_no_permissions() other_staff = self._create_user("staff2", is_staff=True, is_superuser=False) # Staff user must be able to access the view. self.add_permission(staff_user, "change_page") self.add_page_permission(staff_user, page, can_change=True) # Ensure at least one GlobalPagePermission exists for the site so the # template has something to render in the global section. self.add_global_permission(other_staff, can_change=True) # Add a page-level permission row that should be changeable by staff_user # due to their own allowed page paths. other_permission = self.add_page_permission(other_staff, page, can_change=True) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertEqual(response.status_code, 200) rows = response.context["rows"] permission_row = next( row for row in rows if (not row.is_global and row.permission.pk == other_permission.pk) ) self.assertTrue(permission_row.can_change) def test_get_permissions_page_permissions_cant_change_with_only_global_page_permission(self): """Cover the False-case of can_change computation in PageAdmin.get_permissions. The user is allowed to access the view via a *global* page permission, but has no page-level permissions. Since get_permissions() computes allowed pages with check_global=False, the allowed list is empty and thus can_change evaluates to False. """ page = self._permissions_page endpoint = self.get_admin_url(Page, "get_permissions", page.pk) staff_user = self.get_staff_user_with_no_permissions() other_staff = self._create_user("staff3", is_staff=True, is_superuser=False) # Access the view (has_change_permission) via global page permission. self.add_permission(staff_user, "change_page") self.add_global_permission(staff_user, can_change=True) # Create a page permission row for the page which staff_user should NOT # be able to change based on page-level allowed paths. other_permission = self.add_page_permission(other_staff, page, can_change=True) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertEqual(response.status_code, 200) rows = response.context["rows"] permission_row = next( row for row in rows if (not row.is_global and row.permission.pk == other_permission.pk) ) self.assertFalse(permission_row.can_change) def test_pages_not_in_admin_index(self): """ User can't see the "Pages" section the admin if he does not have change permissions on the Page model and/or does not have global change permissions. """ page = self._permissions_page endpoint = admin_reverse("app_list", args=["cms"]) staff_user = self.get_staff_user_with_no_permissions() self.add_permission(staff_user, "change_page") self.add_page_permission( staff_user, page, can_change=False, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertEqual(response.status_code, 404) endpoint = self.get_pages_admin_list_uri() with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertEqual(response.status_code, 403) def test_user_can_edit_page_settings(self): """ User can edit page settings if he has change permissions on the Page model and and he has global change permissions. """ page = self._permissions_page endpoint = self.get_page_change_uri("en", page) redirect_to = self.get_pages_admin_list_uri() staff_user = self.get_staff_user_with_no_permissions() data = self._get_page_data(slug="permissions-2") self.add_permission(staff_user, "change_page") self.add_page_permission( staff_user, page, can_change=True, ) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertRedirects(response, redirect_to) self.assertTrue(self._translation_exists(slug="permissions-2")) def test_user_cant_edit_page_settings(self): """ User can't edit page settings if he does not have change permissions on the Page model and/or does not have global change permissions. """ page = self._permissions_page endpoint = self.get_page_change_uri("en", page) staff_user = self.get_staff_user_with_no_permissions() data = self._get_page_data(slug="permissions-2") self.add_permission(staff_user, "change_page") page_perm = self.add_page_permission( staff_user, page, can_change=False, ) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertFalse(self._translation_exists(slug="permissions-2")) self.remove_permission(staff_user, "change_page") page_perm.can_change = True page_perm.save(update_fields=["can_change"]) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertFalse(self._translation_exists(slug="permissions-2")) def test_user_can_edit_advanced_page_settings(self): """ User can edit advanced page settings if he has change permissions on the Page model, global change permissions and global change advanced settings permissions. """ page = self._permissions_page endpoint = self.get_admin_url(Page, "advanced", page.pk) redirect_to = self.get_pages_admin_list_uri(site_id=page.site_id) staff_user = self.get_staff_user_with_no_permissions() data = self._get_page_data(reverse_id="permissions-2") self.add_permission(staff_user, "change_page") self.add_page_permission( staff_user, page, can_change=True, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertRedirects(response, redirect_to) self.assertTrue(self._page_exists(reverse_id="permissions-2")) def test_user_cant_edit_advanced_page_settings(self): """ User can't edit advanced page settings if he does not have change permissions on the Page model, does not have global change permissions and/or does not have global change advanced settings permissions. """ page = self._permissions_page endpoint = self.get_admin_url(Page, "advanced", page.pk) staff_user = self.get_staff_user_with_no_permissions() data = self._get_page_data(reverse_id="permissions-2") self.add_permission(staff_user, "change_page") page_perm = self.add_page_permission( staff_user, page, can_change=True, can_change_advanced_settings=False, ) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertFalse(self._page_exists(reverse_id="permissions-2")) self.remove_permission(staff_user, "change_page") page_perm.can_change = True page_perm.save(update_fields=["can_change"]) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertFalse(self._page_exists(reverse_id="permissions-2")) def test_user_can_delete_empty_page(self): """ User can delete an empty page if he has delete & change permissions on the Page model and he has page delete & change permissions. """ page = self._permissions_page endpoint = self.get_admin_url(Page, "delete", page.pk) redirect_to = admin_reverse("index") staff_user = self.get_staff_user_with_no_permissions() self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_page") self.add_page_permission( staff_user, page, can_change=True, can_delete=True, ) with self.login_user_context(staff_user): data = {"post": "yes"} response = self.client.post(endpoint, data, follow=True) self.assertRedirects(response, redirect_to) self.assertFalse(self._page_exists()) def test_user_cant_delete_empty_page(self): """ User can't delete an empty page if he does not have delete permissions on the Page model and/or does not have global delete permissions. """ page = self._permissions_page endpoint = self.get_admin_url(Page, "delete", page.pk) staff_user = self.get_staff_user_with_no_permissions() self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_page") page_perm = self.add_page_permission( staff_user, page, can_change=False, can_delete=False, ) with self.login_user_context(staff_user): data = {"post": "yes"} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertTrue(self._page_exists()) self.remove_permission(staff_user, "delete_page") page_perm.can_delete = True page_perm.save(update_fields=["can_delete"]) with self.login_user_context(staff_user): data = {"post": "yes"} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertTrue(self._page_exists()) def test_user_can_delete_non_empty_page(self): """ User can delete a page with plugins if he has delete permissions on the Page model, delete permissions on the plugins in the page translations and page delete & change permissions. """ page = self._permissions_page endpoint = self.get_admin_url(Page, "delete", page.pk) redirect_to = admin_reverse("index") staff_user = self.get_staff_user_with_no_permissions() self._add_plugin_to_page(page) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_page") self.add_permission(staff_user, "delete_link") self.add_page_permission( staff_user, page, can_change=True, can_delete=True, ) with self.login_user_context(staff_user): data = {"post": "yes"} response = self.client.post(endpoint, data, follow=True) self.assertRedirects(response, redirect_to) self.assertFalse(self._page_exists()) def test_user_cant_delete_non_empty_page(self): """ User can't delete a page with plugins if he does not have delete permissions on the Page model, does not have delete permissions on the plugins in the page translations, and/or does not have global delete permissions. """ page = self._permissions_page endpoint = self.get_admin_url(Page, "delete", page.pk) staff_user = self.get_staff_user_with_no_permissions() self._add_plugin_to_page(page) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_page") page_perm = self.add_page_permission( staff_user, page, can_change=False, can_delete=False, ) with self.login_user_context(staff_user): data = {"post": "yes"} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertTrue(self._page_exists()) self.remove_permission(staff_user, "delete_page") page_perm.can_delete = True page_perm.save(update_fields=["can_delete"]) with self.login_user_context(staff_user): data = {"post": "yes"} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertTrue(self._page_exists()) def test_user_can_delete_empty_translation(self): """ User can delete an empty translation if he has delete permissions on the Page model and he has page delete & change permissions. """ page = self._permissions_page translation = self._add_translation_to_page(page) endpoint = self.get_admin_url(PageContent, "delete", translation.pk) redirect_to = self.get_pages_admin_list_uri() staff_user = self.get_staff_user_with_no_permissions() self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_page") self.add_page_permission( staff_user, page, can_change=True, can_delete=True, ) with self.login_user_context(staff_user): data = {"language": translation.language} response = self.client.post(endpoint, data) self.assertRedirects(response, redirect_to) self.assertFalse(self._translation_exists()) def test_user_cant_delete_empty_translation(self): """ User can't delete an empty translation if he does not have delete permissions on the Page model and/or does not have global delete permissions. """ page = self._permissions_page translation = self._add_translation_to_page(page) endpoint = self.get_admin_url(PageContent, "delete", translation.pk) staff_user = self.get_staff_user_with_no_permissions() self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_page") page_perm = self.add_page_permission( staff_user, page, can_change=False, can_delete=False, ) with self.login_user_context(staff_user): data = {"language": translation.language} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertTrue(self._translation_exists()) self.remove_permission(staff_user, "delete_page") page_perm.can_delete = True page_perm.save(update_fields=["can_delete"]) with self.login_user_context(staff_user): data = {"language": translation.language} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertTrue(self._translation_exists()) def test_user_can_delete_non_empty_translation(self): """ User can delete a translation with plugins if he has delete permissions on the Page model, delete permissions on the plugins in the translation and page delete & change permissions. """ page = self._permissions_page translation = self._add_translation_to_page(page) endpoint = self.get_admin_url(PageContent, "delete", translation.pk) redirect_to = self.get_pages_admin_list_uri() staff_user = self.get_staff_user_with_no_permissions() self._add_plugin_to_page(page, language=translation.language) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_page") self.add_permission(staff_user, "delete_link") self.add_page_permission( staff_user, page, can_change=True, can_delete=True, ) with self.login_user_context(staff_user): data = {"language": translation.language} response = self.client.post(endpoint, data) self.assertRedirects(response, redirect_to) self.assertFalse(self._translation_exists()) def test_user_cant_delete_non_empty_translation(self): """ User can't delete a translation with plugins if he does not have delete permissions on the Page model, does not have delete permissions on the plugins in the translation, and/or does not have global delete permissions. """ page = self._permissions_page translation = self._add_translation_to_page(page) endpoint = self.get_admin_url(PageContent, "delete", translation.pk) staff_user = self.get_staff_user_with_no_permissions() self._add_plugin_to_page(page, language=translation.language) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_page") self.add_page_permission( staff_user, page, can_change=False, can_delete=True, ) with self.login_user_context(staff_user): data = {"language": translation.language} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertTrue(self._translation_exists()) def test_user_can_add_page_permissions(self): """ User can add page permissions if he has change permissions on the Page model, add permissions on the PagePermission model, global change permission and global change permissions permission. """ page = self._permissions_page endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=staff_user) data = self._get_page_permissions_data( page=page.pk, user=staff_user_2.pk, ) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "add_pagepermission") self.add_page_permission( staff_user, page, can_change=True, can_change_permissions=True, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertContainsPermissions(response) response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 302) self.assertRedirects(response, endpoint) self.assertTrue(self._page_permission_exists(user=staff_user_2)) def test_user_cant_add_page_permissions(self): """ User can't add page permissions if he does not have change permissions on the Page model, does not have add permissions on the PagePermission model, does not have global change permission, and/or does not have global change permissions permission. """ page = self._permissions_page endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=staff_user) data = self._get_page_permissions_data( page=page.pk, user=staff_user_2.pk, ) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "add_pagepermission") self.add_page_permission( staff_user, page, can_change=True, can_change_permissions=False, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertNotContainsPermissions(response) self.client.post(endpoint, data) self.assertFalse(self._page_permission_exists(user=staff_user_2)) def test_user_can_edit_page_permissions(self): """ User can edit page permissions if he has change permissions on the Page model, change permissions on the PagePermission model, global change permission and global change permissions permission. """ page = self._permissions_page endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=staff_user) permission = self.add_page_permission(user=staff_user_2, page=page, can_change_permissions=True) data = self._get_page_permissions_data( page=page.pk, user=staff_user_2.pk, id=permission.pk, can_change_permissions=False, ) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "change_pagepermission") self.add_page_permission( staff_user, page, can_change=True, can_change_permissions=True, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertContainsPermissions(response) response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 302) self.assertRedirects(response, endpoint) self.assertTrue( self._page_permission_exists( user=staff_user_2, can_change_permissions=False, ) ) def test_user_cant_edit_page_permissions(self): """ User can't edit page permissions if he does not have change permissions on the Page model, does not have change permissions on the PagePermission model, does not have global change permission, and/or does not have global change permissions permission. """ page = self._permissions_page endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=staff_user) permission = self.add_page_permission(user=staff_user_2, page=page, can_change_permissions=True) data = self._get_page_permissions_data( page=page.pk, user=staff_user_2.pk, id=permission.pk, can_change_permissions=False, ) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "change_pagepermission") self.add_page_permission( staff_user, page, can_change=True, can_change_permissions=False, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertNotContainsPermissions(response) self.client.post(endpoint, data) self.assertFalse( self._page_permission_exists( user=staff_user_2, can_change_permissions=False, ) ) def test_user_can_delete_page_permissions(self): """ User can delete page permissions if he has change permissions on the Page model, delete permissions on the PagePermission model, global change permission and global change permissions permission. """ page = self._permissions_page endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=staff_user) permission = self.add_page_permission(user=staff_user_2, page=page) data = self._get_page_permissions_data( page=page.pk, user=staff_user_2.pk, id=permission.pk, DELETE="on", ) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_pagepermission") self.add_page_permission( staff_user, page, can_change=True, can_change_permissions=True, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertContainsPermissions(response) response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 302) self.assertRedirects(response, endpoint) self.assertFalse(self._page_permission_exists(user=staff_user_2)) def test_user_cant_delete_page_permissions(self): """ User can't delete page permissions if he does not have change permissions on the Page model, does not have delete permissions on the PagePermission model, does not have global change permission, and/or does not have global change permissions permission. """ page = self._permissions_page endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=staff_user) permission = self.add_page_permission(user=staff_user_2, page=page) data = self._get_page_permissions_data( page=page.pk, user=staff_user_2.pk, id=permission.pk, DELETE="on", ) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_pagepermission") self.add_page_permission( staff_user, page, can_change=True, can_change_permissions=False, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertNotContainsPermissions(response) self.client.post(endpoint, data) self.assertTrue(self._page_permission_exists(user=staff_user_2)) def test_user_can_add_page_view_restrictions(self): """ User can add page view restrictions if he has change permissions on the Page model, add permissions on the PagePermission model, global change permission and global change permissions permission. """ page = self._permissions_page endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=staff_user) data = self._get_page_view_restrictions_data( page=page.pk, user=staff_user_2.pk, ) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "add_pagepermission") self.add_page_permission( staff_user, page, can_change=True, can_change_permissions=True, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertContainsPermissions(response) response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 302) self.assertRedirects(response, endpoint) self.assertTrue(self._page_permission_exists(user=staff_user_2, can_view=True)) def test_user_cant_add_page_view_restrictions(self): """ User can't add page view restrictions if he does not have change permissions on the Page model, does not have add permissions on the PagePermission model, does not have global change permission, and/or does not have global change permissions permission. """ page = self._permissions_page endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=staff_user) data = self._get_page_view_restrictions_data( page=page.pk, user=staff_user_2.pk, ) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "add_pagepermission") self.add_page_permission( staff_user, page, can_change=True, can_change_permissions=False, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertNotContainsPermissions(response) self.client.post(endpoint, data) self.assertFalse(self._page_permission_exists(user=staff_user_2, can_view=True)) def test_user_can_edit_page_view_restrictions(self): """ User can edit page view restrictions if he has change permissions on the Page model, change permissions on the PagePermission model, global change permission and global change permissions permission. """ page = self._permissions_page endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=staff_user) permission = self.add_page_permission( user=staff_user_2, page=page, can_view=True, grant_on=1, ) data = model_to_dict(permission, exclude=["group"]) data["grant_on"] = 5 data = self._get_page_view_restrictions_data(**data) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "change_pagepermission") self.add_page_permission( staff_user, page, can_change=True, can_change_permissions=True, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertContainsPermissions(response) response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 302) self.assertRedirects(response, endpoint) self.assertTrue( self._page_permission_exists( user=staff_user_2, grant_on=5, ) ) def test_user_cant_edit_page_view_restrictions(self): """ User can't edit page view restrictions if he does not have change permissions on the Page model, does not have change permissions on the PagePermission model, does not have global change permission, and/or does not have global change permissions permission. """ page = self._permissions_page endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=staff_user) permission = self.add_page_permission( user=staff_user_2, page=page, can_view=True, grant_on=1, ) data = model_to_dict(permission, exclude=["group"]) data["grant_on"] = 5 data = self._get_page_view_restrictions_data(**data) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "change_pagepermission") self.add_page_permission( staff_user, page, can_change=True, can_change_permissions=False, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertNotContainsPermissions(response) self.client.post(endpoint, data) self.assertFalse( self._page_permission_exists( user=staff_user_2, grant_on=5, ) ) def test_user_can_delete_page_view_restrictions(self): """ User can delete view restrictions if he has change permissions on the Page model, delete permissions on the PagePermission model, global change permission and global change permissions permission. """ page = self._permissions_page endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=staff_user) permission = self.add_page_permission( user=staff_user_2, page=page, can_view=True, ) data = model_to_dict(permission, exclude=["group"]) data["DELETE"] = True data = self._get_page_view_restrictions_data(**data) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_pagepermission") self.add_page_permission( staff_user, page, can_change=True, can_change_permissions=True, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertContainsPermissions(response) response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 302) self.assertRedirects(response, endpoint) self.assertFalse(self._page_permission_exists(user=staff_user_2, can_view=True)) def test_user_cant_delete_page_view_restrictions(self): """ User can't delete view restrictions if he does not have change permissions on the Page model, does not have delete permissions on the PagePermission model, does not have global change permission, and/or does not have global change permissions permission. """ page = self._permissions_page endpoint = self.get_admin_url(Page, "advanced", page.pk) + "?language=en" staff_user = self.get_staff_user_with_no_permissions() staff_user_2 = self.get_staff_page_user(created_by=staff_user) permission = self.add_page_permission( user=staff_user_2, page=page, can_view=True, ) data = model_to_dict(permission, exclude=["group"]) data["DELETE"] = True data = self._get_page_view_restrictions_data(**data) data["_continue"] = "1" self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_pagepermission") self.add_page_permission( staff_user, page, can_change=True, can_change_permissions=False, can_change_advanced_settings=True, ) with self.login_user_context(staff_user): response = self.client.get(endpoint) self.assertNotContainsPermissions(response) self.client.post(endpoint, data) self.assertTrue(self._page_permission_exists(user=staff_user_2, can_view=True)) # Plugin related tests def test_user_can_add_plugin(self): """ User can add a plugin if he has change permissions on the Page model, add permissions on the plugin model and global change permissions. """ page = self._permissions_page staff_user = self.get_staff_user_with_no_permissions() placeholder = page.get_placeholders("en").get(slot="body") plugins = placeholder.get_plugins("en").filter(plugin_type="LinkPlugin") endpoint = self._get_add_plugin_uri(page) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "add_link") self.add_page_permission( staff_user, page, can_change=True, ) with self.login_user_context(staff_user): data = {"name": "A Link", "external_link": "https://www.django-cms.org"} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 200) self.assertEqual(plugins.count(), 1) def test_user_cant_add_plugin(self): """ User can't add a plugin if he does not have change permissions on the Page model, does not have add permissions on the plugin model and/or does not have global change permissions. """ page = self._permissions_page staff_user = self.get_staff_user_with_no_permissions() placeholder = page.get_placeholders("en").get(slot="body") plugins = placeholder.get_plugins("en").filter(plugin_type="LinkPlugin") endpoint = self._get_add_plugin_uri(page) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "add_link") self.add_page_permission( staff_user, page, can_change=False, ) with self.login_user_context(staff_user): data = {"name": "A Link", "external_link": "https://www.django-cms.org"} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertEqual(plugins.count(), 0) def test_user_can_edit_plugin(self): """ User can edit a plugin if he has change permissions on the Page model, change permissions on the plugin model and global change permissions. """ page = self._permissions_page staff_user = self.get_staff_user_with_no_permissions() plugin = self._add_plugin_to_page(page) endpoint = self.get_change_plugin_uri(plugin) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "change_link") self.add_page_permission( staff_user, page, can_change=True, ) with self.login_user_context(staff_user): data = model_to_dict(plugin, fields=["name", "external_link"]) data["name"] = "A link 2" response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 200) plugin.refresh_from_db() self.assertEqual(plugin.name, data["name"]) def test_user_cant_edit_plugin(self): """ User can't edit a plugin if he does not have change permissions on the Page model, does not have change permissions on the plugin model and/or does not have global change permissions. """ page = self._permissions_page staff_user = self.get_staff_user_with_no_permissions() plugin = self._add_plugin_to_page(page) endpoint = self.get_change_plugin_uri(plugin) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "change_link") self.add_page_permission( staff_user, page, can_change=False, ) with self.login_user_context(staff_user): data = model_to_dict(plugin, fields=["name", "external_link"]) data["name"] = "A link 2" response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) plugin.refresh_from_db() self.assertNotEqual(plugin.name, data["name"]) def test_user_can_delete_plugin(self): """ User can delete a plugin if he has change permissions on the Page model, delete permissions on the plugin model and global change permissions. """ page = self._permissions_page staff_user = self.get_staff_user_with_no_permissions() plugin = self._add_plugin_to_page(page) endpoint = self.get_delete_plugin_uri(plugin) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_link") self.add_page_permission( staff_user, page, can_change=True, ) with self.login_user_context(staff_user): data = {"post": True} response = self.client.post(endpoint, data) self.assertContains(response, '
    ') self.assertFalse(CMSPlugin.objects.filter(pk=plugin.pk).exists()) def test_user_cant_delete_plugin(self): """ User can't delete a plugin if he does not have change permissions on the Page model, does not have delete permissions on the plugin model and/or does not have global change permissions. """ page = self._permissions_page staff_user = self.get_staff_user_with_no_permissions() plugin = self._add_plugin_to_page(page) endpoint = self.get_delete_plugin_uri(plugin) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "delete_link") self.add_page_permission( staff_user, page, can_change=False, ) with self.login_user_context(staff_user): data = {"post": True} response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertTrue(CMSPlugin.objects.filter(pk=plugin.pk).exists()) def test_user_can_move_plugin(self): """ User can move a plugin if he has change permissions on the Page model, change permissions on the plugin model and global change permissions. """ page = self._permissions_page staff_user = self.get_staff_user_with_no_permissions() plugin = self._add_plugin_to_page(page) endpoint = self.get_move_plugin_uri(plugin) source_placeholder = plugin.placeholder target_placeholder = page.get_placeholders("en").get(slot="right-column") data = self._get_move_data(plugin, position=1, placeholder=target_placeholder) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "change_link") self.add_page_permission( staff_user, page, can_change=True, ) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 200) self.assertTrue(target_placeholder.get_plugins("en").filter(pk=plugin.pk)) self.assertFalse(source_placeholder.get_plugins("en").filter(pk=plugin.pk)) def test_user_cant_move_plugin(self): """ User can't move a plugin if he does not have change permissions on the Page model, does not have change permissions on the plugin model and/or does not have global change permissions. """ page = self._permissions_page staff_user = self.get_staff_user_with_no_permissions() plugin = self._add_plugin_to_page(page) endpoint = self.get_move_plugin_uri(plugin) source_placeholder = plugin.placeholder target_placeholder = page.get_placeholders("en").get(slot="right-column") data = self._get_move_data(plugin, position=1, placeholder=target_placeholder) self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "change_link") self.add_page_permission( staff_user, page, can_change=False, ) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertFalse(target_placeholder.get_plugins("en").filter(pk=plugin.pk)) self.assertTrue(source_placeholder.get_plugins("en").filter(pk=plugin.pk)) def test_user_can_copy_plugin(self): """ User can copy a plugin if he has change permissions on the Page model, add permissions on the plugin model and global change permissions. """ page = self._permissions_page staff_user = self.get_staff_user_with_no_permissions() plugin = self._add_plugin_to_page(page) translation = self._add_translation_to_page(page) endpoint = self.get_copy_plugin_uri(plugin) source_placeholder = plugin.placeholder page.get_content_obj(translation.language) target_placeholder = page.get_placeholders(translation.language).get(slot="right-column") data = { "source_plugin_id": plugin.pk, "source_placeholder_id": source_placeholder.pk, "source_language": plugin.language, "target_language": translation.language, "target_placeholder_id": target_placeholder.pk, } self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "add_link") self.add_page_permission( staff_user, page, can_change=True, ) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 200) self.assertTrue(source_placeholder.get_plugins("en").filter(pk=plugin.pk).exists()) self.assertTrue( target_placeholder.get_plugins(translation.language).filter(plugin_type=plugin.plugin_type).exists() ) def test_user_cant_copy_plugin(self): """ User can't copy a plugin if he does not have change permissions on the Page model, does not have add permissions on the plugin model, and/or does not have global change permissions. """ page = self._permissions_page staff_user = self.get_staff_user_with_no_permissions() plugin = self._add_plugin_to_page(page) translation = self._add_translation_to_page(page) endpoint = self.get_copy_plugin_uri(plugin) source_placeholder = plugin.placeholder target_placeholder = page.get_placeholders(translation.language).get(slot="right-column") data = { "source_plugin_id": plugin.pk, "source_placeholder_id": source_placeholder.pk, "source_language": plugin.language, "target_language": translation.language, "target_placeholder_id": target_placeholder.pk, } self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "add_link") self.add_page_permission( staff_user, page, can_change=False, ) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) self.assertTrue(source_placeholder.get_plugins("en").filter(pk=plugin.pk).exists()) self.assertFalse( target_placeholder.get_plugins(translation.language).filter(plugin_type=plugin.plugin_type).exists() ) def test_user_can_copy_plugins_to_language(self): """ User can copy all plugins to another language if he has change permissions on the Page model, add permissions on the plugins being copied and global change permissions. """ page = self._permissions_page staff_user = self.get_staff_user_with_no_permissions() source_translation = self.get_pagecontent_obj(page, "en") target_translation = self._add_translation_to_page(page) endpoint = self.get_admin_url(PageContent, "copy_language", source_translation.pk) plugins = [ self._add_plugin_to_page(page), self._add_plugin_to_page(page), self._add_plugin_to_page(page), self._add_plugin_to_page(page), ] source_placeholder = plugins[0].placeholder target_placeholder = target_translation.get_placeholders().get(slot=source_placeholder.slot) data = { "source_language": "en", "target_language": target_translation.language, } self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "add_link") self.add_page_permission( staff_user, page, can_change=True, ) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 200) new_plugins = target_placeholder.get_plugins() self.assertEqual(new_plugins.count(), len(plugins)) def test_user_cant_copy_plugins_to_language(self): """ User can't copy all plugins to another language if he does have change permissions on the Page model, does not have add permissions on the plugins being copied and/or does not have global change permissions. """ page = self._permissions_page staff_user = self.get_staff_user_with_no_permissions() translation = self._add_translation_to_page(page) endpoint = self.get_admin_url(PageContent, "copy_language", translation.pk) plugins = [ self._add_plugin_to_page(page), self._add_plugin_to_page(page), self._add_plugin_to_page(page), self._add_plugin_to_page(page), ] placeholder = plugins[0].placeholder data = { "source_language": "en", "target_language": translation.language, } self.add_permission(staff_user, "change_page") self.add_permission(staff_user, "add_link") self.add_page_permission( staff_user, page, can_change=False, ) with self.login_user_context(staff_user): response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 403) new_plugins = placeholder.get_plugins(translation.language) self.assertEqual(new_plugins.count(), 0) # Placeholder related tests def test_user_can_clear_empty_placeholder(self): """ User can clear an empty placeholder if he has change permissions on the Page model and global change permissions. """ page = self._permissions_page staff_user = self.get_staff_user_with_no_permissions() placeholder = page.get_placeholders("en").get(slot="body") endpoint = self.get_clear_placeholder_url(placeholder) self.add_permission(staff_user, "change_page") self.add_global_permission(staff_user, can_change=True) with self.login_user_context(staff_user): response = self.client.post(endpoint, {"test": 0}) self.assertEqual(response.status_code, 200) def test_user_cant_clear_empty_placeholder(self): """ User can't clear an empty placeholder if he does not have change permissions on the Page model and/or does not have global change permissions. """ page = self._permissions_page staff_user = self.get_staff_user_with_no_permissions() placeholder = page.get_placeholders("en").get(slot="body") endpoint = self.get_clear_placeholder_url(placeholder) self.add_permission(staff_user, "change_page") self.add_global_permission(staff_user, can_change=False) with self.login_user_context(staff_user): response = self.client.post(endpoint, {"test": 0}) self.assertEqual(response.status_code, 403) def test_user_can_clear_non_empty_placeholder(self): """ User can clear a placeholder with plugins if he has change permissions on the Page model, delete permissions on the plugin models in the placeholder and global change permissions. """ page = self._permissions_page staff_user = self.get_staff_user_with_no_permissions() plugins = [ self._add_plugin_to_page(page, "TextPlugin"), self._add_plugin_to_page(page, "LinkPlugin"), ] placeholder = plugins[0].placeholder endpoint = self.get_clear_placeholder_url(placeholder) self.add_permission(staff_user, "delete_text") self.add_permission(staff_user, "delete_link") self.add_permission(staff_user, "change_page") self.add_global_permission(staff_user, can_change=True) with self.login_user_context(staff_user): response = self.client.post(endpoint, {"test": 0}) self.assertEqual(response.status_code, 200) self.assertEqual(placeholder.get_plugins("en").count(), 0) def test_user_cant_clear_non_empty_placeholder(self): """ User can't clear a placeholder with plugins if he does not have change permissions on the Page model, does not have delete permissions on the plugin models in the placeholder and/or does not have global change permissions. """ page = self._permissions_page staff_user = self.get_staff_user_with_no_permissions() plugins = [ self._add_plugin_to_page(page, "TextPlugin"), self._add_plugin_to_page(page, "LinkPlugin"), ] placeholder = plugins[0].placeholder endpoint = self.get_clear_placeholder_url(placeholder) self.add_permission(staff_user, "delete_text") self.add_permission(staff_user, "delete_link") self.add_permission(staff_user, "change_page") self.add_global_permission(staff_user, can_change=False) with self.login_user_context(staff_user): response = self.client.post(endpoint, {"test": 0}) self.assertEqual(response.status_code, 403) self.assertEqual(placeholder.get_plugins("en").count(), 2) @override_settings(CMS_PERMISSION=False) class PermissionsOffTest(PermissionsTestCase): """ Tests all user interactions with the page admin while permissions are set to False. """ @override_settings(ROOT_URLCONF="cms.test_utils.project.noadmin_urls") class NoAdminPageTests(CMSTestCase): def test_get_page_from_request_fakeadmin_nopage(self): noadmin_apps = [app for app in installed_apps() if app != "django.contrib.admin"] with self.settings(INSTALLED_APPS=noadmin_apps): request = self.get_request("/en/admin/") page = get_page_from_request(request) self.assertEqual(page, None)