forked from ungleich-public/dynamicweb
475 lines
22 KiB
Python
475 lines
22 KiB
Python
import sys
|
|
from unittest.mock import patch
|
|
|
|
from django.contrib.auth import get_user_model
|
|
from django.contrib.auth.models import Permission
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.contrib.sites.models import Site
|
|
from django.core.exceptions import FieldError
|
|
from django.db import connection
|
|
from django.template import TemplateDoesNotExist, TemplateSyntaxError
|
|
from django.test.utils import CaptureQueriesContext
|
|
from djangocms_text.cms_plugins import TextPlugin
|
|
from djangocms_text.models import Text
|
|
|
|
from cms.api import (
|
|
_verify_plugin_type,
|
|
add_plugin,
|
|
assign_user_to_page,
|
|
copy_plugins_to_language,
|
|
create_page,
|
|
create_page_content,
|
|
)
|
|
from cms.apphook_pool import apphook_pool
|
|
from cms.constants import TEMPLATE_INHERITANCE_MAGIC
|
|
from cms.models import Page, PageContent, Placeholder
|
|
from cms.plugin_base import CMSPluginBase
|
|
from cms.test_utils.testcases import CMSTestCase
|
|
from cms.test_utils.util.menu_extender import TestMenu
|
|
from cms.tests.test_apphooks import APP_MODULE, APP_NAME
|
|
from menus.menu_pool import menu_pool
|
|
|
|
|
|
def _grant_page_permission(user, codename):
|
|
content_type = ContentType.objects.get_by_natural_key("cms", "page")
|
|
perm = Permission.objects.get_or_create(codename="%s_page" % codename, content_type=content_type)[0]
|
|
user.user_permissions.add(perm)
|
|
|
|
|
|
class PythonAPITests(CMSTestCase):
|
|
def _get_default_create_page_arguments(self):
|
|
return {"title": "Test", "template": "nav_playground.html", "language": "en"}
|
|
|
|
def test_copy_plugins_to_language_does_not_query_per_placeholder(self):
|
|
page = create_page(**self._get_default_create_page_arguments())
|
|
create_page_content("de", "Test", page, template="nav_playground.html")
|
|
body = page.get_placeholders("en").get(slot="body")
|
|
right_column = page.get_placeholders("en").get(slot="right-column")
|
|
add_plugin(body, TextPlugin, "en", body="Body")
|
|
add_plugin(right_column, TextPlugin, "en", body="Right column")
|
|
|
|
with CaptureQueriesContext(connection) as queries:
|
|
copy_plugins_to_language(page, "en", "de")
|
|
|
|
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)
|
|
|
|
# One batched source-plugin query and one batched target-plugin count query.
|
|
self.assertEqual(len(plugin_batch_queries), 2)
|
|
# Concrete plugins are downcast in one query across all source placeholders.
|
|
self.assertEqual(len(concrete_plugin_queries), 1)
|
|
self.assertCountEqual(
|
|
Text.objects.filter(language="de").values_list("body", flat=True), ["Body", "Right column"]
|
|
)
|
|
|
|
def test_invalid_apphook_type(self):
|
|
self.assertRaises(TypeError, create_page, apphook=1, **self._get_default_create_page_arguments())
|
|
|
|
def test_invalid_template(self):
|
|
kwargs = self._get_default_create_page_arguments()
|
|
kwargs["template"] = "not_valid.htm"
|
|
with self.settings(CMS_TEMPLATES=[("not_valid.htm", "notvalid")]):
|
|
self.assertRaises(TemplateDoesNotExist, create_page, **kwargs)
|
|
kwargs["template"] = TEMPLATE_INHERITANCE_MAGIC
|
|
create_page(**kwargs)
|
|
|
|
def test_create_page_restores_current_user(self):
|
|
# create_page must not leave the creator in the _current_user context
|
|
# variable, and must restore (not clobber) a user set by a surrounding
|
|
# request context. Otherwise the creator leaks into the next operation
|
|
# on the same thread when CurrentUserMiddleware is not installed.
|
|
from cms.utils.permissions import get_current_user, set_current_user
|
|
|
|
prior_user = self.get_superuser()
|
|
creator = get_user_model().objects.create_user(
|
|
username="creator", email="creator@django-cms.org", password="creator", is_staff=True,
|
|
)
|
|
|
|
set_current_user(prior_user)
|
|
try:
|
|
create_page(created_by=creator, **self._get_default_create_page_arguments())
|
|
self.assertEqual(get_current_user(), prior_user)
|
|
finally:
|
|
set_current_user(None)
|
|
|
|
def test_create_page_content_restores_current_user(self):
|
|
# Same guarantee for a direct create_page_content() call, which sets
|
|
# _current_user for attribution but must restore the previous value.
|
|
from cms.utils.permissions import get_current_user, set_current_user
|
|
|
|
prior_user = self.get_superuser()
|
|
creator = get_user_model().objects.create_user(
|
|
username="creator", email="creator@django-cms.org", password="creator", is_staff=True,
|
|
)
|
|
page = create_page(**self._get_default_create_page_arguments())
|
|
|
|
set_current_user(prior_user)
|
|
try:
|
|
create_page_content("de", "Überschrift", page, created_by=creator)
|
|
self.assertEqual(get_current_user(), prior_user)
|
|
finally:
|
|
set_current_user(None)
|
|
|
|
def test_apphook_by_class(self):
|
|
if APP_MODULE in sys.modules:
|
|
del sys.modules[APP_MODULE]
|
|
apphooks = (f"{APP_MODULE}.{APP_NAME}",)
|
|
|
|
with self.settings(CMS_APPHOOKS=apphooks):
|
|
apphook_pool.clear()
|
|
apphook = apphook_pool.get_apphook(APP_NAME)
|
|
page = create_page(apphook=apphook, **self._get_default_create_page_arguments())
|
|
self.assertEqual(page.get_application_urls("en"), APP_NAME)
|
|
|
|
def test_nav_extenders_invalid_type(self):
|
|
if not menu_pool.discovered:
|
|
menu_pool.discover_menus()
|
|
self.old_menu = menu_pool.menus
|
|
menu_pool.menus = {"TestMenu": TestMenu}
|
|
self.assertRaises(
|
|
AssertionError, create_page, navigation_extenders=1, **self._get_default_create_page_arguments()
|
|
)
|
|
menu_pool.menus = self.old_menu
|
|
|
|
def test_nav_extenders_invalid_menu(self):
|
|
if not menu_pool.discovered:
|
|
menu_pool.discover_menus()
|
|
self.old_menu = menu_pool.menus
|
|
menu_pool.menus = {}
|
|
self.assertRaises(
|
|
AssertionError, create_page, navigation_extenders=TestMenu, **self._get_default_create_page_arguments()
|
|
)
|
|
menu_pool.menus = self.old_menu
|
|
|
|
def test_nav_extenders_valid(self):
|
|
if not menu_pool.discovered:
|
|
menu_pool.discover_menus()
|
|
self.old_menu = menu_pool.menus
|
|
menu_pool.menus = {"TestMenu": TestMenu}
|
|
page = create_page(navigation_extenders="TestMenu", **self._get_default_create_page_arguments())
|
|
self.assertEqual(page.navigation_extenders, "TestMenu")
|
|
menu_pool.menus = self.old_menu
|
|
|
|
def test_verify_plugin_type_invalid_type(self):
|
|
self.assertRaises(TypeError, _verify_plugin_type, 1)
|
|
|
|
def test_verify_plugin_type_string(self):
|
|
plugin_model, plugin_type = _verify_plugin_type("TextPlugin")
|
|
self.assertEqual(plugin_model, Text)
|
|
self.assertEqual(plugin_type, "TextPlugin")
|
|
|
|
def test_verify_plugin_type_string_invalid(self):
|
|
self.assertRaises(TypeError, _verify_plugin_type, "InvalidPlugin")
|
|
|
|
def test_verify_plugin_type_plugin_class(self):
|
|
plugin_model, plugin_type = _verify_plugin_type(TextPlugin)
|
|
self.assertEqual(plugin_model, Text)
|
|
self.assertEqual(plugin_type, "TextPlugin")
|
|
|
|
def test_verify_plugin_type_invalid_plugin_class(self):
|
|
class InvalidPlugin(CMSPluginBase):
|
|
model = Text
|
|
|
|
self.assertRaises(AssertionError, _verify_plugin_type, InvalidPlugin)
|
|
|
|
def test_assign_user_to_page_nothing(self):
|
|
page = create_page(**self._get_default_create_page_arguments())
|
|
user = get_user_model().objects.create_user(username="user", email="user@django-cms.org", password="user")
|
|
user.is_staff = True
|
|
self.assertFalse(page.has_change_permission(user))
|
|
|
|
def test_assign_user_to_page_single(self):
|
|
page = create_page(**self._get_default_create_page_arguments())
|
|
user = get_user_model().objects.create_user(username="user", email="user@django-cms.org", password="user")
|
|
user.is_staff = True
|
|
user.save()
|
|
assign_user_to_page(page, user, can_change=True)
|
|
self.assertFalse(page.has_change_permission(user))
|
|
self.assertFalse(page.has_add_permission(user))
|
|
_grant_page_permission(user, "change")
|
|
page = Page.objects.get(pk=page.pk)
|
|
user = get_user_model().objects.get(pk=user.pk)
|
|
self.assertTrue(page.has_change_permission(user))
|
|
self.assertFalse(page.has_add_permission(user))
|
|
|
|
def test_assign_user_to_page_all(self):
|
|
page = create_page(**self._get_default_create_page_arguments())
|
|
user = get_user_model().objects.create_user(username="user", email="user@django-cms.org", password="user")
|
|
user.is_staff = True
|
|
user.save()
|
|
assign_user_to_page(page, user, grant_all=True)
|
|
self.assertFalse(page.has_change_permission(user))
|
|
self.assertFalse(page.has_add_permission(user))
|
|
_grant_page_permission(user, "change")
|
|
_grant_page_permission(user, "add")
|
|
page = Page.objects.get(pk=page.pk)
|
|
user = get_user_model().objects.get(pk=user.pk)
|
|
self.assertTrue(page.has_change_permission(user))
|
|
self.assertTrue(page.has_add_permission(user))
|
|
|
|
def test_page_overwrite_url_default(self):
|
|
self.assertEqual(Page.objects.all().count(), 0)
|
|
home = create_page("root", "nav_playground.html", "en")
|
|
self.assertFalse(home.is_home)
|
|
page = create_page(**self._get_default_create_page_arguments())
|
|
self.assertFalse(page.is_home)
|
|
self.assertTrue(page.get_urls().filter(path="test", managed=True).exists())
|
|
|
|
def test_create_page_can_overwrite_url(self):
|
|
page_attrs = self._get_default_create_page_arguments()
|
|
page_attrs["overwrite_url"] = "test/home"
|
|
page = create_page(**page_attrs)
|
|
self.assertTrue(page.get_urls().filter(path="test/home", managed=False).exists())
|
|
|
|
def test_create_page_atomic(self):
|
|
# Ref: https://github.com/divio/django-cms/issues/5652
|
|
# We'll simulate a scenario where a user creates a page with an
|
|
# invalid template which causes Django to throw an error when the
|
|
# template is scanned for placeholders and thus short circuits the
|
|
# creation mechanism.
|
|
page_attrs = self._get_default_create_page_arguments()
|
|
|
|
# It's important to use TEMPLATE_INHERITANCE_MAGIC to avoid the cms
|
|
# from loading the template before saving and triggering the template error
|
|
# Instead, we delay the loading of the template until after the save is executed.
|
|
page_attrs["template"] = TEMPLATE_INHERITANCE_MAGIC
|
|
|
|
self.assertFalse(Page.objects.filter(pagecontent_set__template=TEMPLATE_INHERITANCE_MAGIC).exists())
|
|
|
|
with patch.object(PageContent, "template_choices", [("col_invalid.html", "notvalid")]):
|
|
self.assertRaises(TemplateSyntaxError, create_page, **page_attrs)
|
|
# The template raised an exception which should cause the database to roll back
|
|
# instead of committing a page in a partial state.
|
|
self.assertFalse(Page.objects.filter(pagecontent_set__template=TEMPLATE_INHERITANCE_MAGIC).exists())
|
|
|
|
def test_create_reverse_id_collision(self):
|
|
create_page("home", "nav_playground.html", "en", reverse_id="foo")
|
|
self.assertRaises(FieldError, create_page, "foo", "nav_playground.html", "en", reverse_id="foo")
|
|
self.assertTrue(Page.objects.count(), 2)
|
|
|
|
def test_create_page_with_all_parameters(self):
|
|
from cms import constants
|
|
|
|
if not menu_pool.discovered:
|
|
menu_pool.discover_menus()
|
|
old_menus = menu_pool.menus
|
|
menu_pool.menus = {"TestMenu": TestMenu}
|
|
|
|
if APP_MODULE in sys.modules:
|
|
del sys.modules[APP_MODULE]
|
|
apphooks = (f"{APP_MODULE}.{APP_NAME}",)
|
|
|
|
try:
|
|
with self.settings(CMS_APPHOOKS=apphooks):
|
|
apphook_pool.clear()
|
|
apphook = apphook_pool.get_apphook(APP_NAME)
|
|
|
|
site = Site.objects.get_current()
|
|
user = get_user_model().objects.create_user(
|
|
username="creator", email="creator@django-cms.org", password="creator"
|
|
)
|
|
parent = create_page("parent", "nav_playground.html", "en")
|
|
|
|
page = create_page(
|
|
title="Full Page",
|
|
template="nav_playground.html",
|
|
language="en",
|
|
menu_title="Full Menu",
|
|
page_title="Full Page Title",
|
|
slug="full-slug",
|
|
apphook=apphook,
|
|
apphook_namespace="full_ns",
|
|
redirect="/redirect-target/",
|
|
meta_description="A full meta description",
|
|
created_by=user,
|
|
parent=parent,
|
|
in_navigation=True,
|
|
soft_root=True,
|
|
reverse_id="full_reverse",
|
|
navigation_extenders="TestMenu",
|
|
site=site,
|
|
login_required=True,
|
|
limit_visibility_in_menu=constants.VISIBILITY_USERS,
|
|
position="first-child",
|
|
overwrite_url="full/overwrite",
|
|
xframe_options=constants.X_FRAME_OPTIONS_DENY,
|
|
)
|
|
|
|
# Page-level fields
|
|
self.assertEqual(page.parent_id, parent.pk)
|
|
self.assertEqual(page.reverse_id, "full_reverse")
|
|
|
|
self.assertEqual(page.navigation_extenders, "TestMenu")
|
|
self.assertEqual(page.get_application_urls("en"), APP_NAME)
|
|
self.assertEqual(page.application_namespace, "full_ns")
|
|
self.assertTrue(page.login_required)
|
|
self.assertEqual(page.site_id, site.pk)
|
|
self.assertEqual(page.created_by, user.username)
|
|
self.assertEqual(page.changed_by, user.username)
|
|
# first-child position: new page should come before any siblings of parent
|
|
self.assertEqual(parent.get_child_pages().first().pk, page.pk)
|
|
|
|
# PageContent-level fields
|
|
content = page.pagecontent_set.get(language="en")
|
|
self.assertEqual(content.title, "Full Page")
|
|
self.assertEqual(content.page_title, "Full Page Title")
|
|
self.assertEqual(content.menu_title, "Full Menu")
|
|
self.assertEqual(content.template, "nav_playground.html")
|
|
self.assertEqual(content.redirect, "/redirect-target/")
|
|
self.assertEqual(content.meta_description, "A full meta description")
|
|
self.assertTrue(content.in_navigation)
|
|
self.assertTrue(content.soft_root)
|
|
self.assertEqual(content.limit_visibility_in_menu, constants.VISIBILITY_USERS)
|
|
self.assertEqual(content.xframe_options, constants.X_FRAME_OPTIONS_DENY)
|
|
self.assertEqual(content.created_by, user.username)
|
|
|
|
# Slug and overwrite_url create the expected URL entry
|
|
self.assertTrue(page.get_urls().filter(language="en", slug="full-slug").exists())
|
|
self.assertTrue(
|
|
page.get_urls().filter(language="en", path="full/overwrite", managed=False).exists()
|
|
)
|
|
finally:
|
|
menu_pool.menus = old_menus
|
|
|
|
def test_create_page_parent_not_same_site(self):
|
|
site = Site.objects.create(id=2, name="example-2.com", domain="example-2.com")
|
|
home = create_page("home", "nav_playground.html", "en")
|
|
with self.settings(CMS_LANGUAGES={2: [{"code": "en", "name": "English"}]}):
|
|
with self.assertRaises(AssertionError):
|
|
create_page("home", "nav_playground.html", "en", parent=home, site=site)
|
|
|
|
home_2 = create_page("home", "nav_playground.html", "en", site=site)
|
|
new_page = create_page("child", "nav_playground.html", "en", parent=home_2)
|
|
self.assertEqual(new_page.site_id, 2)
|
|
|
|
|
|
class PythonAPIPluginTests(CMSTestCase):
|
|
def setUp(self):
|
|
self.placeholder = Placeholder.objects.create(slot="main")
|
|
|
|
def test_add_root_plugin(self):
|
|
root_plugin_1 = add_plugin(self.placeholder, "SolarSystemPlugin", "en")
|
|
self.assertEqual(root_plugin_1.position, 1)
|
|
self.assertEqual(root_plugin_1.language, "en")
|
|
self.assertEqual(root_plugin_1.plugin_type, "SolarSystemPlugin")
|
|
|
|
def test_add_root_plugin_first(self):
|
|
"""
|
|
User can add a new plugin to be in the first position
|
|
"""
|
|
root_plugin_2 = add_plugin(self.placeholder, "SolarSystemPlugin", "en")
|
|
root_plugin_3 = add_plugin(self.placeholder, "SolarSystemPlugin", "en", position="last-child")
|
|
root_plugin_1 = add_plugin(self.placeholder, "SolarSystemPlugin", "en", position="first-child")
|
|
new_tree = self.placeholder.get_plugins("en").values_list("pk", "position")
|
|
expected = [(root_plugin_1.pk, 1), (root_plugin_2.pk, 2), (root_plugin_3.pk, 3)]
|
|
self.assertSequenceEqual(new_tree, expected)
|
|
|
|
def test_add_root_plugin_middle(self):
|
|
root_plugin_1 = add_plugin(self.placeholder, "SolarSystemPlugin", "en")
|
|
root_plugin_2 = add_plugin(self.placeholder, "SolarSystemPlugin", "en", position="last-child")
|
|
root_plugin_4 = add_plugin(self.placeholder, "SolarSystemPlugin", "en", position="last-child")
|
|
root_plugin_6 = add_plugin(self.placeholder, "SolarSystemPlugin", "en", position="last-child")
|
|
root_plugin_3 = add_plugin(self.placeholder, "SolarSystemPlugin", "en", position="left", target=root_plugin_4)
|
|
root_plugin_5 = add_plugin(
|
|
self.placeholder, "SolarSystemPlugin", "en", position="right", target=root_plugin_4.reload()
|
|
)
|
|
new_tree = self.placeholder.get_plugins("en").values_list("pk", "position")
|
|
expected = [
|
|
(root_plugin_1.pk, 1),
|
|
(root_plugin_2.pk, 2),
|
|
(root_plugin_3.pk, 3),
|
|
(root_plugin_4.pk, 4),
|
|
(root_plugin_5.pk, 5),
|
|
(root_plugin_6.pk, 6),
|
|
]
|
|
self.assertSequenceEqual(new_tree, expected)
|
|
|
|
def test_add_child_plugin(self):
|
|
root_plugin_1 = add_plugin(self.placeholder, "SolarSystemPlugin", "en")
|
|
child_plugin_1 = add_plugin(self.placeholder, "SolarSystemPlugin", "en", target=root_plugin_1)
|
|
self.assertEqual(child_plugin_1.position, 2)
|
|
self.assertEqual(child_plugin_1.parent_id, root_plugin_1.pk)
|
|
self.assertEqual(child_plugin_1.language, "en")
|
|
self.assertEqual(child_plugin_1.plugin_type, "SolarSystemPlugin")
|
|
|
|
def test_add_child_plugin_first(self):
|
|
"""
|
|
User can add a new plugin to be in the first position
|
|
"""
|
|
root_plugin_1 = add_plugin(self.placeholder, "SolarSystemPlugin", "en")
|
|
child_plugin_2 = add_plugin(
|
|
self.placeholder, "SolarSystemPlugin", "en", position="last-child", target=root_plugin_1
|
|
)
|
|
child_plugin_3 = add_plugin(
|
|
self.placeholder, "SolarSystemPlugin", "en", position="last-child", target=root_plugin_1
|
|
)
|
|
child_plugin_1 = add_plugin(
|
|
self.placeholder, "SolarSystemPlugin", "en", position="first-child", target=root_plugin_1
|
|
)
|
|
root_plugin_2 = add_plugin(self.placeholder, "SolarSystemPlugin", "en")
|
|
new_tree = self.placeholder.get_plugins("en").values_list("pk", "position")
|
|
expected = [
|
|
(root_plugin_1.pk, 1),
|
|
(child_plugin_1.pk, 2),
|
|
(child_plugin_2.pk, 3),
|
|
(child_plugin_3.pk, 4),
|
|
(root_plugin_2.pk, 5),
|
|
]
|
|
self.assertSequenceEqual(new_tree, expected)
|
|
|
|
def test_add_child_plugin_middle(self):
|
|
root_plugin_1 = add_plugin(self.placeholder, "SolarSystemPlugin", "en")
|
|
child_plugin_1 = add_plugin(
|
|
self.placeholder, "SolarSystemPlugin", "en", position="last-child", target=root_plugin_1
|
|
)
|
|
child_plugin_2 = add_plugin(
|
|
self.placeholder, "SolarSystemPlugin", "en", position="last-child", target=root_plugin_1
|
|
)
|
|
child_plugin_4 = add_plugin(
|
|
self.placeholder, "SolarSystemPlugin", "en", position="last-child", target=root_plugin_1
|
|
)
|
|
child_plugin_6 = add_plugin(
|
|
self.placeholder, "SolarSystemPlugin", "en", position="last-child", target=root_plugin_1
|
|
)
|
|
child_plugin_3 = add_plugin(
|
|
self.placeholder, "SolarSystemPlugin", "en", position="left", target=child_plugin_4
|
|
)
|
|
child_plugin_5 = add_plugin(
|
|
self.placeholder, "SolarSystemPlugin", "en", position="right", target=child_plugin_4.reload()
|
|
)
|
|
root_plugin_3 = add_plugin(self.placeholder, "SolarSystemPlugin", "en", position="last-child")
|
|
new_tree = self.placeholder.get_plugins("en").values_list("pk", "position")
|
|
expected = [
|
|
(root_plugin_1.pk, 1),
|
|
(child_plugin_1.pk, 2),
|
|
(child_plugin_2.pk, 3),
|
|
(child_plugin_3.pk, 4),
|
|
(child_plugin_4.pk, 5),
|
|
(child_plugin_5.pk, 6),
|
|
(child_plugin_6.pk, 7),
|
|
(root_plugin_3.pk, 8),
|
|
]
|
|
self.assertSequenceEqual(new_tree, expected)
|
|
|
|
# Insert additional plugin right of first root plugin to see where it is positioned relative to the
|
|
# first root plugin's children
|
|
root_plugin_2 = add_plugin(self.placeholder, "SolarSystemPlugin", "en", position="right", target=root_plugin_1)
|
|
new_tree = self.placeholder.get_plugins("en").values_list("pk", "position")
|
|
expected = [
|
|
(root_plugin_1.pk, 1),
|
|
(child_plugin_1.pk, 2),
|
|
(child_plugin_2.pk, 3),
|
|
(child_plugin_3.pk, 4),
|
|
(child_plugin_4.pk, 5),
|
|
(child_plugin_5.pk, 6),
|
|
(child_plugin_6.pk, 7),
|
|
(root_plugin_2.pk, 8),
|
|
(root_plugin_3.pk, 9),
|
|
]
|
|
self.assertSequenceEqual(new_tree, expected)
|