forked from ungleich-public/dynamicweb
2216 lines
83 KiB
Python
2216 lines
83 KiB
Python
|
|
import copy
|
|||
|
|
|
|||
|
|
from django.contrib.auth.models import AnonymousUser, Group, Permission
|
|||
|
|
from django.contrib.sites.models import Site
|
|||
|
|
from django.core.cache import caches
|
|||
|
|
from django.db import connection
|
|||
|
|
from django.template import Template, TemplateSyntaxError
|
|||
|
|
from django.template.context import Context
|
|||
|
|
from django.test.utils import CaptureQueriesContext, override_settings
|
|||
|
|
from django.utils.translation import activate, override as force_language
|
|||
|
|
|
|||
|
|
from cms.api import create_page, create_page_content
|
|||
|
|
from cms.apphook_pool import apphook_pool
|
|||
|
|
from cms.cms_menus import CMSMenu, get_visible_page_contents
|
|||
|
|
from cms.models import ACCESS_PAGE_AND_DESCENDANTS, Page, PageContent
|
|||
|
|
from cms.models.permissionmodels import GlobalPagePermission, PagePermission
|
|||
|
|
from cms.test_utils.fixtures.menus import (
|
|||
|
|
ExtendedMenusFixture,
|
|||
|
|
MenusFixture,
|
|||
|
|
SoftrootFixture,
|
|||
|
|
SubMenusFixture,
|
|||
|
|
)
|
|||
|
|
from cms.test_utils.project.sampleapp.cms_apps import (
|
|||
|
|
NamespacedApp,
|
|||
|
|
SampleApp,
|
|||
|
|
SampleApp2,
|
|||
|
|
)
|
|||
|
|
from cms.test_utils.project.sampleapp.cms_menus import (
|
|||
|
|
SampleAppMenu,
|
|||
|
|
StaticMenu,
|
|||
|
|
StaticMenu2,
|
|||
|
|
)
|
|||
|
|
from cms.test_utils.testcases import CMSTestCase
|
|||
|
|
from cms.test_utils.util.context_managers import LanguageOverride, apphooks
|
|||
|
|
from cms.test_utils.util.mock import AttributeObject
|
|||
|
|
from cms.utils.conf import get_cms_setting
|
|||
|
|
from cms.utils.i18n import get_default_language_for_site
|
|||
|
|
from menus.base import NavigationNode
|
|||
|
|
from menus.menu_pool import _build_nodes_inner_for_one_menu, menu_pool
|
|||
|
|
from menus.models import CacheKey
|
|||
|
|
from menus.utils import cut_levels, find_selected, mark_descendants
|
|||
|
|
|
|||
|
|
|
|||
|
|
class BaseMenuTest(CMSTestCase):
|
|||
|
|
def _get_nodes(self, path="/"):
|
|||
|
|
node1 = NavigationNode("1", "/1/", 1)
|
|||
|
|
node2 = NavigationNode("2", "/2/", 2, 1)
|
|||
|
|
node3 = NavigationNode("3", "/3/", 3, 2)
|
|||
|
|
node4 = NavigationNode("4", "/4/", 4, 2)
|
|||
|
|
node5 = NavigationNode("5", "/5/", 5)
|
|||
|
|
nodes = [node1, node2, node3, node4, node5]
|
|||
|
|
tree = _build_nodes_inner_for_one_menu([n for n in nodes], "test")
|
|||
|
|
request = self.get_request(path)
|
|||
|
|
renderer = menu_pool.get_renderer(request)
|
|||
|
|
renderer.apply_modifiers(tree, request)
|
|||
|
|
return tree, nodes
|
|||
|
|
|
|||
|
|
def setUp(self):
|
|||
|
|
super().setUp()
|
|||
|
|
if not menu_pool.discovered:
|
|||
|
|
menu_pool.discover_menus()
|
|||
|
|
self.old_menu = menu_pool.menus
|
|||
|
|
menu_pool.menus = {"CMSMenu": self.old_menu["CMSMenu"]}
|
|||
|
|
menu_pool.clear(Site.objects.get_current().pk)
|
|||
|
|
activate("en")
|
|||
|
|
|
|||
|
|
def tearDown(self):
|
|||
|
|
menu_pool.menus = self.old_menu
|
|||
|
|
super().tearDown()
|
|||
|
|
|
|||
|
|
def get_page(self, num):
|
|||
|
|
return Page.objects.get(pagecontent_set__title="P%s" % num)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class MenuDiscoveryTest(ExtendedMenusFixture, CMSTestCase):
|
|||
|
|
def setUp(self):
|
|||
|
|
super().setUp()
|
|||
|
|
menu_pool.discovered = False
|
|||
|
|
self.old_menu = menu_pool.menus
|
|||
|
|
menu_pool.menus = {}
|
|||
|
|
menu_pool.discover_menus()
|
|||
|
|
menu_pool.register_menu(SampleAppMenu)
|
|||
|
|
menu_pool.register_menu(StaticMenu)
|
|||
|
|
menu_pool.register_menu(StaticMenu2)
|
|||
|
|
|
|||
|
|
def tearDown(self):
|
|||
|
|
menu_pool.menus = self.old_menu
|
|||
|
|
super().tearDown()
|
|||
|
|
|
|||
|
|
def test_menu_registered(self):
|
|||
|
|
menu_pool.discovered = False
|
|||
|
|
menu_pool.discover_menus()
|
|||
|
|
|
|||
|
|
# The following tests that get_registered_menus()
|
|||
|
|
# returns all menus registered based on the for_rendering flag
|
|||
|
|
|
|||
|
|
# A list of menu classes registered regardless of whether they
|
|||
|
|
# have instances attached or not
|
|||
|
|
registered = menu_pool.get_registered_menus(for_rendering=False)
|
|||
|
|
|
|||
|
|
# A list of menu classes registered and filter out any attached menu
|
|||
|
|
# if it does not have instances.
|
|||
|
|
registered_for_rendering = menu_pool.get_registered_menus(for_rendering=True)
|
|||
|
|
|
|||
|
|
# We've registered three menus
|
|||
|
|
self.assertEqual(len(registered), 3)
|
|||
|
|
|
|||
|
|
# But two of those are attached menus and shouldn't be rendered.
|
|||
|
|
self.assertEqual(len(registered_for_rendering), 1)
|
|||
|
|
|
|||
|
|
# Attached both menus to separate pages
|
|||
|
|
create_page("apphooked-page", "nav_playground.html", "en", navigation_extenders="StaticMenu")
|
|||
|
|
|
|||
|
|
create_page("apphooked-page", "nav_playground.html", "en", navigation_extenders="StaticMenu2")
|
|||
|
|
|
|||
|
|
registered = menu_pool.get_registered_menus(for_rendering=False)
|
|||
|
|
registered_for_rendering = menu_pool.get_registered_menus(for_rendering=True)
|
|||
|
|
|
|||
|
|
# Even though we've registered three menus, the total is give because two
|
|||
|
|
# are attached menus and each attached menu has two instances.
|
|||
|
|
self.assertEqual(len(registered), 3)
|
|||
|
|
self.assertEqual(len(registered_for_rendering), 3)
|
|||
|
|
|
|||
|
|
def test_menu_registered_in_renderer(self):
|
|||
|
|
menu_pool.discovered = False
|
|||
|
|
menu_pool.discover_menus()
|
|||
|
|
|
|||
|
|
# The following tests that a menu renderer calculates the registered
|
|||
|
|
# menus on a request basis.
|
|||
|
|
|
|||
|
|
request_1 = self.get_request("/en/")
|
|||
|
|
request_1_renderer = menu_pool.get_renderer(request_1)
|
|||
|
|
|
|||
|
|
registered = menu_pool.get_registered_menus(for_rendering=False)
|
|||
|
|
|
|||
|
|
self.assertEqual(len(registered), 3)
|
|||
|
|
self.assertEqual(len(request_1_renderer.menus), 1)
|
|||
|
|
|
|||
|
|
create_page("apphooked-page", "nav_playground.html", "en", navigation_extenders="StaticMenu")
|
|||
|
|
|
|||
|
|
create_page("apphooked-page", "nav_playground.html", "en", navigation_extenders="StaticMenu2")
|
|||
|
|
|
|||
|
|
request_2 = self.get_request("/en/")
|
|||
|
|
request_2_renderer = menu_pool.get_renderer(request_2)
|
|||
|
|
self.assertEqual(len(request_2_renderer.menus), 3)
|
|||
|
|
|
|||
|
|
def test_menu_expanded(self):
|
|||
|
|
menu_pool.discovered = False
|
|||
|
|
menu_pool.discover_menus()
|
|||
|
|
|
|||
|
|
with self.settings(ROOT_URLCONF="cms.test_utils.project.urls_for_apphook_tests"):
|
|||
|
|
with apphooks(SampleApp):
|
|||
|
|
page = create_page(
|
|||
|
|
"apphooked-page",
|
|||
|
|
"nav_playground.html",
|
|||
|
|
"en",
|
|||
|
|
apphook="SampleApp",
|
|||
|
|
navigation_extenders="StaticMenu",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
self.assertTrue(menu_pool.discovered)
|
|||
|
|
|
|||
|
|
menus = menu_pool.get_registered_menus()
|
|||
|
|
|
|||
|
|
self.assertTrue(menu_pool.discovered)
|
|||
|
|
# Counts the number of StaticMenu (which is expanded) and StaticMenu2
|
|||
|
|
# (which is not) and checks the key name for the StaticMenu instances
|
|||
|
|
static_menus = 1
|
|||
|
|
static_menus_2 = 1
|
|||
|
|
for key, menu in menus.items():
|
|||
|
|
if key.startswith("StaticMenu:"):
|
|||
|
|
static_menus -= 1
|
|||
|
|
self.assertTrue(key.endswith(str(page.pk)) or key.endswith(str(page.pk)))
|
|||
|
|
|
|||
|
|
if key == "StaticMenu2":
|
|||
|
|
static_menus_2 -= 1
|
|||
|
|
|
|||
|
|
self.assertEqual(static_menus, 0)
|
|||
|
|
self.assertEqual(static_menus_2, 0)
|
|||
|
|
|
|||
|
|
def test_multiple_menus(self):
|
|||
|
|
with self.settings(ROOT_URLCONF="cms.test_utils.project.urls_for_apphook_tests"):
|
|||
|
|
with apphooks(NamespacedApp, SampleApp2):
|
|||
|
|
apphook_pool.discovered = False
|
|||
|
|
apphook_pool.discover_apps()
|
|||
|
|
create_page("apphooked-page", "nav_playground.html", "en", apphook="SampleApp2")
|
|||
|
|
create_page("apphooked-page", "nav_playground.html", "en", navigation_extenders="StaticMenu")
|
|||
|
|
create_page(
|
|||
|
|
"apphooked-page",
|
|||
|
|
"nav_playground.html",
|
|||
|
|
"en",
|
|||
|
|
apphook="NamespacedApp",
|
|||
|
|
apphook_namespace="whatever",
|
|||
|
|
navigation_extenders="StaticMenu",
|
|||
|
|
)
|
|||
|
|
self.assertEqual(len(menu_pool.get_menus_by_attribute("cms_enabled", True)), 2)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ExtendedFixturesMenuTests(ExtendedMenusFixture, BaseMenuTest):
|
|||
|
|
"""
|
|||
|
|
Tree from fixture:
|
|||
|
|
|
|||
|
|
+ P1
|
|||
|
|
| + P2
|
|||
|
|
| + P3
|
|||
|
|
| + P9
|
|||
|
|
| + P10
|
|||
|
|
| + P11
|
|||
|
|
+ P4
|
|||
|
|
| + P5
|
|||
|
|
+ P6 (not in menu)
|
|||
|
|
+ P7
|
|||
|
|
+ P8
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def get_page(self, num):
|
|||
|
|
return Page.objects.get(pagecontent_set__title="P%s" % num)
|
|||
|
|
|
|||
|
|
def get_all_pages(self):
|
|||
|
|
return Page.objects.all()
|
|||
|
|
|
|||
|
|
def test_menu_failfast_on_invalid_usage(self):
|
|||
|
|
context = self.get_context()
|
|||
|
|
context["child"] = self.get_page(1)
|
|||
|
|
# test standard show_menu
|
|||
|
|
with self.settings(DEBUG=True, TEMPLATE_DEBUG=True):
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 0 0 0 'menu/menu.html' child %}")
|
|||
|
|
self.assertRaises(TemplateSyntaxError, tpl.render, context)
|
|||
|
|
|
|||
|
|
def test_show_menu_cut_inactive(self):
|
|||
|
|
root = self.get_page(2)
|
|||
|
|
context = self.get_context(page=root)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 1 100 0 1 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes), 2)
|
|||
|
|
self.assertEqual(len(nodes[0].children), 1)
|
|||
|
|
self.assertEqual(len(nodes[1].children), 0)
|
|||
|
|
|
|||
|
|
def test_show_submenu_nephews(self):
|
|||
|
|
page_2 = self.get_page(2)
|
|||
|
|
context = self.get_context(path=page_2.get_absolute_url(), page=page_2)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_sub_menu 100 1 1 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
# P2 is the selected node
|
|||
|
|
self.assertTrue(nodes[0].selected)
|
|||
|
|
# Should include P10 but not P11
|
|||
|
|
self.assertEqual(len(nodes[1].children), 1)
|
|||
|
|
self.assertFalse(nodes[1].children[0].children)
|
|||
|
|
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_sub_menu 100 1 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
# should now include both P10 and P11
|
|||
|
|
self.assertEqual(len(nodes[1].children), 1)
|
|||
|
|
self.assertEqual(len(nodes[1].children[0].children), 1)
|
|||
|
|
|
|||
|
|
def test_show_submenu_template_root_level_none_no_nephew_limit(self):
|
|||
|
|
root = self.get_page(1)
|
|||
|
|
context = self.get_context(path=root.get_absolute_url(), page=root)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_sub_menu 100 None 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
# default nephew limit, P2 and P9 in the nodes list
|
|||
|
|
self.assertEqual(len(nodes), 2)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class FixturesMenuTests(MenusFixture, BaseMenuTest):
|
|||
|
|
"""
|
|||
|
|
Tree from fixture:
|
|||
|
|
|
|||
|
|
+ P1
|
|||
|
|
| + P2
|
|||
|
|
| + P3
|
|||
|
|
+ P4
|
|||
|
|
| + P5
|
|||
|
|
+ P6 (not in menu)
|
|||
|
|
+ P7
|
|||
|
|
+ P8
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def get_page(self, num):
|
|||
|
|
return Page.objects.get(pagecontent_set__title="P%s" % num)
|
|||
|
|
|
|||
|
|
def get_all_pages(self):
|
|||
|
|
return Page.objects.all()
|
|||
|
|
|
|||
|
|
def test_menu_failfast_on_invalid_usage(self):
|
|||
|
|
context = self.get_context()
|
|||
|
|
context["child"] = self.get_page(1)
|
|||
|
|
# test standard show_menu
|
|||
|
|
with self.settings(DEBUG=True, TEMPLATE_DEBUG=True):
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 0 0 0 'menu/menu.html' child %}")
|
|||
|
|
self.assertRaises(TemplateSyntaxError, tpl.render, context)
|
|||
|
|
|
|||
|
|
def test_basic_cms_menu(self):
|
|||
|
|
menus = menu_pool.get_registered_menus()
|
|||
|
|
self.assertEqual(len(menus), 1)
|
|||
|
|
with force_language("en"):
|
|||
|
|
response = self.client.get(self.get_pages_root()) # path = '/'
|
|||
|
|
self.assertEqual(response.status_code, 200)
|
|||
|
|
request = self.get_request()
|
|||
|
|
|
|||
|
|
renderer = menu_pool.get_renderer(request)
|
|||
|
|
|
|||
|
|
# test the cms menu class
|
|||
|
|
menu = renderer.get_menu("CMSMenu")
|
|||
|
|
nodes = menu.get_nodes(request)
|
|||
|
|
pages = self.get_all_pages().order_by("path")
|
|||
|
|
self.assertEqual(len(nodes), len(pages))
|
|||
|
|
self.assertSequenceEqual(
|
|||
|
|
[node.get_absolute_url() for node in nodes],
|
|||
|
|
[page.get_absolute_url() for page in pages],
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
def test_show_page_in_menu_after_move_page(self):
|
|||
|
|
"""
|
|||
|
|
Test checks if the menu cache is cleaned after move page.
|
|||
|
|
"""
|
|||
|
|
page = create_page("page to move", "nav_playground.html", "en")
|
|||
|
|
|
|||
|
|
request = self.get_request("/")
|
|||
|
|
renderer = menu_pool.get_renderer(request)
|
|||
|
|
renderer.draft_mode_active = True
|
|||
|
|
nodes_before = renderer.get_nodes()
|
|||
|
|
index_before = [i for i, s in enumerate(nodes_before) if s.title == page.get_title()]
|
|||
|
|
self.assertEqual(CacheKey.objects.count(), 1)
|
|||
|
|
|
|||
|
|
with self.login_user_context(self.get_superuser()):
|
|||
|
|
# Moves the page to the second position in the tree
|
|||
|
|
data = {"id": page.pk, "position": 1}
|
|||
|
|
endpoint = self.get_admin_url(Page, "move_page", page.pk)
|
|||
|
|
response = self.client.post(endpoint, data)
|
|||
|
|
self.assertEqual(response.status_code, 200)
|
|||
|
|
self.assertEqual(CacheKey.objects.count(), 0)
|
|||
|
|
|
|||
|
|
request = self.get_request("/")
|
|||
|
|
renderer = menu_pool.get_renderer(request)
|
|||
|
|
renderer.draft_mode_active = True
|
|||
|
|
nodes_after = renderer.get_nodes()
|
|||
|
|
index_after = [i for i, s in enumerate(nodes_after) if s.title == page.get_title()]
|
|||
|
|
|
|||
|
|
self.assertEqual(CacheKey.objects.count(), 1)
|
|||
|
|
self.assertNotEqual(index_before, index_after, "Index should not be the same after move page in navigation")
|
|||
|
|
|
|||
|
|
def test_cms_menu_public_with_multiple_languages(self):
|
|||
|
|
pages = self.get_all_pages().order_by("path")
|
|||
|
|
|
|||
|
|
# Fallbacks on
|
|||
|
|
request = self.get_request(path="/de/", language="de")
|
|||
|
|
renderer = menu_pool.get_renderer(request)
|
|||
|
|
menu = renderer.get_menu("CMSMenu")
|
|||
|
|
nodes = menu.get_nodes(request)
|
|||
|
|
self.assertEqual(len(nodes), len(pages))
|
|||
|
|
|
|||
|
|
with force_language("de"):
|
|||
|
|
# Current language is "de" but urls should still point
|
|||
|
|
# to "en" because of fallbacks.
|
|||
|
|
self.assertSequenceEqual(
|
|||
|
|
[node.get_absolute_url() for node in nodes],
|
|||
|
|
[page.get_absolute_url("en", fallback=False) for page in pages],
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Fallbacks off
|
|||
|
|
request = self.get_request(path="/de/", language="de")
|
|||
|
|
lang_settings = copy.deepcopy(get_cms_setting("LANGUAGES"))
|
|||
|
|
lang_settings[1][1]["hide_untranslated"] = True
|
|||
|
|
|
|||
|
|
with self.settings(CMS_LANGUAGES=lang_settings):
|
|||
|
|
renderer = menu_pool.get_renderer(request)
|
|||
|
|
menu = renderer.get_menu("CMSMenu")
|
|||
|
|
nodes = menu.get_nodes(request)
|
|||
|
|
self.assertEqual(len(nodes), 0)
|
|||
|
|
|
|||
|
|
for page in Page.objects.all():
|
|||
|
|
create_page_content(
|
|||
|
|
language="de", title=page.get_title("en"), page=page, slug="{}-de".format(page.get_slug("en"))
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Fallbacks on
|
|||
|
|
# This time however, the "de" translations exist.
|
|||
|
|
request = self.get_request(path="/de/", language="de")
|
|||
|
|
renderer = menu_pool.get_renderer(request)
|
|||
|
|
menu = renderer.get_menu("CMSMenu")
|
|||
|
|
nodes = menu.get_nodes(request)
|
|||
|
|
self.assertEqual(len(nodes), len(pages))
|
|||
|
|
|
|||
|
|
with force_language("de"):
|
|||
|
|
self.assertSequenceEqual(
|
|||
|
|
[node.get_absolute_url() for node in nodes],
|
|||
|
|
[page.get_absolute_url("de", fallback=False) for page in pages],
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Fallbacks off
|
|||
|
|
request = self.get_request(path="/de/", language="de")
|
|||
|
|
|
|||
|
|
with self.settings(CMS_LANGUAGES=lang_settings):
|
|||
|
|
renderer = menu_pool.get_renderer(request)
|
|||
|
|
menu = renderer.get_menu("CMSMenu")
|
|||
|
|
nodes = menu.get_nodes(request)
|
|||
|
|
|
|||
|
|
with force_language("de"):
|
|||
|
|
self.assertSequenceEqual(
|
|||
|
|
[node.get_absolute_url() for node in nodes],
|
|||
|
|
[page.get_absolute_url("de", fallback=False) for page in pages],
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
def test_show_menu(self):
|
|||
|
|
root = self.get_page(1)
|
|||
|
|
context = self.get_context(page=root)
|
|||
|
|
# test standard show_menu
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes), 2)
|
|||
|
|
self.assertEqual(nodes[0].selected, True)
|
|||
|
|
self.assertEqual(nodes[0].sibling, False)
|
|||
|
|
self.assertEqual(nodes[0].descendant, False)
|
|||
|
|
self.assertEqual(nodes[0].children[0].descendant, True)
|
|||
|
|
self.assertEqual(nodes[0].children[0].children[0].descendant, True)
|
|||
|
|
self.assertEqual(nodes[0].get_absolute_url(), self.get_pages_root())
|
|||
|
|
self.assertEqual(nodes[1].get_absolute_url(), self.get_page(4).get_absolute_url())
|
|||
|
|
self.assertEqual(nodes[1].sibling, True)
|
|||
|
|
self.assertEqual(nodes[1].selected, False)
|
|||
|
|
|
|||
|
|
def test_show_menu_num_queries(self):
|
|||
|
|
context = self.get_context()
|
|||
|
|
# test standard show_menu
|
|||
|
|
with self.assertNumQueries(5):
|
|||
|
|
"""
|
|||
|
|
The queries should be:
|
|||
|
|
get all page contents
|
|||
|
|
get all page permissions
|
|||
|
|
get all page urls
|
|||
|
|
get the menu cache key
|
|||
|
|
set the menu cache key
|
|||
|
|
"""
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
|
|||
|
|
def test_show__key_leak(self):
|
|||
|
|
context = self.get_context()
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu %}")
|
|||
|
|
self.assertEqual(CacheKey.objects.count(), 0)
|
|||
|
|
tpl.render(context)
|
|||
|
|
self.assertEqual(CacheKey.objects.count(), 1)
|
|||
|
|
tpl.render(context)
|
|||
|
|
self.assertEqual(CacheKey.objects.count(), 1)
|
|||
|
|
|
|||
|
|
def test_menu_cache_respects_database_keys(self):
|
|||
|
|
cms_page = self.get_page(1)
|
|||
|
|
context = self.get_context(path=cms_page.get_absolute_url(), page=cms_page)
|
|||
|
|
context["request"].session["cms_edit"] = False
|
|||
|
|
|
|||
|
|
# Prime the cache
|
|||
|
|
with self.assertNumQueries(5):
|
|||
|
|
# The queries should be:
|
|||
|
|
# get all page contents
|
|||
|
|
# get all page permissions
|
|||
|
|
# get all page urls
|
|||
|
|
# get the menu cache key
|
|||
|
|
# set the menu cache key
|
|||
|
|
Template("{% load menu_tags %}{% show_menu %}").render(context)
|
|||
|
|
|
|||
|
|
# One new CacheKey should have been created
|
|||
|
|
self.assertEqual(CacheKey.objects.count(), 1)
|
|||
|
|
|
|||
|
|
# Because its cached, only one query is made to the db
|
|||
|
|
with self.assertNumQueries(1):
|
|||
|
|
# The queries should be:
|
|||
|
|
# get the menu cache key
|
|||
|
|
Template("{% load menu_tags %}{% show_menu %}").render(context)
|
|||
|
|
|
|||
|
|
# Delete the current cache key but don't touch the cache
|
|||
|
|
CacheKey.objects.all().delete()
|
|||
|
|
|
|||
|
|
# The menu should be recalculated
|
|||
|
|
with self.assertNumQueries(5):
|
|||
|
|
# The queries should be:
|
|||
|
|
# check if cache key exists
|
|||
|
|
# get all page contents
|
|||
|
|
# get all page permissions
|
|||
|
|
# get all page url objects
|
|||
|
|
# set the menu cache key
|
|||
|
|
Template("{% load menu_tags %}{% show_menu %}").render(context)
|
|||
|
|
|
|||
|
|
def test_menu_cache_default_is_default_cache(self):
|
|||
|
|
from cms.utils.conf import get_menu_cache
|
|||
|
|
|
|||
|
|
menu_cache = get_menu_cache() # Re-read the patched settings
|
|||
|
|
menu_cache.get("something") # Resolve lazy cache
|
|||
|
|
|
|||
|
|
self.assertEqual(menu_cache, caches["default"], "Menu cache is not \"default\"")
|
|||
|
|
|
|||
|
|
@override_settings(
|
|||
|
|
CMS_MENU_CACHE_BACKEND="secondary",
|
|||
|
|
CACHES={
|
|||
|
|
"default": {
|
|||
|
|
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
|
|||
|
|
"LOCATION": "menu-test-default",
|
|||
|
|
},
|
|||
|
|
"secondary": {
|
|||
|
|
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
|
|||
|
|
"LOCATION": "menu-test-secondary",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
)
|
|||
|
|
def test_menu_cache_uses_configured_backend(self):
|
|||
|
|
from cms.utils.conf import get_menu_cache
|
|||
|
|
|
|||
|
|
menu_cache = get_menu_cache() # Re-read the patched settings
|
|||
|
|
menu_cache.get("something") # Resolve lazy cache
|
|||
|
|
|
|||
|
|
self.assertEqual(menu_cache, caches["secondary"], "Menu cache ignores CMS_MENU_CACHE_BACKEND setting")
|
|||
|
|
|
|||
|
|
def test_menu_keys_duplicate_clear(self):
|
|||
|
|
"""
|
|||
|
|
Tests that the menu clears all keys, including duplicates.
|
|||
|
|
"""
|
|||
|
|
CacheKey.objects.create(language="fr", site=1, key="a")
|
|||
|
|
CacheKey.objects.create(language="fr", site=1, key="a")
|
|||
|
|
|
|||
|
|
self.assertEqual(CacheKey.objects.count(), 2)
|
|||
|
|
menu_pool.clear(site_id=1, language="fr")
|
|||
|
|
self.assertEqual(CacheKey.objects.count(), 0)
|
|||
|
|
|
|||
|
|
def test_only_active_tree(self):
|
|||
|
|
context = self.get_context(page=self.get_page(1))
|
|||
|
|
# test standard show_menu
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 0 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
|
|||
|
|
# Expectation
|
|||
|
|
# + P1 (selected)
|
|||
|
|
# | + P2
|
|||
|
|
# | + P3
|
|||
|
|
# + P4
|
|||
|
|
|
|||
|
|
self.assertEqual(len(nodes), 2)
|
|||
|
|
self.assertTrue(nodes[0].selected)
|
|||
|
|
self.assertEqual(len(nodes[0].children), 1)
|
|||
|
|
self.assertEqual(len(nodes[1].children), 0)
|
|||
|
|
self.assertEqual(len(nodes[0].children[0].children), 1)
|
|||
|
|
|
|||
|
|
page_4 = self.get_page(4)
|
|||
|
|
context = self.get_context(path=page_4.get_absolute_url(), page=page_4)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 0 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
|
|||
|
|
# Expectation
|
|||
|
|
# + P1
|
|||
|
|
# + P4 (selected)
|
|||
|
|
# + P5
|
|||
|
|
|
|||
|
|
self.assertEqual(len(nodes), 2)
|
|||
|
|
self.assertEqual(len(nodes[0].children), 0)
|
|||
|
|
self.assertTrue(nodes[1].selected)
|
|||
|
|
self.assertEqual(len(nodes[1].children), 1)
|
|||
|
|
self.assertEqual(nodes[1].children[0].children, [])
|
|||
|
|
|
|||
|
|
def test_only_one_active_level(self):
|
|||
|
|
context = self.get_context(page=self.get_page(1))
|
|||
|
|
# test standard show_menu
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 0 1 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes[1].children), 0)
|
|||
|
|
self.assertEqual(len(nodes[0].children), 1)
|
|||
|
|
self.assertEqual(len(nodes[0].children[0].children), 0)
|
|||
|
|
|
|||
|
|
def test_only_level_zero(self):
|
|||
|
|
context = self.get_context()
|
|||
|
|
# test standard show_menu
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 0 0 0 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
for node in nodes:
|
|||
|
|
self.assertEqual(len(node.children), 0)
|
|||
|
|
|
|||
|
|
def test_only_level_one(self):
|
|||
|
|
site = Site.objects.get_current()
|
|||
|
|
context = self.get_context()
|
|||
|
|
# test standard show_menu
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 1 1 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
level_2_pages = Page.objects.filter(depth=2, site=site)
|
|||
|
|
self.assertEqual(len(nodes), level_2_pages.count())
|
|||
|
|
for node in nodes:
|
|||
|
|
self.assertEqual(len(node.children), 0)
|
|||
|
|
|
|||
|
|
def test_only_level_one_active(self):
|
|||
|
|
context = self.get_context(page=self.get_page(1))
|
|||
|
|
# test standard show_menu
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 1 1 0 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
|
|||
|
|
# Expectation
|
|||
|
|
# + P2
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
self.assertEqual(nodes[0].descendant, True)
|
|||
|
|
self.assertEqual(len(nodes[0].children), 0)
|
|||
|
|
|
|||
|
|
def test_only_level_one_active_and_inacgtive(self):
|
|||
|
|
context = self.get_context(page=self.get_page(1))
|
|||
|
|
# test standard show_menu
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 1 1 1 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
|
|||
|
|
# Expectation
|
|||
|
|
# + P2
|
|||
|
|
# + P5
|
|||
|
|
# + P7 (despite the fact that the parent is not visible)
|
|||
|
|
# + P8 (despite the fact that the parent is not visible)
|
|||
|
|
|
|||
|
|
self.assertEqual(len(nodes), 4)
|
|||
|
|
self.assertEqual(nodes[0].descendant, True)
|
|||
|
|
self.assertEqual(nodes[1].descendant, False)
|
|||
|
|
for node in nodes:
|
|||
|
|
self.assertEqual(len(node.children), 0)
|
|||
|
|
|
|||
|
|
def test_level_zero_and_one(self):
|
|||
|
|
context = self.get_context()
|
|||
|
|
# test standard show_menu
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 1 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
|
|||
|
|
# Expectation
|
|||
|
|
# + P1
|
|||
|
|
# | + P2
|
|||
|
|
# + P4
|
|||
|
|
# + P5
|
|||
|
|
|
|||
|
|
self.assertEqual(len(nodes), 2)
|
|||
|
|
for node in nodes:
|
|||
|
|
self.assertEqual(len(node.children), 1)
|
|||
|
|
|
|||
|
|
def test_one_inactive(self):
|
|||
|
|
context = self.get_context()
|
|||
|
|
# test standard show_menu
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 1 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
|
|||
|
|
# Expectation
|
|||
|
|
# + P1
|
|||
|
|
# | + P2
|
|||
|
|
# + P4
|
|||
|
|
# + P5
|
|||
|
|
|
|||
|
|
self.assertEqual(len(nodes), 2)
|
|||
|
|
for node in nodes:
|
|||
|
|
self.assertEqual(len(node.children), 1)
|
|||
|
|
self.assertEqual(len(node.children[0].children), 0)
|
|||
|
|
|
|||
|
|
def test_show_submenu(self):
|
|||
|
|
context = self.get_context(page=self.get_page(1))
|
|||
|
|
# test standard show_menu
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_sub_menu %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(nodes[0].descendant, True)
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
self.assertEqual(len(nodes[0].children), 1)
|
|||
|
|
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_sub_menu 1 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
self.assertEqual(len(nodes[0].children), 0)
|
|||
|
|
|
|||
|
|
page_3 = self.get_page(3)
|
|||
|
|
context = self.get_context(path=page_3.get_absolute_url(), page=page_3)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_sub_menu 100 1 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
# P3 is the selected node
|
|||
|
|
self.assertFalse(nodes[0].selected)
|
|||
|
|
self.assertTrue(nodes[0].children[0].selected)
|
|||
|
|
# top level node should be P2
|
|||
|
|
self.assertEqual(nodes[0].get_absolute_url(), self.get_page(2).get_absolute_url())
|
|||
|
|
# should include P3 as well
|
|||
|
|
self.assertEqual(len(nodes[0].children), 1)
|
|||
|
|
|
|||
|
|
page_2 = self.get_page(2)
|
|||
|
|
context = self.get_context(path=page_2.get_absolute_url(), page=page_2)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_sub_menu 100 0 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
# P1 should be in the nav
|
|||
|
|
self.assertEqual(nodes[0].get_absolute_url(), self.get_page(1).get_absolute_url())
|
|||
|
|
# P2 is selected
|
|||
|
|
self.assertTrue(nodes[0].children[0].selected)
|
|||
|
|
|
|||
|
|
def test_show_submenu_template_root_level_none(self):
|
|||
|
|
root = self.get_page(1)
|
|||
|
|
context = self.get_context(path=root.get_absolute_url(), page=root)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_sub_menu 100 None 1 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
# First node is P2 (P1 children) thus not selected
|
|||
|
|
self.assertFalse(nodes[0].selected)
|
|||
|
|
# nephew limit of 1, so only P2 is the nodes list
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
# P3 is a child of P2, but not in nodes list
|
|||
|
|
self.assertTrue(nodes[0].children)
|
|||
|
|
|
|||
|
|
def test_show_breadcrumb(self):
|
|||
|
|
page_3 = self.get_page(3)
|
|||
|
|
context = self.get_context(path=self.get_page(3).get_absolute_url(), page=page_3)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_breadcrumb %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["ancestors"]
|
|||
|
|
self.assertEqual(len(nodes), 3)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_breadcrumb 1 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["ancestors"]
|
|||
|
|
self.assertEqual(len(nodes), 2)
|
|||
|
|
context = self.get_context()
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_breadcrumb %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["ancestors"]
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_breadcrumb 1 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["ancestors"]
|
|||
|
|
self.assertEqual(len(nodes), 0)
|
|||
|
|
|
|||
|
|
page1 = self.get_page(1)
|
|||
|
|
page1.in_navigation = False
|
|||
|
|
page1.save()
|
|||
|
|
page2 = self.get_page(2)
|
|||
|
|
context = self.get_context(path=page2.get_absolute_url(), page=page2)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_breadcrumb %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["ancestors"]
|
|||
|
|
self.assertEqual(len(nodes), 2)
|
|||
|
|
self.assertEqual(nodes[0].get_absolute_url(), self.get_pages_root())
|
|||
|
|
self.assertEqual(isinstance(nodes[0], NavigationNode), True)
|
|||
|
|
self.assertEqual(nodes[1].get_absolute_url(), page2.get_absolute_url())
|
|||
|
|
|
|||
|
|
def test_language_chooser(self):
|
|||
|
|
# test simple language chooser with default args
|
|||
|
|
lang_settings = copy.deepcopy(get_cms_setting("LANGUAGES"))
|
|||
|
|
lang_settings[1][0]["public"] = False
|
|||
|
|
with self.settings(CMS_LANGUAGES=lang_settings):
|
|||
|
|
context = self.get_context(path=self.get_page(3).get_absolute_url())
|
|||
|
|
tpl = Template("{% load menu_tags %}{% language_chooser %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
self.assertEqual(len(context["languages"]), 3)
|
|||
|
|
# try a different template and some different args
|
|||
|
|
tpl = Template("{% load menu_tags %}{% language_chooser 'menu/test_language_chooser.html' %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
self.assertEqual(context["template"], "menu/test_language_chooser.html")
|
|||
|
|
tpl = Template("{% load menu_tags %}{% language_chooser 'short' 'menu/test_language_chooser.html' %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
self.assertEqual(context["template"], "menu/test_language_chooser.html")
|
|||
|
|
for lang in context["languages"]:
|
|||
|
|
self.assertEqual(*lang)
|
|||
|
|
|
|||
|
|
def test_language_chooser_all_for_staff(self):
|
|||
|
|
"""
|
|||
|
|
Language chooser should show all configured languages
|
|||
|
|
on the current site if the user is staff.
|
|||
|
|
"""
|
|||
|
|
superuser = self.get_superuser()
|
|||
|
|
lang_settings = copy.deepcopy(get_cms_setting("LANGUAGES"))
|
|||
|
|
# DE is marked as public False
|
|||
|
|
lang_settings[1][1]["public"] = False
|
|||
|
|
# FR is marked as public False
|
|||
|
|
lang_settings[1][2]["public"] = False
|
|||
|
|
|
|||
|
|
with self.settings(CMS_LANGUAGES=lang_settings):
|
|||
|
|
with self.login_user_context(superuser):
|
|||
|
|
context = self.get_context(path=self.get_page(3).get_absolute_url())
|
|||
|
|
Template("{% load menu_tags %}{% language_chooser %}").render(context)
|
|||
|
|
self.assertEqual(len(context["languages"]), 5)
|
|||
|
|
self.assertSequenceEqual(
|
|||
|
|
sorted(lang[0] for lang in context["languages"]), ["de", "en", "es-mx", "fr", "pt-br"]
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
def test_language_chooser_public_for_anon(self):
|
|||
|
|
"""
|
|||
|
|
Language chooser should only show public configured languages
|
|||
|
|
on the current site if the user is anon.
|
|||
|
|
"""
|
|||
|
|
# PT-BR is already set to public False
|
|||
|
|
lang_settings = copy.deepcopy(get_cms_setting("LANGUAGES"))
|
|||
|
|
# DE is marked as public False
|
|||
|
|
lang_settings[1][1]["public"] = False
|
|||
|
|
# FR is marked as public False
|
|||
|
|
lang_settings[1][2]["public"] = False
|
|||
|
|
|
|||
|
|
with self.settings(CMS_LANGUAGES=lang_settings):
|
|||
|
|
context = self.get_context(path=self.get_page(3).get_absolute_url())
|
|||
|
|
Template("{% load menu_tags %}{% language_chooser %}").render(context)
|
|||
|
|
self.assertEqual(len(context["languages"]), 2)
|
|||
|
|
self.assertSequenceEqual(sorted(lang[0] for lang in context["languages"]), ["en", "es-mx"])
|
|||
|
|
|
|||
|
|
def test_page_language_url(self):
|
|||
|
|
path = self.get_page(3).get_absolute_url()
|
|||
|
|
context = self.get_context(path=path)
|
|||
|
|
tpl = Template("{%% load menu_tags %%}{%% page_language_url '%s' %%}" % "en")
|
|||
|
|
url = tpl.render(context)
|
|||
|
|
self.assertEqual(url, "%s" % path)
|
|||
|
|
|
|||
|
|
def test_show_menu_below_id(self):
|
|||
|
|
page2 = self.get_page(2)
|
|||
|
|
page2.reverse_id = "hello"
|
|||
|
|
page2.save()
|
|||
|
|
page2 = self.reload(page2)
|
|||
|
|
self.assertEqual(page2.reverse_id, "hello")
|
|||
|
|
page5 = self.get_page(5)
|
|||
|
|
context = self.get_context(path=page5.get_absolute_url())
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu_below_id 'hello' %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
page3_url = self.get_page(3).get_absolute_url()
|
|||
|
|
self.assertEqual(nodes[0].get_absolute_url(), page3_url)
|
|||
|
|
page2.in_navigation = False
|
|||
|
|
page2.save()
|
|||
|
|
context = self.get_context(path=page5.get_absolute_url())
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu_below_id 'hello' %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
self.assertEqual(nodes[0].get_absolute_url(), page3_url)
|
|||
|
|
|
|||
|
|
def test_home_not_in_menu(self):
|
|||
|
|
page1 = self.get_page(1)
|
|||
|
|
page1.update_translations(in_navigation=False)
|
|||
|
|
page1.save()
|
|||
|
|
page4 = self.get_page(4)
|
|||
|
|
page4.update_translations(in_navigation=False)
|
|||
|
|
page4.save()
|
|||
|
|
context = self.get_context()
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
self.assertEqual(nodes[0].get_absolute_url(), self.get_page(2).get_absolute_url())
|
|||
|
|
self.assertEqual(nodes[0].children[0].get_absolute_url(), self.get_page(3).get_absolute_url())
|
|||
|
|
page4 = self.get_page(4)
|
|||
|
|
page4.update_translations(in_navigation=True)
|
|||
|
|
page4.save()
|
|||
|
|
menu_pool.clear(Site.objects.get_current().pk)
|
|||
|
|
context = self.get_context()
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes), 2)
|
|||
|
|
|
|||
|
|
def test_show_submenu_from_non_menu_page(self):
|
|||
|
|
"""
|
|||
|
|
Here's the structure bit we're interested in:
|
|||
|
|
|
|||
|
|
+ P6 (not in menu)
|
|||
|
|
+ P7
|
|||
|
|
+ P8
|
|||
|
|
|
|||
|
|
When we render P6, there should be a menu entry for P7 and P8 if the
|
|||
|
|
tag parameters are "1 XXX XXX XXX"
|
|||
|
|
"""
|
|||
|
|
page6 = self.get_page(6)
|
|||
|
|
context = self.get_context(page6.get_absolute_url(), page=page6)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 1 100 0 1 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
number_of_p6_children = (
|
|||
|
|
page6.get_child_pages().filter(pagecontent_set__language="en", pagecontent_set__in_navigation=True).count()
|
|||
|
|
)
|
|||
|
|
self.assertEqual(len(nodes), number_of_p6_children)
|
|||
|
|
|
|||
|
|
page7 = self.get_page(7)
|
|||
|
|
context = self.get_context(page7.get_absolute_url(), page=page7)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 1 100 0 1 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes), number_of_p6_children)
|
|||
|
|
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 2 100 0 1 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
number_of_p7_children = (
|
|||
|
|
page7.get_child_pages().filter(pagecontent_set__language="en", pagecontent_set__in_navigation=True).count()
|
|||
|
|
)
|
|||
|
|
self.assertEqual(len(nodes), number_of_p7_children)
|
|||
|
|
|
|||
|
|
def test_show_breadcrumb_invisible(self):
|
|||
|
|
parent = Page.objects.get(pagecontent_set__title="P3")
|
|||
|
|
invisible_page = create_page("invisible", "nav_playground.html", "en", parent=parent, in_navigation=False)
|
|||
|
|
context = self.get_context(
|
|||
|
|
path=invisible_page.get_absolute_url(),
|
|||
|
|
page=invisible_page,
|
|||
|
|
)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_breadcrumb %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["ancestors"]
|
|||
|
|
self.assertEqual(len(nodes), 3)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_breadcrumb 0 'menu/breadcrumb.html' 1 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["ancestors"]
|
|||
|
|
self.assertEqual(len(nodes), 3)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_breadcrumb 0 'menu/breadcrumb.html' 0 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["ancestors"]
|
|||
|
|
self.assertEqual(len(nodes), 4)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class MenuTests(BaseMenuTest):
|
|||
|
|
def test_build_nodes_inner_for_worst_case_menu(self):
|
|||
|
|
"""
|
|||
|
|
Tests the worst case scenario
|
|||
|
|
|
|||
|
|
node5
|
|||
|
|
node4
|
|||
|
|
node3
|
|||
|
|
node2
|
|||
|
|
node1
|
|||
|
|
"""
|
|||
|
|
node1 = NavigationNode("Test1", "/test1/", 1, 2)
|
|||
|
|
node2 = NavigationNode("Test2", "/test2/", 2, 3)
|
|||
|
|
node3 = NavigationNode("Test3", "/test3/", 3, 4)
|
|||
|
|
node4 = NavigationNode("Test4", "/test4/", 4, 5)
|
|||
|
|
node5 = NavigationNode("Test5", "/test5/", 5, None)
|
|||
|
|
|
|||
|
|
menu_class_name = "Test"
|
|||
|
|
nodes = [
|
|||
|
|
node1,
|
|||
|
|
node2,
|
|||
|
|
node3,
|
|||
|
|
node4,
|
|||
|
|
node5,
|
|||
|
|
]
|
|||
|
|
len_nodes = len(nodes)
|
|||
|
|
|
|||
|
|
final_list = _build_nodes_inner_for_one_menu(nodes, menu_class_name)
|
|||
|
|
self.assertEqual(len(final_list), len_nodes)
|
|||
|
|
|
|||
|
|
self.assertEqual(node1.parent, node2)
|
|||
|
|
self.assertEqual(node2.parent, node3)
|
|||
|
|
self.assertEqual(node3.parent, node4)
|
|||
|
|
self.assertEqual(node4.parent, node5)
|
|||
|
|
self.assertEqual(node5.parent, None)
|
|||
|
|
|
|||
|
|
self.assertEqual(node1.children, [])
|
|||
|
|
self.assertEqual(node2.children, [node1])
|
|||
|
|
self.assertEqual(node3.children, [node2])
|
|||
|
|
self.assertEqual(node4.children, [node3])
|
|||
|
|
self.assertEqual(node5.children, [node4])
|
|||
|
|
|
|||
|
|
def test_build_nodes_inner_for_circular_menu(self):
|
|||
|
|
"""
|
|||
|
|
TODO:
|
|||
|
|
To properly handle this test we need to have a circular dependency
|
|||
|
|
detection system.
|
|||
|
|
Go nuts implementing it :)
|
|||
|
|
"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
def test_build_nodes_inner_for_broken_menu(self):
|
|||
|
|
"""
|
|||
|
|
Tests a broken menu tree (non-existing parent)
|
|||
|
|
|
|||
|
|
node5
|
|||
|
|
node4
|
|||
|
|
node3
|
|||
|
|
|
|||
|
|
<non-existent>
|
|||
|
|
node2
|
|||
|
|
node1
|
|||
|
|
"""
|
|||
|
|
node1 = NavigationNode("Test1", "/test1/", 1, 2)
|
|||
|
|
node2 = NavigationNode("Test2", "/test2/", 2, 12)
|
|||
|
|
node3 = NavigationNode("Test3", "/test3/", 3, 4)
|
|||
|
|
node4 = NavigationNode("Test4", "/test4/", 4, 5)
|
|||
|
|
node5 = NavigationNode("Test5", "/test5/", 5, None)
|
|||
|
|
|
|||
|
|
menu_class_name = "Test"
|
|||
|
|
nodes = [
|
|||
|
|
node1,
|
|||
|
|
node2,
|
|||
|
|
node3,
|
|||
|
|
node4,
|
|||
|
|
node5,
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
final_list = _build_nodes_inner_for_one_menu(nodes, menu_class_name)
|
|||
|
|
self.assertEqual(len(final_list), 3)
|
|||
|
|
self.assertFalse(node1 in final_list)
|
|||
|
|
self.assertFalse(node2 in final_list)
|
|||
|
|
|
|||
|
|
self.assertEqual(node1.parent, None)
|
|||
|
|
self.assertEqual(node2.parent, None)
|
|||
|
|
self.assertEqual(node3.parent, node4)
|
|||
|
|
self.assertEqual(node4.parent, node5)
|
|||
|
|
self.assertEqual(node5.parent, None)
|
|||
|
|
|
|||
|
|
self.assertEqual(node1.children, [])
|
|||
|
|
self.assertEqual(node2.children, [])
|
|||
|
|
self.assertEqual(node3.children, [])
|
|||
|
|
self.assertEqual(node4.children, [node3])
|
|||
|
|
self.assertEqual(node5.children, [node4])
|
|||
|
|
|
|||
|
|
def test_utils_mark_descendants(self):
|
|||
|
|
tree_nodes, flat_nodes = self._get_nodes()
|
|||
|
|
mark_descendants(tree_nodes)
|
|||
|
|
for node in flat_nodes:
|
|||
|
|
self.assertTrue(node.descendant, node)
|
|||
|
|
|
|||
|
|
def test_utils_find_selected(self):
|
|||
|
|
tree_nodes, flat_nodes = self._get_nodes()
|
|||
|
|
node = flat_nodes[0]
|
|||
|
|
selected = find_selected(tree_nodes)
|
|||
|
|
self.assertEqual(selected, node)
|
|||
|
|
selected = find_selected([])
|
|||
|
|
self.assertEqual(selected, None)
|
|||
|
|
|
|||
|
|
def test_utils_cut_levels(self):
|
|||
|
|
tree_nodes, flat_nodes = self._get_nodes()
|
|||
|
|
self.assertEqual(cut_levels(tree_nodes, 1), [flat_nodes[1]])
|
|||
|
|
|
|||
|
|
def test_empty_menu(self):
|
|||
|
|
context = self.get_context()
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes), 0)
|
|||
|
|
|
|||
|
|
def test_render_menu_with_invalid_language(self):
|
|||
|
|
"""
|
|||
|
|
When rendering the menu, always fallback to a configured
|
|||
|
|
language on the current site.
|
|||
|
|
"""
|
|||
|
|
# Refs - https://github.com/divio/django-cms/issues/6179
|
|||
|
|
site_2 = Site.objects.create(id=2, name="example-2.com", domain="example-2.com")
|
|||
|
|
de_defaults = {
|
|||
|
|
"site": site_2,
|
|||
|
|
"template": "nav_playground.html",
|
|||
|
|
"language": "de",
|
|||
|
|
}
|
|||
|
|
fr_defaults = {
|
|||
|
|
"site": site_2,
|
|||
|
|
"template": "nav_playground.html",
|
|||
|
|
"language": "fr",
|
|||
|
|
}
|
|||
|
|
create_page("DE-P1", in_navigation=True, **de_defaults)
|
|||
|
|
create_page("DE-P2", in_navigation=True, **de_defaults)
|
|||
|
|
create_page("DE-P3", in_navigation=True, **de_defaults)
|
|||
|
|
create_page("FR-P1", in_navigation=True, **fr_defaults)
|
|||
|
|
create_page("FR-P2", in_navigation=True, **fr_defaults)
|
|||
|
|
|
|||
|
|
with self.settings(SITE_ID=2):
|
|||
|
|
request = self.get_request("/en/")
|
|||
|
|
context = Context()
|
|||
|
|
context["request"] = request
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes), 5)
|
|||
|
|
self.assertEqual(nodes[0].title, "DE-P1")
|
|||
|
|
self.assertEqual(nodes[0].get_absolute_url(), "/de/de-p1/")
|
|||
|
|
self.assertEqual(nodes[1].title, "DE-P2")
|
|||
|
|
self.assertEqual(nodes[1].get_absolute_url(), "/de/de-p2/")
|
|||
|
|
self.assertEqual(nodes[2].title, "DE-P3")
|
|||
|
|
self.assertEqual(nodes[2].get_absolute_url(), "/de/de-p3/")
|
|||
|
|
self.assertEqual(nodes[3].title, "FR-P1")
|
|||
|
|
self.assertEqual(nodes[3].get_absolute_url(), "/fr/fr-p1/")
|
|||
|
|
self.assertEqual(nodes[4].title, "FR-P2")
|
|||
|
|
self.assertEqual(nodes[4].get_absolute_url(), "/fr/fr-p2/")
|
|||
|
|
|
|||
|
|
menu_pool.clear(site_id=2)
|
|||
|
|
|
|||
|
|
with self.settings(SITE_ID=2):
|
|||
|
|
request = self.get_request("/en/de-p2/")
|
|||
|
|
context = Context()
|
|||
|
|
context["request"] = request
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes), 5)
|
|||
|
|
self.assertEqual(nodes[0].title, "DE-P1")
|
|||
|
|
self.assertEqual(nodes[0].get_absolute_url(), "/de/de-p1/")
|
|||
|
|
self.assertEqual(nodes[1].title, "DE-P2")
|
|||
|
|
self.assertEqual(nodes[1].get_absolute_url(), "/de/de-p2/")
|
|||
|
|
self.assertEqual(nodes[2].title, "DE-P3")
|
|||
|
|
self.assertEqual(nodes[2].get_absolute_url(), "/de/de-p3/")
|
|||
|
|
self.assertEqual(nodes[3].title, "FR-P1")
|
|||
|
|
self.assertEqual(nodes[3].get_absolute_url(), "/fr/fr-p1/")
|
|||
|
|
self.assertEqual(nodes[4].title, "FR-P2")
|
|||
|
|
self.assertEqual(nodes[4].get_absolute_url(), "/fr/fr-p2/")
|
|||
|
|
|
|||
|
|
def test_render_menu_with_invalid_language_and_page(self):
|
|||
|
|
"""
|
|||
|
|
This tests an edge-case where the user requests a
|
|||
|
|
language not configure for the current site
|
|||
|
|
while having pages on the current site with unconfigured
|
|||
|
|
translations.
|
|||
|
|
"""
|
|||
|
|
# Refs - https://github.com/divio/django-cms/issues/6179
|
|||
|
|
site_2 = Site.objects.create(id=2, name="example-2.com", domain="example-2.com")
|
|||
|
|
de_defaults = {
|
|||
|
|
"site": site_2,
|
|||
|
|
"template": "nav_playground.html",
|
|||
|
|
"language": "de",
|
|||
|
|
"in_navigation": True,
|
|||
|
|
}
|
|||
|
|
create_page("DE-P1", **de_defaults)
|
|||
|
|
create_page("DE-P2", **de_defaults)
|
|||
|
|
create_page("DE-P3", **de_defaults)
|
|||
|
|
|
|||
|
|
# The nl language is not configured for the current site
|
|||
|
|
# as a result, we have to create the pages manually.
|
|||
|
|
nl_page_1 = Page(site=site_2)
|
|||
|
|
nl_page_1.add_to_tree()
|
|||
|
|
nl_page_1.save()
|
|||
|
|
nl_page_1.pagecontent_set.create(
|
|||
|
|
language="nl",
|
|||
|
|
title="NL-P1",
|
|||
|
|
template="nav_playground.html",
|
|||
|
|
in_navigation=True,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
nl_page_2 = Page(site=site_2)
|
|||
|
|
nl_page_2.add_to_tree()
|
|||
|
|
nl_page_2.save()
|
|||
|
|
nl_page_2.pagecontent_set.create(
|
|||
|
|
language="nl",
|
|||
|
|
title="NL-P2",
|
|||
|
|
template="nav_playground.html",
|
|||
|
|
in_navigation=True,
|
|||
|
|
)
|
|||
|
|
create_page_content("fr", "FR-P2", nl_page_2, in_navigation=True)
|
|||
|
|
|
|||
|
|
with self.settings(SITE_ID=2):
|
|||
|
|
request = self.get_request("/en/")
|
|||
|
|
context = Context()
|
|||
|
|
context["request"] = request
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes), 4)
|
|||
|
|
self.assertEqual(nodes[0].title, "DE-P1")
|
|||
|
|
self.assertEqual(nodes[0].get_absolute_url(), "/de/de-p1/")
|
|||
|
|
self.assertEqual(nodes[1].title, "DE-P2")
|
|||
|
|
self.assertEqual(nodes[1].get_absolute_url(), "/de/de-p2/")
|
|||
|
|
self.assertEqual(nodes[2].title, "DE-P3")
|
|||
|
|
self.assertEqual(nodes[2].get_absolute_url(), "/de/de-p3/")
|
|||
|
|
self.assertEqual(nodes[3].title, "FR-P2")
|
|||
|
|
self.assertEqual(nodes[3].get_absolute_url(), "/fr/fr-p2/")
|
|||
|
|
|
|||
|
|
menu_pool.clear(site_id=2)
|
|||
|
|
|
|||
|
|
with self.settings(SITE_ID=2):
|
|||
|
|
request = self.get_request("/en/de-p2/")
|
|||
|
|
context = Context()
|
|||
|
|
context["request"] = request
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes), 4)
|
|||
|
|
self.assertEqual(nodes[0].title, "DE-P1")
|
|||
|
|
self.assertEqual(nodes[0].get_absolute_url(), "/de/de-p1/")
|
|||
|
|
self.assertEqual(nodes[1].title, "DE-P2")
|
|||
|
|
self.assertEqual(nodes[1].get_absolute_url(), "/de/de-p2/")
|
|||
|
|
self.assertEqual(nodes[2].title, "DE-P3")
|
|||
|
|
self.assertEqual(nodes[2].get_absolute_url(), "/de/de-p3/")
|
|||
|
|
self.assertEqual(nodes[3].title, "FR-P2")
|
|||
|
|
self.assertEqual(nodes[3].get_absolute_url(), "/fr/fr-p2/")
|
|||
|
|
|
|||
|
|
def test_render_menu_with_invalid_language_and_no_fallbacks(self):
|
|||
|
|
"""
|
|||
|
|
The requested language is valid but there's no page
|
|||
|
|
with it and the user has disabled all fallbacks.
|
|||
|
|
The cms should render only nodes for the requested language.
|
|||
|
|
"""
|
|||
|
|
defaults = {
|
|||
|
|
"template": "nav_playground.html",
|
|||
|
|
"language": "de",
|
|||
|
|
}
|
|||
|
|
create_page("DE-P1", in_navigation=True, **defaults)
|
|||
|
|
create_page("DE-P2", in_navigation=True, **defaults)
|
|||
|
|
create_page("DE-P3", in_navigation=True, **defaults)
|
|||
|
|
|
|||
|
|
lang_settings = copy.deepcopy(get_cms_setting("LANGUAGES"))
|
|||
|
|
lang_settings[1][0]["fallbacks"] = []
|
|||
|
|
lang_settings[1][1]["fallbacks"] = []
|
|||
|
|
|
|||
|
|
with self.settings(CMS_LANGUAGES=lang_settings):
|
|||
|
|
request = self.get_request("/en/")
|
|||
|
|
context = Context()
|
|||
|
|
context["request"] = request
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes), 0)
|
|||
|
|
|
|||
|
|
menu_pool.clear(site_id=1)
|
|||
|
|
|
|||
|
|
with self.settings(CMS_LANGUAGES=lang_settings):
|
|||
|
|
request = self.get_request("/en/de-p2/")
|
|||
|
|
context = Context()
|
|||
|
|
context["request"] = request
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes), 0)
|
|||
|
|
|
|||
|
|
def test_menu_renderer_language_code_handling(self):
|
|||
|
|
"""
|
|||
|
|
Tests that MenuRenderer correctly handles language code determination:
|
|||
|
|
1. Uses request.LANGUAGE_CODE if available
|
|||
|
|
2. Falls back to site's default language if LANGUAGE_CODE not set
|
|||
|
|
"""
|
|||
|
|
site = Site.objects.get_current()
|
|||
|
|
|
|||
|
|
# Test with LANGUAGE_CODE set in request
|
|||
|
|
request = self.get_request("/")
|
|||
|
|
request.LANGUAGE_CODE = "fr" # Set a specific language code
|
|||
|
|
renderer = menu_pool.get_renderer(request)
|
|||
|
|
self.assertEqual(renderer.request_language, "fr")
|
|||
|
|
|
|||
|
|
# Test fallback when LANGUAGE_CODE not set
|
|||
|
|
request = self.get_request("/")
|
|||
|
|
# Ensure request has no LANGUAGE_CODE
|
|||
|
|
if hasattr(request, "LANGUAGE_CODE"):
|
|||
|
|
delattr(request, "LANGUAGE_CODE")
|
|||
|
|
renderer = menu_pool.get_renderer(request)
|
|||
|
|
# Should fall back to site's default language
|
|||
|
|
expected_language = get_default_language_for_site(site.pk)
|
|||
|
|
self.assertEqual(renderer.request_language, expected_language)
|
|||
|
|
|
|||
|
|
def test_cmsnavigationnode_attr(self):
|
|||
|
|
"""
|
|||
|
|
Test population of the attr object of the CMSNavigationNode from pages
|
|||
|
|
"""
|
|||
|
|
page_defaults = {
|
|||
|
|
"template": "nav_playground.html",
|
|||
|
|
"language": "en",
|
|||
|
|
}
|
|||
|
|
home = create_page("Normal Page", in_navigation=True, reverse_id="normal-page", **page_defaults)
|
|||
|
|
home.set_as_homepage()
|
|||
|
|
create_page("Redirected Page", in_navigation=True, reverse_id="redirected-page", redirect='https://redirect.example.com', **page_defaults)
|
|||
|
|
|
|||
|
|
request = self.get_request("/en/")
|
|||
|
|
context = Context()
|
|||
|
|
context["request"] = request
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
|
|||
|
|
expected_normal_attr = {
|
|||
|
|
"is_page": True,
|
|||
|
|
"soft_root": False,
|
|||
|
|
"redirect_url": None,
|
|||
|
|
"auth_required": False,
|
|||
|
|
"reverse_id": "normal-page",
|
|||
|
|
"is_home": True,
|
|||
|
|
"visible_for_authenticated": True,
|
|||
|
|
"visible_for_anonymous": True,
|
|||
|
|
"navigation_extenders": [],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
expected_redirect_attr = {
|
|||
|
|
"is_page": True,
|
|||
|
|
"soft_root": False,
|
|||
|
|
"redirect_url": "https://redirect.example.com",
|
|||
|
|
"auth_required": False,
|
|||
|
|
"reverse_id": "redirected-page",
|
|||
|
|
"is_home": False,
|
|||
|
|
"visible_for_authenticated": True,
|
|||
|
|
"visible_for_anonymous": True,
|
|||
|
|
"navigation_extenders": [],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
self.assertEqual(2, len(nodes))
|
|||
|
|
self.assertEqual(expected_normal_attr, nodes[0].attr)
|
|||
|
|
self.assertEqual(expected_redirect_attr, nodes[1].attr)
|
|||
|
|
|
|||
|
|
|
|||
|
|
@override_settings(CMS_PERMISSION=False)
|
|||
|
|
class AdvancedSoftrootTests(SoftrootFixture, CMSTestCase):
|
|||
|
|
"""
|
|||
|
|
Tree in fixture (as taken from issue 662):
|
|||
|
|
|
|||
|
|
top
|
|||
|
|
root
|
|||
|
|
aaa
|
|||
|
|
111
|
|||
|
|
ccc
|
|||
|
|
ddd
|
|||
|
|
222
|
|||
|
|
bbb
|
|||
|
|
333
|
|||
|
|
444
|
|||
|
|
|
|||
|
|
In the fixture, all pages are "in_navigation", "published" and
|
|||
|
|
NOT-"soft_root".
|
|||
|
|
|
|||
|
|
What is a soft root?
|
|||
|
|
|
|||
|
|
If a page is a soft root, it becomes the root page in the menu if
|
|||
|
|
we are currently on or under that page.
|
|||
|
|
|
|||
|
|
If we are above that page, the children of this page are not shown.
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def tearDown(self):
|
|||
|
|
Page.objects.all().delete()
|
|||
|
|
menu_pool.clear(all=True)
|
|||
|
|
|
|||
|
|
def get_page(self, name):
|
|||
|
|
return Page.objects.get(urls__slug=name)
|
|||
|
|
|
|||
|
|
def assertTreeQuality(self, a, b, *attrs):
|
|||
|
|
"""
|
|||
|
|
Checks that the node-lists a and b are the same for attrs.
|
|||
|
|
|
|||
|
|
This is recursive over the tree
|
|||
|
|
"""
|
|||
|
|
msg = f"root nodes: {len(a)!r} != {len(b)!r} with {a!r}, {b!r}"
|
|||
|
|
self.assertEqual(len(a), len(b), msg)
|
|||
|
|
for n1, n2 in zip(a, b, strict=False):
|
|||
|
|
for attr in attrs:
|
|||
|
|
a1 = getattr(n1, attr)
|
|||
|
|
a2 = getattr(n2, attr)
|
|||
|
|
msg = f"{attr}: {a1!r} != {a2!r} with {n1!r}, {n2!r} ({attr})"
|
|||
|
|
self.assertEqual(a1, a2, msg)
|
|||
|
|
self.assertTreeQuality(n1.children, n2.children)
|
|||
|
|
|
|||
|
|
def test_top_not_in_nav(self):
|
|||
|
|
"""
|
|||
|
|
top: not in navigation
|
|||
|
|
|
|||
|
|
tag: show_menu 0 100 0 100
|
|||
|
|
|
|||
|
|
context shared: current page is aaa
|
|||
|
|
context 1: root is NOT a softroot
|
|||
|
|
context 2: root IS a softroot
|
|||
|
|
|
|||
|
|
expected result: the two node-trees should be equal
|
|||
|
|
"""
|
|||
|
|
top = self.get_page("top")
|
|||
|
|
top.in_navigation = False
|
|||
|
|
top.save()
|
|||
|
|
aaa = self.get_page("aaa")
|
|||
|
|
# root is NOT a soft root
|
|||
|
|
context = self.get_context(aaa.get_absolute_url())
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 0 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
hard_root = context["children"]
|
|||
|
|
# root IS a soft root
|
|||
|
|
root = self.get_page("root")
|
|||
|
|
root.update_translations(soft_root=True)
|
|||
|
|
aaa = self.get_page("aaa")
|
|||
|
|
context = self.get_context(aaa.get_absolute_url())
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 0 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
soft_root = context["children"]
|
|||
|
|
# assert the two trees are equal in terms of 'level' and 'title'
|
|||
|
|
self.assertTreeQuality(hard_root, soft_root, "level", "title")
|
|||
|
|
|
|||
|
|
def test_menu_tree_without_soft_root(self):
|
|||
|
|
"""
|
|||
|
|
tag: show_menu 0 100 0 100
|
|||
|
|
|
|||
|
|
expected result 1:
|
|||
|
|
0:top
|
|||
|
|
1:root
|
|||
|
|
2:aaa
|
|||
|
|
3:111
|
|||
|
|
4:ccc
|
|||
|
|
5:ddd
|
|||
|
|
3:222
|
|||
|
|
2:bbb
|
|||
|
|
"""
|
|||
|
|
aaa = self.get_page("aaa")
|
|||
|
|
# root is NOT a soft root
|
|||
|
|
context = self.get_context(aaa.get_absolute_url(), page=aaa)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 0 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
hard_root = context["children"]
|
|||
|
|
mock_tree = [
|
|||
|
|
AttributeObject(
|
|||
|
|
title="top",
|
|||
|
|
level=0,
|
|||
|
|
children=[
|
|||
|
|
AttributeObject(
|
|||
|
|
title="root",
|
|||
|
|
level=1,
|
|||
|
|
children=[
|
|||
|
|
AttributeObject(
|
|||
|
|
title="aaa",
|
|||
|
|
level=2,
|
|||
|
|
children=[
|
|||
|
|
AttributeObject(
|
|||
|
|
title="111",
|
|||
|
|
level=3,
|
|||
|
|
children=[
|
|||
|
|
AttributeObject(
|
|||
|
|
title="ccc",
|
|||
|
|
level=4,
|
|||
|
|
children=[AttributeObject(title="ddd", level=5, children=[])],
|
|||
|
|
)
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
AttributeObject(title="222", level=3, children=[]),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
AttributeObject(title="bbb", level=2, children=[]),
|
|||
|
|
],
|
|||
|
|
)
|
|||
|
|
],
|
|||
|
|
)
|
|||
|
|
]
|
|||
|
|
self.assertTreeQuality(hard_root, mock_tree)
|
|||
|
|
|
|||
|
|
def test_menu_tree_with_soft_root(self):
|
|||
|
|
"""
|
|||
|
|
tag: show_menu 0 100 0 100
|
|||
|
|
|
|||
|
|
expected result 2:
|
|||
|
|
0:root
|
|||
|
|
1:aaa
|
|||
|
|
2:111
|
|||
|
|
3:ccc
|
|||
|
|
4:ddd
|
|||
|
|
2:222
|
|||
|
|
1:bbb
|
|||
|
|
"""
|
|||
|
|
# root IS a soft root
|
|||
|
|
root = self.get_page("root")
|
|||
|
|
root.update_translations(soft_root=True)
|
|||
|
|
aaa = self.get_page("aaa")
|
|||
|
|
context = self.get_context(aaa.get_absolute_url(), page=aaa)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 0 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
soft_root = context["children"]
|
|||
|
|
mock_tree = [
|
|||
|
|
AttributeObject(
|
|||
|
|
title="root",
|
|||
|
|
level=0,
|
|||
|
|
children=[
|
|||
|
|
AttributeObject(
|
|||
|
|
title="aaa",
|
|||
|
|
level=1,
|
|||
|
|
children=[
|
|||
|
|
AttributeObject(
|
|||
|
|
title="111",
|
|||
|
|
level=2,
|
|||
|
|
children=[
|
|||
|
|
AttributeObject(
|
|||
|
|
title="ccc",
|
|||
|
|
level=3,
|
|||
|
|
children=[AttributeObject(title="ddd", level=4, children=[])],
|
|||
|
|
)
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
AttributeObject(title="222", level=2, children=[]),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
AttributeObject(title="bbb", level=1, children=[]),
|
|||
|
|
],
|
|||
|
|
)
|
|||
|
|
]
|
|||
|
|
self.assertTreeQuality(soft_root, mock_tree, "title", "level")
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ShowSubMenuCheck(SubMenusFixture, BaseMenuTest):
|
|||
|
|
"""
|
|||
|
|
Tree from fixture:
|
|||
|
|
|
|||
|
|
+ P1
|
|||
|
|
| + P2
|
|||
|
|
| + P3
|
|||
|
|
+ P4
|
|||
|
|
| + P5
|
|||
|
|
+ P6
|
|||
|
|
+ P7 (not in menu)
|
|||
|
|
+ P8
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def test_show_submenu(self):
|
|||
|
|
page = self.get_page(6)
|
|||
|
|
subpage = self.get_page(8)
|
|||
|
|
context = self.get_context(page.get_absolute_url(), page=page)
|
|||
|
|
# test standard show_menu
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_sub_menu %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
self.assertEqual(nodes[0].id, subpage.pk)
|
|||
|
|
|
|||
|
|
def test_show_submenu_num_queries(self):
|
|||
|
|
page = self.get_page(6)
|
|||
|
|
subpage = self.get_page(8)
|
|||
|
|
context = self.get_context(page.get_absolute_url(), page=page)
|
|||
|
|
|
|||
|
|
# test standard show_menu
|
|||
|
|
with self.assertNumQueries(5):
|
|||
|
|
"""
|
|||
|
|
The queries should be:
|
|||
|
|
get all page contents
|
|||
|
|
get all page permissions
|
|||
|
|
get all page urls
|
|||
|
|
get the menu cache key
|
|||
|
|
set the menu cache key
|
|||
|
|
"""
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_sub_menu %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
self.assertEqual(nodes[0].id, subpage.pk)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ShowMenuBelowIdTests(BaseMenuTest):
|
|||
|
|
"""
|
|||
|
|
Test for issue 521
|
|||
|
|
|
|||
|
|
Build the following tree:
|
|||
|
|
|
|||
|
|
A
|
|||
|
|
|-B
|
|||
|
|
|-C
|
|||
|
|
\\-D (not in nav)
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def test_not_in_navigation(self):
|
|||
|
|
a = create_page("A", "nav_playground.html", "en", in_navigation=True, reverse_id="a")
|
|||
|
|
b = create_page("B", "nav_playground.html", "en", parent=a, in_navigation=True)
|
|||
|
|
c = create_page("C", "nav_playground.html", "en", parent=b, in_navigation=True)
|
|||
|
|
create_page("D", "nav_playground.html", "en", parent=self.reload(b), in_navigation=False)
|
|||
|
|
context = self.get_context(a.get_absolute_url())
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu_below_id 'a' 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes), 1, nodes)
|
|||
|
|
node = nodes[0]
|
|||
|
|
self.assertEqual(node.id, b.pk)
|
|||
|
|
children = node.children
|
|||
|
|
self.assertEqual(len(children), 1, repr(children))
|
|||
|
|
child = children[0]
|
|||
|
|
self.assertEqual(child.id, c.pk)
|
|||
|
|
|
|||
|
|
def test_menu_beyond_soft_root(self):
|
|||
|
|
"""
|
|||
|
|
Test for issue 4107
|
|||
|
|
|
|||
|
|
Build the following tree:
|
|||
|
|
|
|||
|
|
A
|
|||
|
|
|-B (soft_root)
|
|||
|
|
|-C
|
|||
|
|
"""
|
|||
|
|
stdkwargs = {
|
|||
|
|
"template": "nav_playground.html",
|
|||
|
|
"language": "en",
|
|||
|
|
"in_navigation": True,
|
|||
|
|
}
|
|||
|
|
a = create_page("A", reverse_id="a", **stdkwargs)
|
|||
|
|
b = create_page("B", parent=a, soft_root=True, **stdkwargs)
|
|||
|
|
c = create_page("C", parent=b, **stdkwargs)
|
|||
|
|
|
|||
|
|
context = self.get_context(a.get_absolute_url(), page=a)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
# check whole menu
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
a_node = nodes[0]
|
|||
|
|
self.assertEqual(a_node.id, a.pk) # On A, show from A
|
|||
|
|
self.assertEqual(len(a_node.children), 1)
|
|||
|
|
b_node = a_node.children[0]
|
|||
|
|
self.assertEqual(b_node.id, b.pk)
|
|||
|
|
self.assertEqual(len(b_node.children), 1)
|
|||
|
|
c_node = b_node.children[0]
|
|||
|
|
self.assertEqual(c_node.id, c.pk)
|
|||
|
|
self.assertEqual(len(c_node.children), 0)
|
|||
|
|
|
|||
|
|
context = self.get_context(b.get_absolute_url(), page=b)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
# check whole menu
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
b_node = nodes[0]
|
|||
|
|
self.assertEqual(b_node.id, b.pk) # On B, show from B
|
|||
|
|
self.assertEqual(len(b_node.children), 1)
|
|||
|
|
c_node = b_node.children[0]
|
|||
|
|
self.assertEqual(c_node.id, c.pk)
|
|||
|
|
self.assertEqual(len(c_node.children), 0)
|
|||
|
|
|
|||
|
|
context = self.get_context(c.get_absolute_url(), page=c)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
# check whole menu
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
b_node = nodes[0]
|
|||
|
|
self.assertEqual(b_node.id, b.pk) # On C, show from B
|
|||
|
|
self.assertEqual(len(b_node.children), 1)
|
|||
|
|
c_node = b_node.children[0]
|
|||
|
|
self.assertEqual(c_node.id, c.pk)
|
|||
|
|
self.assertEqual(len(c_node.children), 0)
|
|||
|
|
|
|||
|
|
context = self.get_context(a.get_absolute_url(), page=a)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu_below_id 'a' 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
# check whole menu
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
b_node = nodes[0]
|
|||
|
|
self.assertEqual(b_node.id, b.pk) # On A, show from B (since below A)
|
|||
|
|
self.assertEqual(len(b_node.children), 1)
|
|||
|
|
c_node = b_node.children[0]
|
|||
|
|
self.assertEqual(c_node.id, c.pk)
|
|||
|
|
self.assertEqual(len(c_node.children), 0)
|
|||
|
|
|
|||
|
|
context = self.get_context(b.get_absolute_url(), page=b)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu_below_id 'a' 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
# check whole menu
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
b_node = nodes[0]
|
|||
|
|
self.assertEqual(b_node.id, b.pk) # On B, show from B (since below A)
|
|||
|
|
self.assertEqual(len(b_node.children), 1)
|
|||
|
|
c_node = b_node.children[0]
|
|||
|
|
self.assertEqual(c_node.id, c.pk)
|
|||
|
|
self.assertEqual(len(c_node.children), 0)
|
|||
|
|
|
|||
|
|
context = self.get_context(c.get_absolute_url(), page=c)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu_below_id 'a' 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
# check whole menu
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
b_node = nodes[0]
|
|||
|
|
self.assertEqual(b_node.id, b.pk) # On C, show from B (since below A)
|
|||
|
|
self.assertEqual(len(b_node.children), 1)
|
|||
|
|
c_node = b_node.children[0]
|
|||
|
|
self.assertEqual(c_node.id, c.pk)
|
|||
|
|
self.assertEqual(len(c_node.children), 0)
|
|||
|
|
|
|||
|
|
def test_not_in_navigation_num_queries(self):
|
|||
|
|
"""
|
|||
|
|
Test for issue 521
|
|||
|
|
|
|||
|
|
Build the following tree:
|
|||
|
|
|
|||
|
|
A
|
|||
|
|
|-B
|
|||
|
|
|-C
|
|||
|
|
\\-D (not in nav)
|
|||
|
|
"""
|
|||
|
|
a = create_page("A", "nav_playground.html", "en", in_navigation=True, reverse_id="a")
|
|||
|
|
b = create_page("B", "nav_playground.html", "en", parent=a, in_navigation=True)
|
|||
|
|
c = create_page("C", "nav_playground.html", "en", parent=b, in_navigation=True)
|
|||
|
|
create_page("D", "nav_playground.html", "en", parent=self.reload(b), in_navigation=False)
|
|||
|
|
|
|||
|
|
with LanguageOverride("en"):
|
|||
|
|
context = self.get_context(a.get_absolute_url())
|
|||
|
|
with self.assertNumQueries(5):
|
|||
|
|
"""
|
|||
|
|
The queries should be:
|
|||
|
|
get all page urls
|
|||
|
|
get all page contents
|
|||
|
|
get all page permissions
|
|||
|
|
get the menu cache key
|
|||
|
|
set the menu cache key
|
|||
|
|
"""
|
|||
|
|
# Actually seems to run:
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu_below_id 'a' 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes), 1, nodes)
|
|||
|
|
node = nodes[0]
|
|||
|
|
self.assertEqual(node.id, b.pk)
|
|||
|
|
children = node.children
|
|||
|
|
self.assertEqual(len(children), 1, repr(children))
|
|||
|
|
child = children[0]
|
|||
|
|
self.assertEqual(child.id, c.pk)
|
|||
|
|
|
|||
|
|
def test_menu_in_soft_root(self):
|
|||
|
|
"""
|
|||
|
|
Test for issue 3504
|
|||
|
|
|
|||
|
|
Build the following tree:
|
|||
|
|
|
|||
|
|
A
|
|||
|
|
|-B
|
|||
|
|
C (soft_root)
|
|||
|
|
"""
|
|||
|
|
a = create_page("A", "nav_playground.html", "en", in_navigation=True, reverse_id="a")
|
|||
|
|
b = create_page("B", "nav_playground.html", "en", parent=a, in_navigation=True)
|
|||
|
|
c = create_page("C", "nav_playground.html", "en", in_navigation=True, soft_root=True)
|
|||
|
|
context = self.get_context(a.get_absolute_url())
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu_below_id 'a' %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
node = nodes[0]
|
|||
|
|
self.assertEqual(node.id, b.pk)
|
|||
|
|
context = self.get_context(c.get_absolute_url())
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu_below_id 'a' %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
node = nodes[0]
|
|||
|
|
self.assertEqual(node.id, b.pk)
|
|||
|
|
|
|||
|
|
|
|||
|
|
@override_settings(
|
|||
|
|
CMS_PERMISSION=True,
|
|||
|
|
CMS_PUBLIC_FOR="staff",
|
|||
|
|
)
|
|||
|
|
class ViewPermissionMenuTests(CMSTestCase):
|
|||
|
|
def setUp(self):
|
|||
|
|
self.page = create_page("page", "nav_playground.html", "en")
|
|||
|
|
self.pages = [self.page]
|
|||
|
|
self.user = self.get_standard_user()
|
|||
|
|
self.site = Site.objects.get_current()
|
|||
|
|
|
|||
|
|
def get_request(self, user=None):
|
|||
|
|
attrs = {
|
|||
|
|
"user": user or AnonymousUser(),
|
|||
|
|
"REQUEST": {},
|
|||
|
|
"POST": {},
|
|||
|
|
"GET": {},
|
|||
|
|
"session": {},
|
|||
|
|
}
|
|||
|
|
return type("Request", (object,), attrs)
|
|||
|
|
|
|||
|
|
def test_public_for_all_staff(self):
|
|||
|
|
request = self.get_request(self.user)
|
|||
|
|
request.user.is_staff = True
|
|||
|
|
all = [page.get_content_obj() for page in self.pages]
|
|||
|
|
with self.assertNumQueries(4):
|
|||
|
|
"""
|
|||
|
|
The queries are:
|
|||
|
|
User permissions
|
|||
|
|
Content type
|
|||
|
|
GlobalPagePermission query
|
|||
|
|
PagePermission count query
|
|||
|
|
"""
|
|||
|
|
visible = get_visible_page_contents(request, all, self.site)
|
|||
|
|
self.assertEqual(visible, all)
|
|||
|
|
|
|||
|
|
@override_settings(CMS_PUBLIC_FOR="all")
|
|||
|
|
def test_public_for_all(self):
|
|||
|
|
request = self.get_request(self.user)
|
|||
|
|
all = [page.get_content_obj() for page in self.pages]
|
|||
|
|
with self.assertNumQueries(4):
|
|||
|
|
"""
|
|||
|
|
The queries are:
|
|||
|
|
User permissions
|
|||
|
|
Content type
|
|||
|
|
GlobalPagePermission query
|
|||
|
|
PagePermission query for affected pages
|
|||
|
|
"""
|
|||
|
|
visible = get_visible_page_contents(request, all, self.site)
|
|||
|
|
self.assertEqual(visible, all)
|
|||
|
|
|
|||
|
|
@override_settings(CMS_PUBLIC_FOR="all")
|
|||
|
|
def test_unauthed(self):
|
|||
|
|
request = self.get_request()
|
|||
|
|
all = [page.get_content_obj() for page in self.pages]
|
|||
|
|
with self.assertNumQueries(1):
|
|||
|
|
"""
|
|||
|
|
The query is:
|
|||
|
|
PagePermission query for affected pages
|
|||
|
|
"""
|
|||
|
|
visible = get_visible_page_contents(request, all, self.site)
|
|||
|
|
self.assertEqual(visible, all)
|
|||
|
|
|
|||
|
|
def test_authed_basic_perm(self):
|
|||
|
|
self.user.user_permissions.add(Permission.objects.get(codename="view_page"))
|
|||
|
|
request = self.get_request(self.user)
|
|||
|
|
all = [page.get_content_obj() for page in self.pages]
|
|||
|
|
|
|||
|
|
with self.assertNumQueries(2):
|
|||
|
|
"""
|
|||
|
|
The queries are:
|
|||
|
|
User permissions
|
|||
|
|
Content type
|
|||
|
|
"""
|
|||
|
|
visible = get_visible_page_contents(request, all, self.site)
|
|||
|
|
self.assertEqual(visible, all)
|
|||
|
|
|
|||
|
|
def test_authed_no_access(self):
|
|||
|
|
request = self.get_request(self.user)
|
|||
|
|
all = [page.get_content_obj() for page in self.pages]
|
|||
|
|
with self.assertNumQueries(4):
|
|||
|
|
"""
|
|||
|
|
The queries are:
|
|||
|
|
User permission
|
|||
|
|
Group permission
|
|||
|
|
GlobalpagePermission query for user
|
|||
|
|
PagePermission query for user
|
|||
|
|
"""
|
|||
|
|
visible = get_visible_page_contents(request, all, self.site)
|
|||
|
|
self.assertEqual(visible, [])
|
|||
|
|
|
|||
|
|
def test_unauthed_no_access(self):
|
|||
|
|
request = self.get_request()
|
|||
|
|
|
|||
|
|
with self.assertNumQueries(0):
|
|||
|
|
nodes = get_visible_page_contents(
|
|||
|
|
request, (page.get_page_content_obj_attribute() for page in self.pages), self.site
|
|||
|
|
)
|
|||
|
|
self.assertEqual(nodes, [])
|
|||
|
|
|
|||
|
|
def test_page_permissions(self):
|
|||
|
|
request = self.get_request(self.user)
|
|||
|
|
PagePermission.objects.create(can_view=True, user=self.user, page=self.page)
|
|||
|
|
all = [page.get_content_obj() for page in self.pages]
|
|||
|
|
with self.assertNumQueries(4):
|
|||
|
|
"""
|
|||
|
|
The queries are:
|
|||
|
|
PagePermission query for affected pages
|
|||
|
|
User permissions
|
|||
|
|
Content type
|
|||
|
|
GlobalpagePermission query for user
|
|||
|
|
"""
|
|||
|
|
visible = get_visible_page_contents(request, all, self.site)
|
|||
|
|
self.assertEqual(visible, all)
|
|||
|
|
|
|||
|
|
def test_page_permissions_view_groups(self):
|
|||
|
|
group = Group.objects.create(name="testgroup")
|
|||
|
|
self.user.groups.add(group)
|
|||
|
|
request = self.get_request(self.user)
|
|||
|
|
PagePermission.objects.create(can_view=True, group=group, page=self.page)
|
|||
|
|
all = [page.get_content_obj() for page in self.pages]
|
|||
|
|
|
|||
|
|
with self.assertNumQueries(5):
|
|||
|
|
"""
|
|||
|
|
The queries are:
|
|||
|
|
PagePermission query for affected pages
|
|||
|
|
User permissions
|
|||
|
|
Content type
|
|||
|
|
GlobalpagePermission query for user
|
|||
|
|
Group query via PagePermission
|
|||
|
|
"""
|
|||
|
|
visible = get_visible_page_contents(request, all, self.site)
|
|||
|
|
self.assertEqual(visible, all)
|
|||
|
|
|
|||
|
|
def test_global_permission(self):
|
|||
|
|
GlobalPagePermission.objects.create(can_view=True, user=self.user)
|
|||
|
|
request = self.get_request(self.user)
|
|||
|
|
group = Group.objects.create(name="testgroup")
|
|||
|
|
PagePermission.objects.create(can_view=True, group=group, page=self.page)
|
|||
|
|
all = [page.get_content_obj() for page in self.pages]
|
|||
|
|
with self.assertNumQueries(3):
|
|||
|
|
"""
|
|||
|
|
The queries are:
|
|||
|
|
User permissions
|
|||
|
|
Content type
|
|||
|
|
GlobalpagePermission query for user
|
|||
|
|
"""
|
|||
|
|
visible = get_visible_page_contents(request, all, self.site)
|
|||
|
|
self.assertEqual(visible, all)
|
|||
|
|
|
|||
|
|
|
|||
|
|
@override_settings(
|
|||
|
|
CMS_PERMISSION=True,
|
|||
|
|
CMS_PUBLIC_FOR="all",
|
|||
|
|
)
|
|||
|
|
class PublicViewPermissionMenuTests(CMSTestCase):
|
|||
|
|
def setUp(self):
|
|||
|
|
"""
|
|||
|
|
Create this published hierarchy:
|
|||
|
|
A
|
|||
|
|
B1 B2
|
|||
|
|
C1 C2 C3 C4
|
|||
|
|
"""
|
|||
|
|
template = "nav_playground.html"
|
|||
|
|
kw = dict(in_navigation=True)
|
|||
|
|
a = create_page("a", template, "en", **kw)
|
|||
|
|
b1 = create_page("b1", template, "en", parent=a, **kw)
|
|||
|
|
b2 = create_page("b2", template, "en", parent=a, **kw)
|
|||
|
|
c1 = create_page("c1", template, "en", parent=b1, **kw)
|
|||
|
|
c2 = create_page("c2", template, "en", parent=b1, **kw)
|
|||
|
|
c3 = create_page("c3", template, "en", parent=b2, **kw)
|
|||
|
|
c4 = create_page("c4", template, "en", parent=b2, **kw)
|
|||
|
|
self.pages = [a, b1, c1, c2, b2, c3, c4] # tree order
|
|||
|
|
self.site = Site.objects.get_current()
|
|||
|
|
|
|||
|
|
self.user = self._create_user("standard", is_staff=False, is_superuser=False)
|
|||
|
|
self.other = self._create_user("other", is_staff=False, is_superuser=False)
|
|||
|
|
PagePermission.objects.create(page=b1, user=self.user, can_view=True, grant_on=ACCESS_PAGE_AND_DESCENDANTS)
|
|||
|
|
PagePermission.objects.create(page=b2, user=self.other, can_view=True, grant_on=ACCESS_PAGE_AND_DESCENDANTS)
|
|||
|
|
attrs = {
|
|||
|
|
"user": self.user,
|
|||
|
|
"REQUEST": {},
|
|||
|
|
"POST": {},
|
|||
|
|
"GET": {},
|
|||
|
|
"session": {},
|
|||
|
|
}
|
|||
|
|
self.request = type("Request", (object,), attrs)
|
|||
|
|
|
|||
|
|
def test_list_access(self):
|
|||
|
|
page_contents = [page.get_content_obj() for page in self.pages]
|
|||
|
|
page_contents = get_visible_page_contents(self.request, page_contents, self.site)
|
|||
|
|
titles = [page_content.title for page_content in page_contents]
|
|||
|
|
self.assertSequenceEqual(sorted(titles), ["a", "b1", "c1", "c2"])
|
|||
|
|
|
|||
|
|
def test_qs_access(self):
|
|||
|
|
page_contents = PageContent.objects.all()
|
|||
|
|
page_contents = get_visible_page_contents(self.request, page_contents, self.site)
|
|||
|
|
titles = [page_content.title for page_content in page_contents]
|
|||
|
|
self.assertSequenceEqual(sorted(titles), ["a", "b1", "c1", "c2"])
|
|||
|
|
|
|||
|
|
|
|||
|
|
@override_settings(
|
|||
|
|
CMS_PERMISSION=True,
|
|||
|
|
CMS_PUBLIC_FOR="all",
|
|||
|
|
)
|
|||
|
|
class ViewPermissionMenuQueryTests(CMSTestCase):
|
|||
|
|
"""
|
|||
|
|
Regression tests: building the menu must not run one query per page.
|
|||
|
|
|
|||
|
|
``CMSMenu.get_nodes`` restricts the loaded fields with ``only()``. Every
|
|||
|
|
field accessed by the view-restriction check in
|
|||
|
|
``get_visible_page_contents`` (such as ``page.path``) has to be part of
|
|||
|
|
that ``only()`` call, otherwise each page triggers an extra query to
|
|||
|
|
load the deferred field.
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
template = "nav_playground.html"
|
|||
|
|
|
|||
|
|
def _get_menu_query_count(self):
|
|||
|
|
request = self.get_request("/")
|
|||
|
|
renderer = menu_pool.get_renderer(request)
|
|||
|
|
menu = CMSMenu(renderer)
|
|||
|
|
with CaptureQueriesContext(connection) as queries:
|
|||
|
|
menu.get_nodes(request)
|
|||
|
|
return len(queries.captured_queries)
|
|||
|
|
|
|||
|
|
def test_number_of_queries_does_not_scale_with_page_count(self):
|
|||
|
|
create_page("home", self.template, "en", in_navigation=True)
|
|||
|
|
restricted = create_page("restricted", self.template, "en", in_navigation=True)
|
|||
|
|
PagePermission.objects.create(
|
|||
|
|
page=restricted,
|
|||
|
|
user=self.get_standard_user(),
|
|||
|
|
can_view=True,
|
|||
|
|
grant_on=ACCESS_PAGE_AND_DESCENDANTS,
|
|||
|
|
)
|
|||
|
|
for index in range(3):
|
|||
|
|
create_page(f"page-{index}", self.template, "en", in_navigation=True)
|
|||
|
|
|
|||
|
|
self._get_menu_query_count() # warm up caches (site, content types, ...)
|
|||
|
|
query_count = self._get_menu_query_count()
|
|||
|
|
|
|||
|
|
for index in range(5):
|
|||
|
|
create_page(f"extra-{index}", self.template, "en", in_navigation=True)
|
|||
|
|
|
|||
|
|
self.assertEqual(self._get_menu_query_count(), query_count)
|
|||
|
|
|
|||
|
|
|
|||
|
|
@override_settings(CMS_PERMISSION=False)
|
|||
|
|
class SoftrootTests(CMSTestCase):
|
|||
|
|
"""
|
|||
|
|
Ask evildmp/superdmp if you don't understand softroots!
|
|||
|
|
|
|||
|
|
Softroot description from the docs:
|
|||
|
|
|
|||
|
|
A soft root is a page that acts as the root for a menu navigation tree.
|
|||
|
|
|
|||
|
|
Typically, this will be a page that is the root of a significant new
|
|||
|
|
section on your site.
|
|||
|
|
|
|||
|
|
When the soft root feature is enabled, the navigation menu for any page
|
|||
|
|
will start at the nearest soft root, rather than at the real root of
|
|||
|
|
the site’s page hierarchy.
|
|||
|
|
|
|||
|
|
This feature is useful when your site has deep page hierarchies (and
|
|||
|
|
therefore multiple levels in its navigation trees). In such a case, you
|
|||
|
|
usually don’t want to present site visitors with deep menus of nested
|
|||
|
|
items.
|
|||
|
|
|
|||
|
|
For example, you’re on the page “Introduction to Bleeding”, so the menu
|
|||
|
|
might look like this:
|
|||
|
|
|
|||
|
|
School of Medicine
|
|||
|
|
Medical Education
|
|||
|
|
Departments
|
|||
|
|
Department of Lorem Ipsum
|
|||
|
|
Department of Donec Imperdiet
|
|||
|
|
Department of Cras Eros
|
|||
|
|
Department of Mediaeval Surgery
|
|||
|
|
Theory
|
|||
|
|
Cures
|
|||
|
|
Bleeding
|
|||
|
|
Introduction to Bleeding <this is the current page>
|
|||
|
|
Bleeding - the scientific evidence
|
|||
|
|
Cleaning up the mess
|
|||
|
|
Cupping
|
|||
|
|
Leaches
|
|||
|
|
Maggots
|
|||
|
|
Techniques
|
|||
|
|
Instruments
|
|||
|
|
Department of Curabitur a Purus
|
|||
|
|
Department of Sed Accumsan
|
|||
|
|
Department of Etiam
|
|||
|
|
Research
|
|||
|
|
Administration
|
|||
|
|
Contact us
|
|||
|
|
Impressum
|
|||
|
|
|
|||
|
|
which is frankly overwhelming.
|
|||
|
|
|
|||
|
|
By making “Department of Mediaeval Surgery” a soft root, the menu
|
|||
|
|
becomes much more manageable:
|
|||
|
|
|
|||
|
|
Department of Mediaeval Surgery
|
|||
|
|
Theory
|
|||
|
|
Cures
|
|||
|
|
Bleeding
|
|||
|
|
Introduction to Bleeding <current page>
|
|||
|
|
Bleeding - the scientific evidence
|
|||
|
|
Cleaning up the mess
|
|||
|
|
Cupping
|
|||
|
|
Leaches
|
|||
|
|
Maggots
|
|||
|
|
Techniques
|
|||
|
|
Instruments
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def test_basic_home(self):
|
|||
|
|
"""
|
|||
|
|
Given the tree:
|
|||
|
|
|
|||
|
|
|- Home
|
|||
|
|
| |- Projects (SOFTROOT)
|
|||
|
|
| | |- django CMS
|
|||
|
|
| | |- django Shop
|
|||
|
|
| |- People
|
|||
|
|
|
|||
|
|
Expected menu when on "Home" (0 100 100 100):
|
|||
|
|
|
|||
|
|
|- Home
|
|||
|
|
| |- Projects (SOFTROOT)
|
|||
|
|
| | |- django CMS
|
|||
|
|
| | |- django Shop
|
|||
|
|
| |- People
|
|||
|
|
"""
|
|||
|
|
stdkwargs = {
|
|||
|
|
"template": "nav_playground.html",
|
|||
|
|
"language": "en",
|
|||
|
|
"in_navigation": True,
|
|||
|
|
}
|
|||
|
|
home = create_page("Home", **stdkwargs)
|
|||
|
|
projects = create_page("Projects", parent=home, soft_root=True, **stdkwargs)
|
|||
|
|
djangocms = create_page("django CMS", parent=projects, **stdkwargs)
|
|||
|
|
djangoshop = create_page("django Shop", parent=projects, **stdkwargs)
|
|||
|
|
people = create_page("People", parent=home, **stdkwargs)
|
|||
|
|
# On Home
|
|||
|
|
context = self.get_context(home.get_absolute_url())
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
# check everything
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
homenode = nodes[0]
|
|||
|
|
self.assertEqual(homenode.id, home.pk)
|
|||
|
|
self.assertEqual(len(homenode.children), 2)
|
|||
|
|
projectsnode, peoplenode = homenode.children
|
|||
|
|
self.assertEqual(projectsnode.id, projects.pk)
|
|||
|
|
self.assertEqual(peoplenode.id, people.pk)
|
|||
|
|
self.assertEqual(len(projectsnode.children), 2)
|
|||
|
|
cmsnode, shopnode = projectsnode.children
|
|||
|
|
self.assertEqual(cmsnode.id, djangocms.pk)
|
|||
|
|
self.assertEqual(shopnode.id, djangoshop.pk)
|
|||
|
|
self.assertEqual(len(cmsnode.children), 0)
|
|||
|
|
self.assertEqual(len(shopnode.children), 0)
|
|||
|
|
self.assertEqual(len(peoplenode.children), 0)
|
|||
|
|
|
|||
|
|
def test_basic_projects(self):
|
|||
|
|
"""
|
|||
|
|
Given the tree:
|
|||
|
|
|
|||
|
|
|- Home
|
|||
|
|
| |- Projects (SOFTROOT)
|
|||
|
|
| | |- django CMS
|
|||
|
|
| | |- django Shop
|
|||
|
|
| |- People
|
|||
|
|
|
|||
|
|
Expected menu when on "Projects" (0 100 100 100):
|
|||
|
|
|
|||
|
|
|- Projects (SOFTROOT)
|
|||
|
|
| |- django CMS
|
|||
|
|
| |- django Shop
|
|||
|
|
"""
|
|||
|
|
stdkwargs = {
|
|||
|
|
"template": "nav_playground.html",
|
|||
|
|
"language": "en",
|
|||
|
|
"in_navigation": True,
|
|||
|
|
}
|
|||
|
|
home = create_page("Home", **stdkwargs)
|
|||
|
|
projects = create_page("Projects", parent=home, soft_root=True, **stdkwargs)
|
|||
|
|
djangocms = create_page("django CMS", parent=projects, **stdkwargs)
|
|||
|
|
djangoshop = create_page("django Shop", parent=projects, **stdkwargs)
|
|||
|
|
create_page("People", parent=home, **stdkwargs)
|
|||
|
|
# On Projects
|
|||
|
|
context = self.get_context(projects.get_absolute_url(), page=projects)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
# check everything
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
projectsnode = nodes[0]
|
|||
|
|
self.assertEqual(projectsnode.id, projects.pk)
|
|||
|
|
self.assertEqual(len(projectsnode.children), 2)
|
|||
|
|
cmsnode, shopnode = projectsnode.children
|
|||
|
|
self.assertEqual(cmsnode.id, djangocms.pk)
|
|||
|
|
self.assertEqual(shopnode.id, djangoshop.pk)
|
|||
|
|
self.assertEqual(len(cmsnode.children), 0)
|
|||
|
|
self.assertEqual(len(shopnode.children), 0)
|
|||
|
|
|
|||
|
|
def test_basic_djangocms(self):
|
|||
|
|
"""
|
|||
|
|
Given the tree:
|
|||
|
|
|
|||
|
|
|- Home
|
|||
|
|
| |- Projects (SOFTROOT)
|
|||
|
|
| | |- django CMS
|
|||
|
|
| | |- django Shop
|
|||
|
|
| |- People
|
|||
|
|
|
|||
|
|
Expected menu when on "django CMS" (0 100 100 100):
|
|||
|
|
|
|||
|
|
|- Projects (SOFTROOT)
|
|||
|
|
| |- django CMS
|
|||
|
|
| |- django Shop
|
|||
|
|
"""
|
|||
|
|
stdkwargs = {
|
|||
|
|
"template": "nav_playground.html",
|
|||
|
|
"language": "en",
|
|||
|
|
"in_navigation": True,
|
|||
|
|
}
|
|||
|
|
home = create_page("Home", **stdkwargs)
|
|||
|
|
projects = create_page("Projects", parent=home, soft_root=True, **stdkwargs)
|
|||
|
|
djangocms = create_page("django CMS", parent=projects, **stdkwargs)
|
|||
|
|
djangoshop = create_page("django Shop", parent=projects, **stdkwargs)
|
|||
|
|
create_page("People", parent=home, **stdkwargs)
|
|||
|
|
# On django CMS
|
|||
|
|
context = self.get_context(djangocms.get_absolute_url(), page=djangocms)
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
# check everything
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
projectsnode = nodes[0]
|
|||
|
|
self.assertEqual(projectsnode.id, projects.pk)
|
|||
|
|
self.assertEqual(len(projectsnode.children), 2)
|
|||
|
|
cmsnode, shopnode = projectsnode.children
|
|||
|
|
self.assertEqual(cmsnode.id, djangocms.pk)
|
|||
|
|
self.assertEqual(shopnode.id, djangoshop.pk)
|
|||
|
|
self.assertEqual(len(cmsnode.children), 0)
|
|||
|
|
self.assertEqual(len(shopnode.children), 0)
|
|||
|
|
|
|||
|
|
def test_basic_people(self):
|
|||
|
|
"""
|
|||
|
|
Given the tree:
|
|||
|
|
|
|||
|
|
|- Home
|
|||
|
|
| |- Projects (SOFTROOT)
|
|||
|
|
| | |- django CMS
|
|||
|
|
| | |- django Shop
|
|||
|
|
| |- People
|
|||
|
|
|
|||
|
|
Expected menu when on "People" (0 100 100 100):
|
|||
|
|
|
|||
|
|
|- Home
|
|||
|
|
| |- Projects (SOFTROOT)
|
|||
|
|
| | |- django CMS
|
|||
|
|
| | |- django Shop
|
|||
|
|
| |- People
|
|||
|
|
"""
|
|||
|
|
stdkwargs = {
|
|||
|
|
"template": "nav_playground.html",
|
|||
|
|
"language": "en",
|
|||
|
|
"in_navigation": True,
|
|||
|
|
}
|
|||
|
|
home = create_page("Home", **stdkwargs)
|
|||
|
|
projects = create_page("Projects", parent=home, soft_root=True, **stdkwargs)
|
|||
|
|
djangocms = create_page("django CMS", parent=projects, **stdkwargs)
|
|||
|
|
djangoshop = create_page("django Shop", parent=projects, **stdkwargs)
|
|||
|
|
people = create_page("People", parent=home, **stdkwargs)
|
|||
|
|
# On People
|
|||
|
|
context = self.get_context(home.get_absolute_url())
|
|||
|
|
tpl = Template("{% load menu_tags %}{% show_menu 0 100 100 100 %}")
|
|||
|
|
tpl.render(context)
|
|||
|
|
nodes = context["children"]
|
|||
|
|
# check everything
|
|||
|
|
self.assertEqual(len(nodes), 1)
|
|||
|
|
homenode = nodes[0]
|
|||
|
|
self.assertEqual(homenode.id, home.pk)
|
|||
|
|
self.assertEqual(len(homenode.children), 2)
|
|||
|
|
projectsnode, peoplenode = homenode.children
|
|||
|
|
self.assertEqual(projectsnode.id, projects.pk)
|
|||
|
|
self.assertEqual(peoplenode.id, people.pk)
|
|||
|
|
self.assertEqual(len(projectsnode.children), 2)
|
|||
|
|
cmsnode, shopnode = projectsnode.children
|
|||
|
|
self.assertEqual(cmsnode.id, djangocms.pk)
|
|||
|
|
self.assertEqual(shopnode.id, djangoshop.pk)
|
|||
|
|
self.assertEqual(len(cmsnode.children), 0)
|
|||
|
|
self.assertEqual(len(shopnode.children), 0)
|
|||
|
|
self.assertEqual(len(peoplenode.children), 0)
|