forked from ungleich-public/dynamicweb
1544 lines
65 KiB
Python
1544 lines
65 KiB
Python
import sys
|
|
import types
|
|
import uuid
|
|
|
|
from django.apps import apps
|
|
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 import checks
|
|
from django.core.cache import cache
|
|
from django.core.checks.urls import check_url_config
|
|
from django.http import Http404, HttpResponse
|
|
from django.shortcuts import render
|
|
from django.template import Context, Template
|
|
from django.template.response import TemplateResponse
|
|
from django.test import RequestFactory, SimpleTestCase
|
|
from django.test.utils import override_settings
|
|
from django.urls import NoReverseMatch, clear_url_caches, include, path, resolve, reverse
|
|
from django.utils.timezone import now
|
|
from django.utils.translation import activate, override as force_language
|
|
from django.views.generic import TemplateView
|
|
|
|
from cms.admin.forms import AdvancedSettingsForm
|
|
from cms.api import create_page, create_page_content
|
|
from cms.app_base import CMSApp
|
|
from cms.apphook_pool import apphook_pool
|
|
from cms.appresolver import applications_page_check, clear_app_resolvers, get_app_patterns
|
|
from cms.middleware.page import get_page
|
|
from cms.models import PageContent
|
|
from cms.test_utils.project.placeholderapp.models import Example1
|
|
from cms.test_utils.testcases import CMSTestCase
|
|
from cms.tests.test_menu_utils import DumbPageLanguageUrl
|
|
from cms.toolbar.toolbar import CMSToolbar
|
|
from cms.views import render_object_edit
|
|
from menus.menu_pool import menu_pool
|
|
from menus.utils import DefaultLanguageChanger
|
|
|
|
APP_NAME = 'SampleApp'
|
|
NS_APP_NAME = 'NamespacedApp'
|
|
APP_MODULE = "cms.test_utils.project.sampleapp.cms_apps"
|
|
MENU_MODULE = "cms.test_utils.project.sampleapp.cms_menus"
|
|
|
|
|
|
class BaseApphooksTestCase(CMSTestCase):
|
|
"""
|
|
Test cases for apphooks
|
|
"""
|
|
|
|
def setUp(self):
|
|
clear_app_resolvers()
|
|
clear_url_caches()
|
|
|
|
if APP_MODULE in sys.modules:
|
|
del sys.modules[APP_MODULE]
|
|
|
|
self.reload_urls()
|
|
self.apphook_clear()
|
|
|
|
def tearDown(self):
|
|
clear_app_resolvers()
|
|
clear_url_caches()
|
|
|
|
if APP_MODULE in sys.modules:
|
|
del sys.modules[APP_MODULE]
|
|
|
|
self.reload_urls()
|
|
self.apphook_clear()
|
|
|
|
def reload_urls(self):
|
|
from django.conf import settings
|
|
|
|
url_modules = [
|
|
'cms.urls',
|
|
# TODO: Add here intermediary modules which may
|
|
# include() the 'cms.urls' if it isn't included
|
|
# directly in the root urlconf.
|
|
# '...',
|
|
'cms.test_utils.project.second_cms_urls_for_apphook_tests',
|
|
'cms.test_utils.project.urls_for_apphook_tests',
|
|
APP_MODULE,
|
|
settings.ROOT_URLCONF,
|
|
]
|
|
|
|
clear_app_resolvers()
|
|
clear_url_caches()
|
|
|
|
for module in url_modules:
|
|
if module in sys.modules:
|
|
del sys.modules[module]
|
|
|
|
|
|
class ApphooksTestCase(BaseApphooksTestCase):
|
|
def create_base_structure(self, apphook, content_langs, namespace=None):
|
|
self.apphook_clear()
|
|
superuser = get_user_model().objects.create_superuser('admin', 'admin@admin.com', 'admin')
|
|
self.superuser = superuser
|
|
page = create_page("home", "nav_playground.html", "en",
|
|
created_by=superuser)
|
|
create_page_content('de', page.get_title(), page)
|
|
child_page = create_page("child_page", "nav_playground.html", "en",
|
|
created_by=superuser, parent=page)
|
|
create_page_content('de', child_page.get_title(), child_page)
|
|
child_child_page = create_page("child_child_page", "nav_playground.html",
|
|
"en", created_by=superuser, parent=child_page, apphook=apphook,
|
|
apphook_namespace=namespace)
|
|
create_page_content("de", child_child_page.get_title(), child_child_page)
|
|
# publisher_public is set to draft on publish, issue with onetoone reverse
|
|
child_child_page = self.reload(child_child_page)
|
|
|
|
if isinstance(content_langs, str):
|
|
contents = child_child_page.get_content_obj(content_langs)
|
|
else:
|
|
contents = [child_child_page.get_content_obj(lang) for lang in content_langs]
|
|
|
|
self.reload_urls()
|
|
|
|
return contents
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.fourth_urls_for_apphook_tests')
|
|
def test_check_url_config(self):
|
|
"""
|
|
Test for urls config check.
|
|
"""
|
|
self.apphook_clear()
|
|
result = check_url_config(None)
|
|
self.assertEqual(len(result), 0)
|
|
|
|
@override_settings(CMS_APPHOOKS=[f'{APP_MODULE}.{APP_NAME}'])
|
|
def test_explicit_apphooks(self):
|
|
"""
|
|
Test explicit apphook loading with the CMS_APPHOOKS setting.
|
|
"""
|
|
self.apphook_clear()
|
|
hooks = apphook_pool.get_apphooks()
|
|
app_names = [hook[0] for hook in hooks]
|
|
self.assertEqual(len(hooks), 1)
|
|
self.assertEqual(app_names, [APP_NAME])
|
|
self.apphook_clear()
|
|
|
|
@override_settings(
|
|
INSTALLED_APPS=['cms.test_utils.project.sampleapp'],
|
|
ROOT_URLCONF='cms.test_utils.project.urls_for_apphook_tests',
|
|
)
|
|
def test_implicit_apphooks(self):
|
|
"""
|
|
Test implicit apphook loading with INSTALLED_APPS cms_apps.py
|
|
"""
|
|
self.apphook_clear()
|
|
hooks = apphook_pool.get_apphooks()
|
|
app_names = [hook[0] for hook in hooks]
|
|
self.assertEqual(len(hooks), 9)
|
|
self.assertIn(NS_APP_NAME, app_names)
|
|
self.assertIn(APP_NAME, app_names)
|
|
self.apphook_clear()
|
|
|
|
def test_apphook_on_homepage(self):
|
|
self.apphook_clear()
|
|
superuser = get_user_model().objects.create_superuser('admin', 'admin@admin.com', 'admin')
|
|
page = self.create_homepage("apphooked-page", "nav_playground.html", "en",
|
|
created_by=superuser, apphook="SampleApp")
|
|
create_page("not-apphooked-page", "nav_playground.html", "en",
|
|
created_by=superuser, apphook="", slug='blankapp')
|
|
english_title = page.pagecontent_set.all()[0]
|
|
self.assertEqual(english_title.language, 'en')
|
|
create_page_content("de", "aphooked-page-de", page)
|
|
with force_language("en"):
|
|
response = self.client.get(self.get_pages_root())
|
|
self.assertTemplateUsed(response, 'sampleapp/home.html')
|
|
self.assertContains(response, '<--noplaceholder-->')
|
|
response = self.client.get('/en/blankapp/')
|
|
self.assertTemplateUsed(response, 'nav_playground.html')
|
|
|
|
self.apphook_clear()
|
|
|
|
def test_pagecontent_get_template_uses_apphook_root_template(self):
|
|
"""When the apphook exposes a root template, PageContent.get_template
|
|
returns it instead of the page's own template — so the structure board
|
|
can show placeholders matching what the apphook actually renders."""
|
|
self.apphook_clear()
|
|
superuser = get_user_model().objects.create_superuser('admin', 'admin@admin.com', 'admin')
|
|
page = create_page(
|
|
'apphooked-page', 'nav_playground.html', 'en',
|
|
created_by=superuser, apphook=APP_NAME,
|
|
)
|
|
page_content = page.pagecontent_set.get(language='en')
|
|
|
|
# SampleApp's root view is a function-based view, so without a custom
|
|
# get_root_template override the lookup returns None and PageContent
|
|
# falls back to the page's configured template.
|
|
self.assertEqual(page_content.get_template(), 'nav_playground.html')
|
|
|
|
sample_app = apphook_pool.get_apphook(APP_NAME)
|
|
original = sample_app.get_root_template
|
|
sample_app.get_root_template = lambda **kwargs: 'apphook/from_root_view.html'
|
|
try:
|
|
del page_content._template_cache
|
|
self.assertEqual(
|
|
page_content.get_template(),
|
|
'apphook/from_root_view.html',
|
|
)
|
|
finally:
|
|
sample_app.get_root_template = original
|
|
|
|
self.apphook_clear()
|
|
|
|
def test_apphook_without_landing_page(self):
|
|
"""test rendering the apphook without landing page.
|
|
|
|
tests for https://github.com/django-cms/django-cms/issues/7765
|
|
"""
|
|
from django.contrib.contenttypes.models import ContentType
|
|
|
|
from cms import views
|
|
from cms.utils.urlutils import admin_reverse
|
|
|
|
APP_NAME = 'SampleAppWithoutLandingPage'
|
|
[content] = self.create_base_structure(
|
|
APP_NAME, ["en"], "apphook-without-landing"
|
|
)
|
|
content_type = ContentType.objects.get(app_label='cms', model='pagecontent')
|
|
endpoint = admin_reverse('cms_placeholder_render_object_edit', args=(content_type.pk, content.pk,))
|
|
# make sure, we do not get views.details as this always uses the
|
|
# published page content
|
|
self.assertIsNot(endpoint, views.details)
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.urls_for_apphook_tests')
|
|
def test_apphook_does_not_crash_django_checks(self):
|
|
# This test case reproduced the situation causing the error reported in issue #6717.
|
|
self.apphook_clear()
|
|
superuser = get_user_model().objects.create_superuser('admin', 'admin@admin.com', 'admin')
|
|
create_page("apphooked-page", "nav_playground.html", "en",
|
|
created_by=superuser, apphook="SampleApp")
|
|
self.reload_urls()
|
|
checks.run_checks()
|
|
self.apphook_clear()
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.urls_for_apphook_tests')
|
|
def test_apphook_on_root_reverse(self):
|
|
self.apphook_clear()
|
|
superuser = get_user_model().objects.create_superuser('admin', 'admin@admin.com', 'admin')
|
|
page = create_page("apphooked-page", "nav_playground.html", "en",
|
|
created_by=superuser, apphook="SampleApp")
|
|
create_page_content("de", "aphooked-page-de", page)
|
|
|
|
self.reload_urls()
|
|
|
|
self.assertFalse(reverse('sample-settings').startswith('//'))
|
|
self.apphook_clear()
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.urls_for_apphook_tests')
|
|
def test_multisite_apphooks(self):
|
|
self.apphook_clear()
|
|
site1, _ = Site.objects.get_or_create(pk=1)
|
|
site2, _ = Site.objects.get_or_create(pk=2)
|
|
superuser = get_user_model().objects.create_superuser('admin', 'admin@admin.com', 'admin')
|
|
request = self.get_request()
|
|
request.user = superuser
|
|
home_site_1 = create_page(
|
|
"home", "nav_playground.html", "en", created_by=superuser, site=site1
|
|
)
|
|
home_site_2 = create_page(
|
|
"home", "nav_playground.html", "de", created_by=superuser, site=site2
|
|
)
|
|
|
|
page_a_1 = create_page(
|
|
"apphooked-page", "nav_playground.html", "en", created_by=superuser, parent=home_site_1,
|
|
apphook=NS_APP_NAME, apphook_namespace="instance"
|
|
)
|
|
page_a_2 = create_page(
|
|
"apphooked-page", "nav_playground.html", "de", created_by=superuser, parent=home_site_1,
|
|
)
|
|
page_b_1 = create_page(
|
|
"apphooked-page", "nav_playground.html", "de", created_by=superuser, parent=home_site_2,
|
|
site=site2
|
|
)
|
|
|
|
# AdvancedSettingsForm expects a _site and _request attribute
|
|
# on __init__. These are normally set by the admin.
|
|
# In this case, we need to set these in a new class.
|
|
Meta = type('Meta', (AdvancedSettingsForm.Meta, object), {})
|
|
formClass = type(AdvancedSettingsForm)(
|
|
'AdvancedSettingsForm',
|
|
(AdvancedSettingsForm,),
|
|
{'Meta': Meta, '_site': site1, '_request': request},
|
|
)
|
|
form = formClass(instance=page_a_1)
|
|
self.assertFalse(form._check_unique_namespace_instance("instance"))
|
|
|
|
form = formClass(instance=page_a_2)
|
|
self.assertTrue(form._check_unique_namespace_instance("instance"))
|
|
|
|
formClass = type(AdvancedSettingsForm)(
|
|
'AdvancedSettingsForm',
|
|
(AdvancedSettingsForm,),
|
|
{'Meta': Meta, '_site': site2, '_request': request},
|
|
)
|
|
form = formClass(instance=page_b_1)
|
|
self.assertFalse(form._check_unique_namespace_instance("instance"))
|
|
|
|
self.apphook_clear()
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.second_urls_for_apphook_tests')
|
|
def test_get_page_for_apphook(self):
|
|
en_title, de_title = self.create_base_structure(APP_NAME, ['en', 'de'])
|
|
with force_language("en"):
|
|
path = reverse('sample-settings')
|
|
request = self.get_request(path)
|
|
request.LANGUAGE_CODE = 'en'
|
|
attached_to_page = applications_page_check(request)
|
|
self.assertEqual(attached_to_page.pk, en_title.page.pk)
|
|
|
|
response = self.client.get(path)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
self.assertTemplateUsed(response, 'sampleapp/home.html')
|
|
self.assertContains(response, en_title.title)
|
|
with force_language("de"):
|
|
path = reverse('sample-settings')
|
|
request = self.get_request(path)
|
|
request.LANGUAGE_CODE = 'de'
|
|
attached_to_page = applications_page_check(request)
|
|
self.assertEqual(attached_to_page.pk, de_title.page.pk)
|
|
|
|
response = self.client.get(path)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertTemplateUsed(response, 'sampleapp/home.html')
|
|
self.assertContains(response, de_title.title)
|
|
self.apphook_clear()
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.second_urls_for_apphook_tests')
|
|
def test_apphook_on_page_type_is_not_routed(self):
|
|
"""Page types are not served on the public site. An apphook attached to
|
|
one must not be dispatched either -- its patterns are matched before the
|
|
catch-all ``cms.views.details`` route, which would bypass the check
|
|
there."""
|
|
en_title, de_title = self.create_base_structure(APP_NAME, ['en', 'de'])
|
|
|
|
with force_language("en"):
|
|
path = reverse('sample-settings')
|
|
|
|
self.assertEqual(self.client.get(path).status_code, 200)
|
|
|
|
en_title.page.update(is_page_type=True)
|
|
self.reload_urls()
|
|
|
|
self.assertEqual(self.client.get(path).status_code, 404)
|
|
self.apphook_clear()
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.second_urls_for_apphook_tests')
|
|
def test_apphook_permissions(self):
|
|
en_title, de_title = self.create_base_structure(APP_NAME, ['en', 'de'])
|
|
|
|
with force_language("en"):
|
|
path = reverse('sample-settings')
|
|
|
|
response = self.client.get(path)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
en_title.page.update(login_required=True)
|
|
|
|
response = self.client.get(path)
|
|
self.assertEqual(response.status_code, 302)
|
|
self.apphook_clear()
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.second_urls_for_apphook_tests')
|
|
def test_apphook_permissions_preserves_view_name(self):
|
|
self.create_base_structure(APP_NAME, ['en', 'de'])
|
|
|
|
view_names = (
|
|
('sample-settings', 'sample_view'),
|
|
('sample-class-view', 'ClassView'),
|
|
('sample-class-based-view', 'view'),
|
|
# Naming convention changed in Django 4
|
|
)
|
|
|
|
with force_language("en"):
|
|
for url_name, view_name in view_names:
|
|
path = reverse(url_name)
|
|
match = resolve(path)
|
|
self.assertEqual(match.func.__name__, view_name)
|
|
|
|
def test_apphooks_with_excluded_permissions(self):
|
|
en_title = self.create_base_structure('SampleAppWithExcludedPermissions', 'en')
|
|
|
|
with force_language("en"):
|
|
excluded_path = reverse('excluded:example')
|
|
not_excluded_path = reverse('not_excluded:example')
|
|
|
|
en_title.page.update(login_required=True)
|
|
|
|
excluded_response = self.client.get(excluded_path)
|
|
not_excluded_response = self.client.get(not_excluded_path)
|
|
self.assertEqual(excluded_response.status_code, 200)
|
|
self.assertEqual(not_excluded_response.status_code, 302)
|
|
self.apphook_clear()
|
|
|
|
@override_settings(CMS_APPHOOKS=[f'{APP_MODULE}.{APP_NAME}'])
|
|
def test_apphook_without_name_defaults_to_class_name(self):
|
|
"""
|
|
Test that an apphook without a name defaults to the class name.
|
|
"""
|
|
@apphook_pool.register
|
|
class AppWithoutName(CMSApp):
|
|
def get_urls(self, page=None, language=None, **kwargs):
|
|
return ["sampleapp.urls"]
|
|
|
|
@apphook_pool.register
|
|
class AppWithName(CMSApp):
|
|
name = "Custom name"
|
|
|
|
def get_urls(self, page=None, language=None, **kwargs):
|
|
return ["sampleapp.urls"]
|
|
|
|
hooks = apphook_pool.get_apphooks()
|
|
hook_names = dict(hooks)
|
|
self.assertEqual(hook_names.get('AppWithoutName'), 'AppWithoutName')
|
|
self.assertEqual(hook_names.get('AppWithName'), 'Custom name')
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.urls_3')
|
|
def test_get_page_for_apphook_on_preview_or_edit(self):
|
|
if get_user_model().USERNAME_FIELD == 'email':
|
|
superuser = get_user_model().objects.create_superuser('admin', 'admin@admin.com', 'admin@admin.com')
|
|
else:
|
|
superuser = get_user_model().objects.create_superuser('admin', 'admin@admin.com', 'admin')
|
|
|
|
page = create_page("home", "nav_playground.html", "en",
|
|
created_by=superuser, apphook=APP_NAME)
|
|
create_page_content('de', page.get_title(), page)
|
|
|
|
with force_language("en"):
|
|
path = reverse('sample-settings')
|
|
request = self.get_request(path)
|
|
request.LANGUAGE_CODE = 'en'
|
|
attached_to_page = applications_page_check(request)
|
|
self.assertEqual(attached_to_page.pk, page.pk)
|
|
|
|
with force_language("de"):
|
|
path = reverse('sample-settings')
|
|
request = self.get_request(path)
|
|
request.LANGUAGE_CODE = 'de'
|
|
attached_to_page = applications_page_check(request)
|
|
self.assertEqual(attached_to_page.pk, page.pk)
|
|
|
|
with self.login_user_context(superuser):
|
|
with force_language("en"):
|
|
path = reverse('sample-settings')
|
|
request = self.get_request(path)
|
|
request.LANGUAGE_CODE = 'en'
|
|
attached_to_page = applications_page_check(request)
|
|
self.assertEqual(attached_to_page.pk, page.pk)
|
|
with force_language("de"):
|
|
path = reverse('sample-settings')
|
|
request = self.get_request(path)
|
|
request.LANGUAGE_CODE = 'de'
|
|
attached_to_page = applications_page_check(request)
|
|
self.assertEqual(attached_to_page.pk, page.pk)
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.second_urls_for_apphook_tests')
|
|
def test_get_root_page_for_apphook_with_instance_namespace(self):
|
|
en_title = self.create_base_structure(NS_APP_NAME, 'en', 'instance_ns')
|
|
|
|
self.reload_urls()
|
|
with force_language("en"):
|
|
reverse("example_app:example")
|
|
reverse("example1:example")
|
|
reverse("example2:example")
|
|
path = reverse('namespaced_app_ns:sample-root')
|
|
path_instance = reverse('instance_ns:sample-root')
|
|
self.assertEqual(path, path_instance)
|
|
|
|
request = self.get_request(path)
|
|
request.LANGUAGE_CODE = 'en'
|
|
|
|
attached_to_page = applications_page_check(request)
|
|
self.assertEqual(attached_to_page.pk, en_title.page.pk)
|
|
self.apphook_clear()
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.second_urls_for_apphook_tests')
|
|
def test_get_child_page_for_apphook_with_instance_namespace(self):
|
|
en_title = self.create_base_structure(NS_APP_NAME, 'en', 'instance_ns')
|
|
with force_language("en"):
|
|
path = reverse('namespaced_app_ns:sample-settings')
|
|
path_instance1 = reverse('instance_ns:sample-settings')
|
|
path_instance2 = reverse('namespaced_app_ns:sample-settings', current_app='instance_ns')
|
|
self.assertEqual(path, path_instance1)
|
|
self.assertEqual(path, path_instance2)
|
|
|
|
request = self.get_request(path)
|
|
request.LANGUAGE_CODE = 'en'
|
|
attached_to_page = applications_page_check(request)
|
|
self.assertEqual(attached_to_page.pk, en_title.page_id)
|
|
self.apphook_clear()
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.second_urls_for_apphook_tests')
|
|
def test_get_sub_page_for_apphook_with_implicit_current_app(self):
|
|
en_title = self.create_base_structure(NS_APP_NAME, 'en', 'namespaced_app_ns')
|
|
with force_language("en"):
|
|
path = reverse('namespaced_app_ns:current-app')
|
|
request = self.get_request(path)
|
|
request.LANGUAGE_CODE = 'en'
|
|
|
|
attached_to_page = applications_page_check(request)
|
|
self.assertEqual(attached_to_page.pk, en_title.page.pk)
|
|
|
|
response = self.client.get(path)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertTemplateUsed(response, 'sampleapp/app.html')
|
|
self.assertContains(response, 'namespaced_app_ns')
|
|
self.assertContains(response, path)
|
|
self.apphook_clear()
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.second_urls_for_apphook_tests')
|
|
def test_default_language_changer_with_implicit_current_app(self):
|
|
self.create_base_structure(NS_APP_NAME, ['en', 'de'], 'namespaced_app_ns')
|
|
self.reload_urls()
|
|
with force_language("en"):
|
|
path = reverse('namespaced_app_ns:translated-url')
|
|
request = self.get_request(path)
|
|
request.LANGUAGE_CODE = 'en'
|
|
|
|
url = DefaultLanguageChanger(request)('en')
|
|
self.assertEqual(url, path)
|
|
|
|
url = DefaultLanguageChanger(request)('de')
|
|
self.assertEqual(url, '/de%s' % path[3:].replace('/page', '/Seite'))
|
|
self.apphook_clear()
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.second_urls_for_apphook_tests')
|
|
def test_get_i18n_apphook_with_explicit_current_app(self):
|
|
self.apphook_clear()
|
|
titles = self.create_base_structure(NS_APP_NAME, ['en', 'de'], 'instance_1')
|
|
public_de_title = titles[1]
|
|
de_title = PageContent.objects.get(page=public_de_title.page, language="de")
|
|
|
|
self.reload_urls()
|
|
self.apphook_clear()
|
|
|
|
page2 = create_page("page2",
|
|
"nav_playground.html",
|
|
language="en",
|
|
created_by=self.superuser,
|
|
parent=de_title.page.parent,
|
|
apphook=NS_APP_NAME,
|
|
apphook_namespace="instance_2")
|
|
create_page_content("de", "de_title", page2, slug="slug")
|
|
clear_app_resolvers()
|
|
clear_url_caches()
|
|
|
|
if APP_MODULE in sys.modules:
|
|
del sys.modules[APP_MODULE]
|
|
|
|
self.reload_urls()
|
|
with force_language("de"):
|
|
reverse('namespaced_app_ns:current-app', current_app="instance_1")
|
|
reverse('namespaced_app_ns:current-app', current_app="instance_2")
|
|
reverse('namespaced_app_ns:current-app')
|
|
with force_language("en"):
|
|
reverse('namespaced_app_ns:current-app', current_app="instance_1")
|
|
reverse('namespaced_app_ns:current-app', current_app="instance_2")
|
|
reverse('namespaced_app_ns:current-app')
|
|
self.apphook_clear()
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.second_urls_for_apphook_tests')
|
|
def test_apphook_include_extra_parameters(self):
|
|
self.create_base_structure(NS_APP_NAME, ['en', 'de'], 'instance_1')
|
|
with force_language("en"):
|
|
path = reverse('namespaced_app_ns:extra_second')
|
|
request = self.get_request(path)
|
|
request.LANGUAGE_CODE = 'en'
|
|
response = self.client.get(path)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertTemplateUsed(response, 'sampleapp/extra.html')
|
|
self.assertContains(response, 'someopts')
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.second_urls_for_apphook_tests')
|
|
def test_get_sub_page_for_apphook_with_explicit_current_app(self):
|
|
en_title = self.create_base_structure(NS_APP_NAME, 'en', 'instance_ns')
|
|
with force_language("en"):
|
|
path = reverse('namespaced_app_ns:current-app')
|
|
|
|
request = self.get_request(path)
|
|
request.LANGUAGE_CODE = 'en'
|
|
|
|
attached_to_page = applications_page_check(request)
|
|
self.assertEqual(attached_to_page.pk, en_title.page.pk)
|
|
|
|
response = self.client.get(path)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertTemplateUsed(response, 'sampleapp/app.html')
|
|
self.assertContains(response, 'instance_ns')
|
|
self.assertContains(response, path)
|
|
self.apphook_clear()
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.second_urls_for_apphook_tests')
|
|
def test_include_urlconf(self):
|
|
self.create_base_structure(APP_NAME, 'en')
|
|
|
|
path = reverse('extra_second')
|
|
response = self.client.get(path)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertTemplateUsed(response, 'sampleapp/extra.html')
|
|
self.assertContains(response, "test included urlconf")
|
|
|
|
path = reverse('extra_first')
|
|
response = self.client.get(path)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertTemplateUsed(response, 'sampleapp/extra.html')
|
|
self.assertContains(response, "test urlconf")
|
|
with force_language("de"):
|
|
path = reverse('extra_first')
|
|
response = self.client.get(path)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertTemplateUsed(response, 'sampleapp/extra.html')
|
|
self.assertContains(response, "test urlconf")
|
|
with force_language("de"):
|
|
path = reverse('extra_second')
|
|
response = self.client.get(path)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertTemplateUsed(response, 'sampleapp/extra.html')
|
|
self.assertContains(response, "test included urlconf")
|
|
|
|
self.apphook_clear()
|
|
|
|
@override_settings(CMS_PERMISSION=False, ROOT_URLCONF='cms.test_utils.project.urls_2')
|
|
def test_apphook_breaking_under_home_with_new_path_caching(self):
|
|
home = self.create_homepage("home", "nav_playground.html", "en")
|
|
child = create_page("child", "nav_playground.html", "en", parent=home)
|
|
# not-home is what breaks stuff, because it contains the slug of the home page
|
|
not_home = create_page("not-home", "nav_playground.html", "en", parent=child)
|
|
create_page("subchild", "nav_playground.html", "en", parent=not_home, apphook='SampleApp')
|
|
with force_language("en"):
|
|
self.reload_urls()
|
|
urlpatterns = get_app_patterns()
|
|
resolver = urlpatterns[0]
|
|
url = resolver.reverse('sample-root')
|
|
self.assertEqual(url, 'child/not-home/subchild/')
|
|
|
|
@override_settings(CMS_PERMISSION=False, ROOT_URLCONF='cms.test_utils.project.urls_2')
|
|
def test_apphook_on_page_without_path_is_skipped(self):
|
|
"""A page whose ``PageUrl.path`` is ``None`` must not break url resolution.
|
|
|
|
Regression test for #8886: ``_get_app_patterns`` built ``page_url.path + "/"``
|
|
unconditionally, so a single such page raised ``TypeError`` and took down every
|
|
apphook on the site -- including the admin -- until the database was edited by hand.
|
|
Unpublishing a page with djangocms-versioning leaves exactly this state.
|
|
"""
|
|
home = self.create_homepage("home", "nav_playground.html", "en")
|
|
create_page("live-app", "nav_playground.html", "en", parent=home, apphook='SampleApp')
|
|
pathless = create_page("pathless-app", "nav_playground.html", "en", parent=home, apphook='SampleApp')
|
|
|
|
# Without page content the url keeps its slug but loses its path -- the core
|
|
# equivalent of an unpublished page.
|
|
PageContent.objects.filter(page=pathless, language="en").delete()
|
|
pathless.update_urls_from_content("en")
|
|
self.assertIsNone(pathless.urls.get(language="en").path)
|
|
|
|
with force_language("en"):
|
|
self.reload_urls()
|
|
urlpatterns = get_app_patterns() # must not raise TypeError
|
|
|
|
# the remaining apphook still resolves, i.e. the pathless page was skipped
|
|
# rather than the whole pattern list being abandoned
|
|
self.assertEqual(urlpatterns[0].reverse('sample-root'), 'live-app/')
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.urls')
|
|
def test_apphook_urlpattern_order(self):
|
|
# this one includes the actual cms.urls, so it can be tested if
|
|
# they are loaded in the correct order (the cms page pattern must be last)
|
|
# (the other testcases replicate the inclusion code and thus don't test this)
|
|
self.create_base_structure(APP_NAME, 'en')
|
|
path = reverse('extra_second')
|
|
response = self.client.get(path)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertTemplateUsed(response, 'sampleapp/extra.html')
|
|
self.assertContains(response, "test included urlconf")
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.urls')
|
|
def test_apphooks_receive_url_params(self):
|
|
# make sure that urlparams actually reach the apphook views
|
|
self.create_base_structure(APP_NAME, 'en')
|
|
path = reverse('sample-params', kwargs=dict(my_params='is-my-param-really-in-the-context-QUESTIONMARK'))
|
|
response = self.client.get(path)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertTemplateUsed(response, 'sampleapp/home.html')
|
|
self.assertContains(response, 'my_params: is-my-param-really-in-the-context-QUESTIONMARK')
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.third_urls_for_apphook_tests')
|
|
def test_multiple_apphooks(self):
|
|
# test for #1538
|
|
self.apphook_clear()
|
|
superuser = get_user_model().objects.create_superuser('admin', 'admin@admin.com', 'admin')
|
|
create_page("home", "nav_playground.html", "en", created_by=superuser, )
|
|
create_page("apphook1-page", "nav_playground.html", "en",
|
|
created_by=superuser, apphook="SampleApp")
|
|
create_page("apphook2-page", "nav_playground.html", "en",
|
|
created_by=superuser, apphook="SampleApp2")
|
|
|
|
reverse('sample-root')
|
|
reverse('sample2-root')
|
|
self.apphook_clear()
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.fourth_urls_for_apphook_tests')
|
|
def test_apphooks_return_urls_directly(self):
|
|
self.apphook_clear()
|
|
superuser = get_user_model().objects.create_superuser('admin', 'admin@admin.com', 'admin')
|
|
create_page("apphooked3-page", "nav_playground.html", "en",
|
|
created_by=superuser, apphook="SampleApp3")
|
|
self.reload_urls()
|
|
|
|
path = reverse('sample3-root')
|
|
response = self.client.get(path)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, 'Sample App 3 Response')
|
|
self.apphook_clear()
|
|
|
|
def test_apphook_pool_register_returns_apphook(self):
|
|
@apphook_pool.register
|
|
class TestApp(CMSApp):
|
|
name = "Test App"
|
|
self.assertIsNotNone(TestApp)
|
|
|
|
# Now test the quick return codepath, when apphooks is not empty
|
|
apphook_pool.apphooks.append("foo")
|
|
|
|
@apphook_pool.register
|
|
class TestApp2(CMSApp):
|
|
name = "Test App 2"
|
|
self.assertIsNotNone(TestApp2)
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.second_urls_for_apphook_tests')
|
|
def test_apphook_csrf_exempt_endpoint(self):
|
|
self.create_base_structure(NS_APP_NAME, 'en', 'instance_ns')
|
|
|
|
client = self.client_class(enforce_csrf_checks=True)
|
|
|
|
with force_language("en"):
|
|
path = reverse('namespaced_app_ns:sample-exempt')
|
|
|
|
response = client.post(path)
|
|
|
|
# Assert our POST request went through
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
with force_language("en"):
|
|
path = reverse('namespaced_app_ns:sample-account')
|
|
|
|
response = client.post(path)
|
|
|
|
# Assert our POST request did not go through
|
|
self.assertEqual(response.status_code, 403)
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.second_urls_for_apphook_tests')
|
|
def test_toolbar_current_app_namespace(self):
|
|
self.create_base_structure(NS_APP_NAME, 'en', 'instance_ns')
|
|
with force_language("en"):
|
|
path = reverse('namespaced_app_ns:sample-settings')
|
|
request = self.get_request(path)
|
|
toolbar = CMSToolbar(request)
|
|
self.assertTrue(
|
|
toolbar.toolbars['cms.test_utils.project.sampleapp.cms_toolbars.CategoryToolbar'].is_current_app
|
|
)
|
|
self.assertFalse(
|
|
toolbar.toolbars[
|
|
'cms.test_utils.project.extensionapp.cms_toolbars.MyPageContentExtensionToolbar'
|
|
].is_current_app
|
|
)
|
|
|
|
# Testing a decorated view
|
|
with force_language("en"):
|
|
path = reverse('namespaced_app_ns:sample-exempt')
|
|
request = self.get_request(path)
|
|
toolbar = CMSToolbar(request)
|
|
self.assertEqual(
|
|
toolbar.toolbars['cms.test_utils.project.sampleapp.cms_toolbars.CategoryToolbar'].app_path,
|
|
'cms.test_utils.project.sampleapp'
|
|
)
|
|
self.assertTrue(
|
|
toolbar.toolbars['cms.test_utils.project.sampleapp.cms_toolbars.CategoryToolbar'].is_current_app
|
|
)
|
|
self.assertEqual(
|
|
toolbar.toolbars[
|
|
'cms.test_utils.project.extensionapp.cms_toolbars.MyPageContentExtensionToolbar'
|
|
].app_path,
|
|
'cms.test_utils.project.sampleapp'
|
|
)
|
|
self.assertFalse(
|
|
toolbar.toolbars[
|
|
'cms.test_utils.project.extensionapp.cms_toolbars.MyPageContentExtensionToolbar'
|
|
].is_current_app
|
|
)
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.second_urls_for_apphook_tests')
|
|
def test_toolbar_current_app_apphook_with_implicit_current_app(self):
|
|
self.create_base_structure(NS_APP_NAME, 'en', 'namespaced_app_ns')
|
|
with force_language("en"):
|
|
path = reverse('namespaced_app_ns:current-app')
|
|
request = self.get_request(path)
|
|
toolbar = CMSToolbar(request)
|
|
self.assertEqual(
|
|
toolbar.toolbars['cms.test_utils.project.sampleapp.cms_toolbars.CategoryToolbar'].app_path,
|
|
'cms.test_utils.project.sampleapp'
|
|
)
|
|
self.assertTrue(
|
|
toolbar.toolbars['cms.test_utils.project.sampleapp.cms_toolbars.CategoryToolbar'].is_current_app
|
|
)
|
|
self.assertEqual(
|
|
toolbar.toolbars[
|
|
'cms.test_utils.project.extensionapp.cms_toolbars.MyPageContentExtensionToolbar'
|
|
].app_path,
|
|
'cms.test_utils.project.sampleapp'
|
|
)
|
|
self.assertFalse(
|
|
toolbar.toolbars[
|
|
'cms.test_utils.project.extensionapp.cms_toolbars.MyPageContentExtensionToolbar'
|
|
].is_current_app
|
|
)
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.placeholderapp_urls')
|
|
def test_toolbar_no_namespace(self):
|
|
# Test with a basic application with no defined app_name and no namespace
|
|
self.create_base_structure(APP_NAME, 'en')
|
|
path = reverse('detail', kwargs={'id': 20})
|
|
request = self.get_request(path)
|
|
toolbar = CMSToolbar(request)
|
|
self.assertFalse(
|
|
toolbar.toolbars['cms.test_utils.project.sampleapp.cms_toolbars.CategoryToolbar'].is_current_app
|
|
)
|
|
self.assertFalse(
|
|
toolbar.toolbars[
|
|
'cms.test_utils.project.extensionapp.cms_toolbars.MyPageContentExtensionToolbar'
|
|
].is_current_app
|
|
)
|
|
self.assertTrue(
|
|
toolbar.toolbars['cms.test_utils.project.placeholderapp.cms_toolbars.Example1Toolbar'].is_current_app
|
|
)
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.placeholderapp_urls')
|
|
def test_toolbar_multiple_supported_apps(self):
|
|
# Test with a basic application with no defined app_name and no namespace
|
|
self.create_base_structure(APP_NAME, 'en')
|
|
path = reverse('detail', kwargs={'id': 20})
|
|
request = self.get_request(path)
|
|
toolbar = CMSToolbar(request)
|
|
self.assertEqual(toolbar.toolbars['cms.test_utils.project.sampleapp.cms_toolbars.CategoryToolbar'].app_path,
|
|
'cms.test_utils.project.placeholderapp')
|
|
self.assertFalse(
|
|
toolbar.toolbars['cms.test_utils.project.sampleapp.cms_toolbars.CategoryToolbar'].is_current_app
|
|
)
|
|
self.assertEqual(
|
|
toolbar.toolbars[
|
|
'cms.test_utils.project.extensionapp.cms_toolbars.MyPageContentExtensionToolbar']
|
|
.app_path,
|
|
'cms.test_utils.project.placeholderapp'
|
|
)
|
|
self.assertFalse(
|
|
toolbar.toolbars[
|
|
'cms.test_utils.project.extensionapp.cms_toolbars.MyPageContentExtensionToolbar'
|
|
].is_current_app
|
|
)
|
|
self.assertEqual(
|
|
toolbar.toolbars['cms.test_utils.project.extensionapp.cms_toolbars.MyPageExtensionToolbar'].app_path,
|
|
'cms.test_utils.project.placeholderapp'
|
|
)
|
|
self.assertTrue(
|
|
toolbar.toolbars['cms.test_utils.project.extensionapp.cms_toolbars.MyPageExtensionToolbar'].is_current_app
|
|
)
|
|
self.assertEqual(
|
|
toolbar.toolbars['cms.test_utils.project.placeholderapp.cms_toolbars.Example1Toolbar'].app_path,
|
|
'cms.test_utils.project.placeholderapp'
|
|
)
|
|
self.assertTrue(
|
|
toolbar.toolbars['cms.test_utils.project.placeholderapp.cms_toolbars.Example1Toolbar'].is_current_app
|
|
)
|
|
|
|
@override_settings(
|
|
CMS_APPHOOKS=['cms.test_utils.project.placeholderapp.cms_apps.Example1App'],
|
|
ROOT_URLCONF='cms.test_utils.project.placeholderapp_urls',
|
|
)
|
|
def test_toolbar_staff(self):
|
|
# Test that the toolbar contains edit mode switcher if placeholders are available
|
|
apphooks = (
|
|
'cms.test_utils.project.placeholderapp.cms_apps.Example1App',
|
|
)
|
|
|
|
switcher_id = 'Mode Switcher'
|
|
|
|
with self.settings(CMS_APPHOOKS=apphooks, ROOT_URLCONF='cms.test_utils.project.placeholderapp_urls'):
|
|
self.create_base_structure('Example1App', 'en')
|
|
ex1 = Example1.objects.create(char_1='1', char_2='2', char_3='3', char_4='4', date_field=now())
|
|
path = reverse('example_detail', kwargs={'pk': ex1.pk})
|
|
|
|
self.user = self._create_user('admin_staff', True, True)
|
|
with self.login_user_context(self.user):
|
|
response = self.client.get(path + "?toolbar_on")
|
|
|
|
request = response.context['request']
|
|
toolbar = request.toolbar
|
|
items = toolbar.get_right_items()
|
|
switchers = [item.identifier for item in items
|
|
if getattr(item, 'identifier', '') == switcher_id]
|
|
self.assertEqual(len(switchers), 1)
|
|
|
|
self.user = self._create_user('staff', True, False)
|
|
with self.login_user_context(self.user):
|
|
response = self.client.get(path + "?toolbar_on")
|
|
|
|
request = response.context['request']
|
|
request.user = get_user_model().objects.get(pk=self.user.pk)
|
|
toolbar = request.toolbar
|
|
items = toolbar.get_right_items()
|
|
switchers = [item.identifier for item in items
|
|
if getattr(item, 'identifier', '') == switcher_id]
|
|
self.assertEqual(len(switchers), 0)
|
|
|
|
self.user.user_permissions.add(Permission.objects.get(codename='change_example1'))
|
|
with self.login_user_context(self.user):
|
|
response = self.client.get(path + "?toolbar_on")
|
|
|
|
request = response.context['request']
|
|
request.user = get_user_model().objects.get(pk=self.user.pk)
|
|
toolbar = request.toolbar
|
|
items = toolbar.get_right_items()
|
|
switchers = [item.identifier for item in items
|
|
if getattr(item, 'identifier', '') == switcher_id]
|
|
self.assertEqual(len(switchers), 0)
|
|
|
|
self.user.user_permissions.add(Permission.objects.get(codename='use_structure'))
|
|
with self.login_user_context(self.user):
|
|
response = self.client.get(path + "?toolbar_on")
|
|
|
|
request = response.context['request']
|
|
request.user = get_user_model().objects.get(pk=self.user.pk)
|
|
toolbar = request.toolbar
|
|
items = toolbar.get_right_items()
|
|
switchers = [item.identifier for item in items
|
|
if getattr(item, 'identifier', '') == switcher_id]
|
|
self.assertEqual(len(switchers), 1)
|
|
|
|
self.user = None
|
|
|
|
def test_nested_apphooks_urls(self):
|
|
# make sure that urlparams actually reach the apphook views
|
|
with self.settings(ROOT_URLCONF='cms.test_utils.project.urls'):
|
|
self.apphook_clear()
|
|
|
|
superuser = get_user_model().objects.create_superuser('admin', 'admin@admin.com', 'admin')
|
|
create_page("home", "nav_playground.html", "en", created_by=superuser, )
|
|
parent_page = create_page("parent-apphook-page", "nav_playground.html", "en",
|
|
created_by=superuser, apphook="ParentApp")
|
|
create_page("child-apphook-page", "nav_playground.html", "en", parent=parent_page,
|
|
created_by=superuser, apphook="ChildApp")
|
|
|
|
parent_app_path = reverse('parentapp_view', kwargs={'path': 'parent/path/'})
|
|
child_app_path = reverse('childapp_view', kwargs={'path': 'child-path/'})
|
|
|
|
# Ensure the page structure is ok before getting responses
|
|
self.assertEqual(parent_app_path, '/en/parent-apphook-page/parent/path/')
|
|
self.assertEqual(child_app_path, '/en/parent-apphook-page/child-apphook-page/child-path/')
|
|
|
|
# Get responses for both paths and ensure that the right view will answer
|
|
response = self.client.get(parent_app_path)
|
|
self.assertContains(response, 'parent app content', status_code=200)
|
|
|
|
response = self.client.get(child_app_path)
|
|
self.assertContains(response, 'child app content', status_code=200)
|
|
|
|
self.apphook_clear()
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.second_urls_for_apphook_tests')
|
|
def test_get_apps(self):
|
|
"""
|
|
Check that urlconf are dynamically loaded according to the different page the apphook is
|
|
attached to
|
|
"""
|
|
titles = self.create_base_structure('VariableUrlsApp', ['en', 'de'])
|
|
titles[0].page.reverse_id = 'page1'
|
|
titles[0].page.save()
|
|
|
|
self.reload_urls()
|
|
|
|
# only one urlconf is configured given that only one page is created
|
|
with force_language('de'):
|
|
reverse('extra_first')
|
|
with self.assertRaises(NoReverseMatch):
|
|
reverse('sample2-root')
|
|
|
|
self.reload_urls()
|
|
self.apphook_clear()
|
|
|
|
page2 = create_page('page2', 'nav_playground.html',
|
|
'en', created_by=self.superuser,
|
|
parent=titles[0].page.parent,
|
|
apphook='VariableUrlsApp', reverse_id='page2')
|
|
create_page_content('de', 'de_title', page2, slug='slug')
|
|
|
|
self.reload_urls()
|
|
|
|
with force_language('de'):
|
|
reverse('sample2-root')
|
|
reverse('extra_first')
|
|
|
|
self.apphook_clear()
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.second_urls_for_apphook_tests')
|
|
def test_get_menus(self):
|
|
"""
|
|
Check that menus are dynamically loaded according to the different page the apphook is
|
|
attached to
|
|
"""
|
|
titles = self.create_base_structure('VariableUrlsApp', ['en', 'de'])
|
|
titles[0].page.reverse_id = 'page1'
|
|
titles[0].page.save()
|
|
cache.clear()
|
|
self.reload_urls()
|
|
menu_pool.discover_menus()
|
|
cache.clear()
|
|
|
|
request = self.get_request('/')
|
|
renderer = menu_pool.get_renderer(request)
|
|
nodes = renderer.get_nodes()
|
|
nodes_urls = [node.url for node in nodes]
|
|
self.assertTrue(reverse('sample-account') in nodes_urls)
|
|
self.assertFalse('/en/child_page/page2/' in nodes_urls)
|
|
|
|
self.reload_urls()
|
|
self.apphook_clear()
|
|
|
|
cache.clear()
|
|
self.reload_urls()
|
|
|
|
page2 = create_page('page2', 'nav_playground.html',
|
|
'en', created_by=self.superuser,
|
|
parent=titles[0].page.parent,
|
|
in_navigation=True,
|
|
apphook='VariableUrlsApp', reverse_id='page2')
|
|
create_page_content('de', 'de_title', page2, slug='slug')
|
|
request = self.get_request('/page2/')
|
|
renderer = menu_pool.get_renderer(request)
|
|
nodes = renderer.get_nodes()
|
|
nodes_urls = [node.url for node in nodes]
|
|
self.assertTrue(reverse('sample-account') in nodes_urls)
|
|
self.assertTrue(reverse('sample2-root') in nodes_urls)
|
|
self.assertTrue('/static/fresh/' in nodes_urls)
|
|
|
|
self.apphook_clear()
|
|
|
|
@override_settings(
|
|
CMS_APPHOOKS=['cms.test_utils.project.sampleapp.cms_apps.AppWithNoMenu'],
|
|
)
|
|
def test_menu_node_is_selected_on_app_root(self):
|
|
"""
|
|
If a user requests a page with an apphook,
|
|
the menu should mark the node for that page as selected.
|
|
"""
|
|
defaults = {
|
|
'language': 'en',
|
|
'in_navigation': True,
|
|
'template': 'nav_playground.html',
|
|
}
|
|
homepage = create_page('EN-P1', **defaults)
|
|
homepage.set_as_homepage()
|
|
app_root = create_page('EN-P2', apphook='AppWithNoMenu', apphook_namespace='app_with_no_menu', **defaults)
|
|
|
|
# Public version
|
|
request = self.get_request('/en/en-p2/')
|
|
request.current_page = get_page(request)
|
|
menu_nodes = menu_pool.get_renderer(request).get_nodes()
|
|
self.assertEqual(len(menu_nodes), 2)
|
|
self.assertEqual(menu_nodes[0].id, homepage.pk)
|
|
self.assertEqual(menu_nodes[0].selected, False)
|
|
self.assertEqual(menu_nodes[1].id, app_root.pk)
|
|
self.assertEqual(menu_nodes[1].selected, True)
|
|
|
|
@override_settings(
|
|
CMS_APPHOOKS=['cms.test_utils.project.sampleapp.cms_apps.AppWithNoMenu'],
|
|
)
|
|
def test_menu_node_is_selected_on_app_sub_path(self):
|
|
"""
|
|
If a user requests a path belonging to an apphook,
|
|
the menu should mark the node for the apphook page as selected.
|
|
"""
|
|
# Refs - https://github.com/divio/django-cms/issues/6336
|
|
defaults = {
|
|
'language': 'en',
|
|
'in_navigation': True,
|
|
'template': 'nav_playground.html',
|
|
}
|
|
homepage = create_page('EN-P1', **defaults)
|
|
homepage.set_as_homepage()
|
|
app_root = create_page('EN-P2', apphook='AppWithNoMenu', apphook_namespace='app_with_no_menu', **defaults)
|
|
|
|
request = self.get_request('/en/en-p2/settings/')
|
|
request.current_page = get_page(request)
|
|
menu_nodes = menu_pool.get_renderer(request).get_nodes()
|
|
self.assertEqual(len(menu_nodes), 2)
|
|
self.assertEqual(menu_nodes[0].id, homepage.pk)
|
|
self.assertEqual(menu_nodes[0].selected, False)
|
|
self.assertEqual(menu_nodes[1].id, app_root.pk)
|
|
self.assertEqual(menu_nodes[1].selected, True)
|
|
|
|
@override_settings(CMS_APPHOOKS=['cms.test_utils.project.sampleapp.cms_apps.SampleApp3'],)
|
|
def test_apphook_root_page_placeholders_in_endpoints(self):
|
|
"""
|
|
Test that page content placeholders are available in edit and preview endpoints
|
|
for the root page of an apphook.
|
|
"""
|
|
from django.contrib.contenttypes.models import ContentType
|
|
|
|
from cms.toolbar.utils import get_object_edit_url, get_object_preview_url, get_object_structure_url
|
|
|
|
# Create a page with an apphook attached
|
|
self.apphook_clear()
|
|
superuser = get_user_model().objects.create_superuser('admin', 'admin@admin.com', 'admin')
|
|
page = create_page(
|
|
"apphook-root",
|
|
"nav_playground.html",
|
|
"en",
|
|
created_by=superuser,
|
|
apphook="SampleApp3"
|
|
)
|
|
content = page.get_content_obj("en")
|
|
|
|
self.reload_urls()
|
|
|
|
# Test edit endpoint
|
|
edit_url = get_object_edit_url(content, language="en")
|
|
with self.login_user_context(superuser):
|
|
response = self.client.get(edit_url)
|
|
self.assertEqual(response.status_code, 200)
|
|
# Verify the apphook view is called
|
|
self.assertContains(response, 'Sample App 3 Response')
|
|
# Check that toolbar was set with the page content object
|
|
self.assertTrue(hasattr(response.wsgi_request, 'toolbar'))
|
|
toolbar = response.wsgi_request.toolbar
|
|
# Verify the toolbar object is the page content
|
|
self.assertIsNotNone(toolbar.obj, "toolbar.obj should be set for edit endpoint")
|
|
self.assertEqual(toolbar.obj, content, "toolbar.obj should match the page content")
|
|
|
|
# Test preview endpoint
|
|
preview_url = get_object_preview_url(content, language="en")
|
|
with self.login_user_context(superuser):
|
|
response = self.client.get(preview_url)
|
|
self.assertEqual(response.status_code, 200)
|
|
# Verify the apphook view is called
|
|
self.assertContains(response, 'Sample App 3 Response')
|
|
# Check that toolbar was set with the page content object
|
|
self.assertTrue(hasattr(response.wsgi_request, 'toolbar'))
|
|
toolbar = response.wsgi_request.toolbar
|
|
# Verify the toolbar object is the page content
|
|
self.assertIsNotNone(toolbar.obj, "toolbar.obj should be set for preview endpoint")
|
|
self.assertEqual(toolbar.obj, content, "toolbar.obj should match the page content")
|
|
|
|
# Test structure endpoint
|
|
structure_url = get_object_structure_url(content, language="en")
|
|
with self.login_user_context(superuser):
|
|
response = self.client.get(structure_url)
|
|
self.assertEqual(response.status_code, 200)
|
|
# Check that toolbar was set with the page content object
|
|
self.assertTrue(hasattr(response.wsgi_request, 'toolbar'))
|
|
toolbar = response.wsgi_request.toolbar
|
|
# Verify the toolbar object is the page content
|
|
self.assertIsNotNone(toolbar.obj, "toolbar.obj should be set for structure endpoint")
|
|
self.assertEqual(toolbar.obj, content, "toolbar.obj should match the page content")
|
|
# Verify that page content placeholders are present in the markup
|
|
for placeholder in content.get_placeholders():
|
|
# Check that placeholder ID is in the response content
|
|
self.assertContains(
|
|
response,
|
|
f'"placeholder_id": "{placeholder.pk}"',
|
|
msg_prefix=f"Placeholder {placeholder.slot} should be present in structure markup"
|
|
)
|
|
|
|
self.apphook_clear()
|
|
|
|
|
|
class ApphooksPageLanguageUrlTestCase(CMSTestCase):
|
|
def setUp(self):
|
|
clear_app_resolvers()
|
|
clear_url_caches()
|
|
|
|
if APP_MODULE in sys.modules:
|
|
del sys.modules[APP_MODULE]
|
|
self.reload_urls()
|
|
|
|
def tearDown(self):
|
|
clear_app_resolvers()
|
|
clear_url_caches()
|
|
|
|
if APP_MODULE in sys.modules:
|
|
del sys.modules[APP_MODULE]
|
|
self.apphook_clear()
|
|
|
|
def reload_urls(self):
|
|
from django.conf import settings
|
|
|
|
url_modules = [
|
|
'cms.urls',
|
|
'cms.test_utils.project.second_cms_urls_for_apphook_tests',
|
|
settings.ROOT_URLCONF,
|
|
]
|
|
|
|
clear_app_resolvers()
|
|
clear_url_caches()
|
|
|
|
for module in url_modules:
|
|
if module in sys.modules:
|
|
del sys.modules[module]
|
|
|
|
def test_page_language_url_for_apphook(self):
|
|
|
|
self.apphook_clear()
|
|
superuser = get_user_model().objects.create_superuser('admin', 'admin@admin.com', 'admin')
|
|
page = self.create_homepage("home", "nav_playground.html", "en", created_by=superuser)
|
|
create_page_content('de', page.get_title(), page)
|
|
|
|
child_page = create_page("child_page", "nav_playground.html", "en",
|
|
created_by=superuser, parent=page)
|
|
create_page_content('de', child_page.get_title(), child_page)
|
|
child_page.refresh_from_db()
|
|
|
|
child_child_page = create_page("child_child_page", "nav_playground.html",
|
|
"en", created_by=superuser, parent=child_page, apphook='SampleApp')
|
|
create_page_content("de", f'{child_child_page.get_title()}_de', child_child_page)
|
|
child_child_page.refresh_from_db()
|
|
|
|
with force_language("en"):
|
|
path = reverse('extra_first')
|
|
|
|
request = self.get_request(path)
|
|
request.LANGUAGE_CODE = 'en'
|
|
request.current_page = child_child_page
|
|
|
|
fake_context = {'request': request}
|
|
tag = DumbPageLanguageUrl()
|
|
|
|
output = tag.get_context(fake_context, 'en')
|
|
url = output['content']
|
|
|
|
self.assertEqual(url, '/en/child_page/child_child_page/extra_1/')
|
|
|
|
output = tag.get_context(fake_context, 'de')
|
|
url = output['content']
|
|
# look the extra "_de"
|
|
self.assertEqual(url, '/de/child_page/child_child_page_de/extra_1/')
|
|
|
|
output = tag.get_context(fake_context, 'fr')
|
|
url = output['content']
|
|
self.assertEqual(url, '/en/child_page/child_child_page/extra_1/')
|
|
|
|
self.apphook_clear()
|
|
|
|
|
|
@override_settings(SITE_ID="")
|
|
class ApphooksSiteTestCase(BaseApphooksTestCase):
|
|
"""
|
|
Test cases for apphooks in a multisite configuration using a single instance. Sites
|
|
are mocked for the test, in reality either taken from the request or from a middleware.
|
|
|
|
These test cases verify that apphooks are only accessible on their respective sites
|
|
when multiple sites are configured in Django CMS.
|
|
"""
|
|
|
|
def create_pages(self):
|
|
self.apphook_clear()
|
|
self.site1 = Site.objects.get(pk=1)
|
|
self.site2 = Site.objects.create(domain='otherserver', name='Site 2')
|
|
|
|
superuser = self.get_superuser()
|
|
self.page1 = create_page("Page 1", "nav_playground.html", "de", site=self.site1, apphook='SampleApp', created_by=superuser)
|
|
create_page_content("en", "en_title", self.page1, slug="page-1")
|
|
self.page2 = create_page("Page 2", "nav_playground.html", "de", site=self.site1, created_by=superuser)
|
|
self.page3 = create_page("Page 3", "nav_playground.html", "de", site=self.site2, apphook='SampleApp', created_by=superuser)
|
|
self.page4 = create_page("Page 4", "nav_playground.html", "de", site=self.site2, created_by=superuser)
|
|
self.page2.set_as_homepage()
|
|
self.page4.set_as_homepage()
|
|
activate("de")
|
|
|
|
def get_for_site(self, site, url):
|
|
resolver_match = resolve(url)
|
|
request = self.get_request(url)
|
|
request.META["host"] = f"{site.domain}:80"
|
|
request.site = site
|
|
request.resolver_match = resolver_match
|
|
try:
|
|
return resolver_match.func(request, *resolver_match.args, **resolver_match.kwargs)
|
|
except Http404 as e:
|
|
return HttpResponse(str(e), status=404)
|
|
|
|
def test_apphook_access_on_site1(self):
|
|
self.create_pages()
|
|
|
|
# Verify that the cms_site_filter decorator was called
|
|
url = self.page1.get_absolute_url("de")
|
|
|
|
response = self.get_for_site(self.site1, url)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
response = self.get_for_site(self.site1, '/de/page-3/')
|
|
self.assertEqual(response.status_code, 404) # Sollte nicht erreichbar sein
|
|
|
|
def test_apphook_access_on_site2(self):
|
|
self.create_pages()
|
|
|
|
# Verify that the cms_site_filter decorator was called
|
|
url = self.page3.get_absolute_url("de")
|
|
|
|
response = self.get_for_site(self.site2, url)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
response = self.get_for_site(self.site2, '/de/page-1/')
|
|
self.assertEqual(response.status_code, 404) # Sollte nicht erreichbar sein
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.second_urls_for_apphook_tests')
|
|
def test_apphook_site_filter_preserves_view_name(self):
|
|
self.create_pages()
|
|
# Force a fresh import of the urlconf now that the apphook pages exist so
|
|
# that the global APP_RESOLVERS are rebuilt for the overridden
|
|
# ROOT_URLCONF. Without this, reverse() below depends on whether prior
|
|
# tests left the url modules cached in sys.modules (order dependence).
|
|
self.reload_urls()
|
|
|
|
view_names = (
|
|
('sample-settings', 'sample_view'),
|
|
('sample-class-view', 'ClassView'),
|
|
('sample-class-based-view', 'view'),
|
|
)
|
|
|
|
with force_language("de"):
|
|
for url_name, view_name in view_names:
|
|
path = reverse(url_name)
|
|
match = resolve(path)
|
|
self.assertEqual(match.func.__name__, view_name)
|
|
|
|
class ApphookFrontendEditingTests(BaseApphooksTestCase):
|
|
"""
|
|
Tests for rendering external models attached via apphooks in frontend editing mode.
|
|
Ensures that context remains consistent (e.g., current_page is resolved).
|
|
"""
|
|
|
|
|
|
@override_settings(ROOT_URLCONF='cms.test_utils.project.urls')
|
|
def test_current_page_resolution_in_render_object_edit(self):
|
|
from django.contrib.sessions.backends.base import SessionBase
|
|
|
|
from cms.test_utils.project.sampleapp.models import Category
|
|
|
|
# 1. Setup: Create an apphooked page
|
|
superuser = self.get_superuser()
|
|
create_page(
|
|
title="Apphook Page",
|
|
template="nav_playground.html",
|
|
language="en",
|
|
created_by=superuser,
|
|
apphook="SampleApp"
|
|
)
|
|
self.reload_urls()
|
|
|
|
# 2. Setup: Create an external model instance (Category)
|
|
category = Category.add_root(name="Test Category")
|
|
ct = ContentType.objects.get_for_model(Category)
|
|
|
|
# 3. Setup: Register a mock renderer
|
|
cms_extension = apps.get_app_config('cms').cms_extension
|
|
|
|
def mock_render_category(request, obj):
|
|
t = Template("{% load cms_tags %}[{% page_attribute 'page_title' %}]")
|
|
return HttpResponse(t.render(Context({"request": request})))
|
|
|
|
original_renderer = cms_extension.toolbar_enabled_models.get(Category)
|
|
cms_extension.toolbar_enabled_models[Category] = mock_render_category
|
|
|
|
try:
|
|
# 4. Action: Create a request
|
|
url = f"/admin/cms/placeholder/render-object-edit/{ct.pk}/{category.pk}/"
|
|
request = RequestFactory().get(url)
|
|
request.user = superuser
|
|
request.session = SessionBase()
|
|
request.toolbar = CMSToolbar(request)
|
|
|
|
# 5. Execution
|
|
response = render_object_edit(request, ct.pk, category.pk)
|
|
content = response.content.decode('utf-8')
|
|
|
|
# 6. Verification
|
|
self.assertEqual(content, "[Apphook Page]")
|
|
|
|
finally:
|
|
if original_renderer:
|
|
cms_extension.toolbar_enabled_models[Category] = original_renderer
|
|
elif Category in cms_extension.toolbar_enabled_models:
|
|
del cms_extension.toolbar_enabled_models[Category]
|
|
|
|
|
|
def _register_root_template_urlconf(patterns):
|
|
"""Build a unique, throwaway urlconf module so it can be referenced by
|
|
string path (which is what ``django.urls.get_resolver`` expects)."""
|
|
name = f'cms.tests._root_template_test_urlconf_{uuid.uuid4().hex}'
|
|
module = types.ModuleType(name)
|
|
module.urlpatterns = patterns
|
|
sys.modules[name] = module
|
|
return name
|
|
|
|
|
|
class GetRootTemplateTests(SimpleTestCase):
|
|
"""Unit tests for :meth:`cms.app_base.CMSApp.get_root_template`."""
|
|
|
|
def setUp(self):
|
|
clear_url_caches()
|
|
|
|
def tearDown(self):
|
|
clear_url_caches()
|
|
|
|
def _make_app(self, urlconf):
|
|
class _App(CMSApp):
|
|
_urls = [urlconf]
|
|
return _App()
|
|
|
|
def test_class_based_view_with_class_level_template_name(self):
|
|
class HomeView(TemplateView):
|
|
template_name = 'apphook/cbv_home.html'
|
|
|
|
urlconf = _register_root_template_urlconf([path('', HomeView.as_view())])
|
|
|
|
self.assertEqual(
|
|
self._make_app(urlconf).get_root_template(),
|
|
'apphook/cbv_home.html',
|
|
)
|
|
|
|
def test_class_based_view_with_template_name_via_as_view_kwargs(self):
|
|
# ``TemplateView.as_view(template_name=...)`` stashes the template on
|
|
# ``view_initkwargs``; the lookup must pick it up from there.
|
|
urlconf = _register_root_template_urlconf([
|
|
path('', TemplateView.as_view(template_name='apphook/inline.html')),
|
|
])
|
|
|
|
self.assertEqual(
|
|
self._make_app(urlconf).get_root_template(),
|
|
'apphook/inline.html',
|
|
)
|
|
|
|
def test_fbv_returning_template_response_returns_none(self):
|
|
# The template name lives inside the response, not on the callable.
|
|
# Without rendering the view, get_root_template can't see it.
|
|
def home(request):
|
|
return TemplateResponse(request, 'apphook/fbv_tr.html', {})
|
|
|
|
urlconf = _register_root_template_urlconf([path('', home)])
|
|
|
|
self.assertIsNone(self._make_app(urlconf).get_root_template())
|
|
|
|
def test_fbv_using_render_shortcut_returns_none(self):
|
|
# ``render()`` returns a plain HttpResponse with the body already
|
|
# rendered — by the time we'd inspect a response, the template name
|
|
# is gone.
|
|
def home(request):
|
|
return render(request, 'apphook/fbv_render.html')
|
|
|
|
urlconf = _register_root_template_urlconf([path('', home)])
|
|
|
|
self.assertIsNone(self._make_app(urlconf).get_root_template())
|
|
|
|
def test_subclass_can_override_get_root_template(self):
|
|
# The advertised escape hatch when inference doesn't fit: override
|
|
# the method on the apphook subclass.
|
|
class _App(CMSApp):
|
|
_urls = []
|
|
|
|
def get_root_template(self, page=None, language=None, **kwargs):
|
|
return 'apphook/manual.html'
|
|
|
|
self.assertEqual(_App().get_root_template(), 'apphook/manual.html')
|
|
|
|
def test_returns_none_when_no_root_pattern(self):
|
|
class DetailView(TemplateView):
|
|
template_name = 'apphook/detail.html'
|
|
|
|
urlconf = _register_root_template_urlconf([
|
|
path('detail/', DetailView.as_view()),
|
|
])
|
|
|
|
self.assertIsNone(self._make_app(urlconf).get_root_template())
|
|
|
|
def test_root_pattern_via_include(self):
|
|
# ``path('', include(...))`` — the root pattern lives inside the
|
|
# included urlconf and must still be discovered.
|
|
class HomeView(TemplateView):
|
|
template_name = 'apphook/inner_home.html'
|
|
|
|
inner = _register_root_template_urlconf([path('', HomeView.as_view())])
|
|
outer = _register_root_template_urlconf([path('', include(inner))])
|
|
|
|
self.assertEqual(
|
|
self._make_app(outer).get_root_template(),
|
|
'apphook/inner_home.html',
|
|
)
|
|
|
|
def test_non_root_pattern_template_is_ignored(self):
|
|
# When the root view doesn't expose a template, we must NOT fall back
|
|
# to a sibling pattern's template — that would be misleading.
|
|
def root_fbv(request):
|
|
return TemplateResponse(request, 'apphook/root.html', {})
|
|
|
|
class AboutView(TemplateView):
|
|
template_name = 'apphook/about.html'
|
|
|
|
urlconf = _register_root_template_urlconf([
|
|
path('', root_fbv),
|
|
path('about/', AboutView.as_view()),
|
|
])
|
|
|
|
self.assertIsNone(self._make_app(urlconf).get_root_template())
|
|
|
|
def test_inferred_template_is_not_cached(self):
|
|
# Inferred templates must NOT be cached on the apphook instance:
|
|
# apphook instances are shared singletons, and apphooks like
|
|
# VariableUrlsApp return different urlconfs depending on page/language.
|
|
# Caching the first inferred template would lock it in for every
|
|
# subsequent (possibly differently-routed) call.
|
|
class HomeOne(TemplateView):
|
|
template_name = 'apphook/one.html'
|
|
|
|
class HomeTwo(TemplateView):
|
|
template_name = 'apphook/two.html'
|
|
|
|
urlconf_one = _register_root_template_urlconf([path('', HomeOne.as_view())])
|
|
urlconf_two = _register_root_template_urlconf([path('', HomeTwo.as_view())])
|
|
|
|
urlconfs = iter([urlconf_one, urlconf_two])
|
|
|
|
class _App(CMSApp):
|
|
def get_urls(self, page=None, language=None, **kwargs):
|
|
return [next(urlconfs)]
|
|
|
|
app = _App()
|
|
self.assertEqual(app.get_root_template(), 'apphook/one.html')
|
|
self.assertEqual(app.get_root_template(), 'apphook/two.html')
|
|
|
|
def test_invalid_urlconf_string_returns_none(self):
|
|
# A urlconf that fails to import shouldn't crash the lookup.
|
|
class _App(CMSApp):
|
|
_urls = ['cms.tests._does_not_exist_xyz']
|
|
|
|
self.assertIsNone(_App().get_root_template())
|