dynamicweb/cms/tests/test_page_admin.py

5522 lines
226 KiB
Python

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 <li>
# 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": "<p>text</p>"},
"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, "<title>Add a page</title>", 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, "<title>Add a page</title>", html=True)
self.assertContains(response, "&amp;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&amp;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 = (
'<ul class="errorlist" id="id_slug_error"><li>Page '
'<a href="{}" target="_blank">test page 1</a> '
"has the same url 'test-page-1' as current page.</li></ul>"
).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 = (
'<ul class="errorlist" id="id_slug_error">'
'<li>Page <a href="{}" target="_blank">subpage</a> '
"has the same url 'page/subpage' as current page.</li></ul>"
).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 = (
'<ul class="errorlist" id="id_slug_error">'
'<li>Page <a href="{}" target="_blank">child-page</a> '
"has the same url 'child-page' as current page.</li></ul>"
).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 = (
'<ul class="errorlist" id="id_slug_error">'
'<li>Page <a href="{}" target="_blank">page</a> '
"has the same url 'page' as current page.</li></ul>"
).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, "<title>Change a page</title>", 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 = '<ul class="errorlist" id="id_redirect_error"><li>Enter a valid URL.</li></ul>'
# 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 = (
'<ul class="errorlist" id="id_redirect_error">'
"<li>Enter a valid relative or absolute URL.</li></ul>"
)
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"] = '<script>alert("test")</script>'
# 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 = "<li>%s</li>"
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("<script>alert('title')</script>", "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 = "<img src=x onerror=alert('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, "&lt;img src=x onerror=alert(&#x27;url&#x27;)&gt; (en)")
self.assertContains(response, "&lt;script&gt;alert(&#x27;title&#x27;)&lt;/script&gt;")
self.assertNotContains(response, "<img src=x")
self.assertNotContains(response, "<script>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 = (
'<input type="text" name="overwrite_url" value="new-url" '
'maxlength="255" aria-describedby="id_overwrite_url_helptext" '
'id="id_overwrite_url">'
)
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 = (
'<ul class="errorlist" id="id_overwrite_url_error"><li>Page '
'<a href="{}" target="_blank">boo</a> '
"has the same url 'boo' as current page \"hoo\".</li></ul>"
).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 = (
'<input type="text" name="overwrite_url" maxlength="255" '
'aria-describedby="id_overwrite_url_helptext" id="id_overwrite_url">'
)
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 = '<ul class="errorlist"><li>Invalid application config value</li></ul>'
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 = "<li>\nHome\n</li>" "<li>\nAlpha\n</li>" "<li>\nGamma\n</li>"
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("<li>\nBeta\n</li>", 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 = "<li>\nHome\n</li>" "<li>\nAlpha\n</li>" "<li>\nGamma\n</li>"
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 = (
"<li>\nHome\n</li>"
"<li>\nAlpha"
"<ul>\n<li>\nBeta\n</li>\n</ul>\n</li>"
"<li>\nGamma"
"<ul>\n<li>\nDelta\n</li>\n</ul>\n</li>"
"<li>\nTheta\n</li>"
)
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: <a href="..."><span class="cms-icon cms-icon-redirect"></span></a>
self.assertRegex(
content, rf'<a href="{re.escape(admin_url)}"[^>]*>\s*<span class="cms-icon cms-icon-redirect">'
)
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("<li>\nAlpha\n</li>", content)
self.assertNotIn("<li>\nHome\n</li>", content)
self.assertNotIn("<li>\nBeta\n</li>", content)
self.assertNotIn("<li>\nGamma\n</li>", 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,
'<h2 id="pagepermission_set-2-heading" class="inline-heading">Page permissions</h2>',
html=True,
)
except AssertionError:
# Django < 5.1
self.assertContains(response, "<h2>Page permissions</h2>", html=True)
def assertNotContainsPermissions(self, response):
try:
# Django >= 5.1
self.assertNotContains(
response,
'<h2 id="pagepermission_set-1-heading" class="inline-heading">Page permissions</h2>',
html=True,
)
except AssertionError:
# Django < 5.1
self.assertNotContains(response, "<h2>Page permissions</h2>", 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,
'<a href="/en/admin/cms/pagecontent/">Page contents</a>',
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,
'<td class="user">staff</td>',
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, '<div class="success"></div>')
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, '<div class="success"></div>')
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)