diff --git a/wagtail/wagtailadmin/tests/test_pages_views.py b/wagtail/wagtailadmin/tests/test_pages_views.py
index 3da08de13..6c1f490dd 100644
--- a/wagtail/wagtailadmin/tests/test_pages_views.py
+++ b/wagtail/wagtailadmin/tests/test_pages_views.py
@@ -1,5 +1,5 @@
from django.test import TestCase
-from wagtail.tests.models import SimplePage, EventPage
+from wagtail.tests.models import SimplePage, EventPage, StandardIndex, StandardChild, BusinessIndex, BusinessChild
from wagtail.tests.utils import unittest, WagtailTestUtils
from wagtail.wagtailcore.models import Page, PageRevision
from django.core.urlresolvers import reverse
@@ -262,6 +262,11 @@ class TestPageCreation(TestCase, WagtailTestUtils):
self.assertTemplateUsed(response, 'tests/simple_page.html')
self.assertContains(response, "New page!")
+ # Check that the treebeard attributes were set correctly on the page object
+ self.assertEqual(response.context['self'].depth, self.root_page.depth + 1)
+ self.assertTrue(response.context['self'].path.startswith(self.root_page.path))
+ self.assertEqual(response.context['self'].get_parent(), self.root_page)
+
class TestPageEdit(TestCase, WagtailTestUtils):
def setUp(self):
@@ -759,3 +764,48 @@ class TestContentTypeUse(TestCase, WagtailTestUtils):
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/pages/content_type_use.html')
self.assertContains(response, "Christmas")
+
+
+class TestSubpageBusinessRules(TestCase, WagtailTestUtils):
+ def setUp(self):
+ # Find root page
+ self.root_page = Page.objects.get(id=2)
+
+ # Add standard page
+ self.standard_index = StandardIndex()
+ self.standard_index.title = "Standard Index"
+ self.standard_index.slug = "standard-index"
+ self.root_page.add_child(instance=self.standard_index)
+
+ # Add business page
+ self.business_index = BusinessIndex()
+ self.business_index.title = "Business Index"
+ self.business_index.slug = "business-index"
+ self.root_page.add_child(instance=self.business_index)
+
+ # Add business child
+ self.business_child = BusinessChild()
+ self.business_child.title = "Business Child"
+ self.business_child.slug = "business-child"
+ self.business_index.add_child(instance=self.business_child)
+
+ # Login
+ self.login()
+
+ def test_standard_subpage(self):
+ response = self.client.get(reverse('wagtailadmin_pages_add_subpage', args=(self.standard_index.id, )))
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(response, 'Standard Child')
+ self.assertContains(response, 'Business Child')
+
+ def test_business_subpage(self):
+ response = self.client.get(reverse('wagtailadmin_pages_add_subpage', args=(self.business_index.id, )))
+ self.assertEqual(response.status_code, 200)
+ self.assertNotContains(response, 'Standard Child')
+ self.assertContains(response, 'Business Child')
+
+ def test_business_child_subpage(self):
+ response = self.client.get(reverse('wagtailadmin_pages_add_subpage', args=(self.business_child.id, )))
+ self.assertEqual(response.status_code, 200)
+ self.assertNotContains(response, 'Standard Child')
+ self.assertEqual(0, len(response.context['page_types']))
diff --git a/wagtail/wagtailadmin/views/pages.py b/wagtail/wagtailadmin/views/pages.py
index a611a0647..9a42b7ddf 100644
--- a/wagtail/wagtailadmin/views/pages.py
+++ b/wagtail/wagtailadmin/views/pages.py
@@ -55,13 +55,11 @@ def add_subpage(request, parent_page_id):
if not parent_page.permissions_for_user(request.user).can_add_subpage():
raise PermissionDenied
- page_types = sorted([ContentType.objects.get_for_model(model_class) for model_class in parent_page.clean_subpage_types()], key=lambda pagetype: pagetype.name.lower())
- all_page_types = sorted(get_page_types(), key=lambda pagetype: pagetype.name.lower())
+ page_types = sorted(parent_page.clean_subpage_types(), key=lambda pagetype: pagetype.name.lower())
return render(request, 'wagtailadmin/pages/add_subpage.html', {
'parent_page': parent_page,
'page_types': page_types,
- 'all_page_types': all_page_types,
})
@@ -364,6 +362,10 @@ def preview_on_create(request, content_type_app_name, content_type_model_name, p
parent_page = get_object_or_404(Page, id=parent_page_id).specific
page.set_url_path(parent_page)
+ # Set treebeard attributes
+ page.depth = parent_page.depth + 1
+ page.path = Page._get_children_path_interval(parent_page.path)[1]
+
# This view will generally be invoked as an AJAX request; as such, in the case of
# an error Django will return a plaintext response. This isn't what we want, since
# we will be writing the response back to an HTML page regardless of success or
diff --git a/wagtail/wagtailadmin/views/userbar.py b/wagtail/wagtailadmin/views/userbar.py
index 6e6ef3492..41776f684 100644
--- a/wagtail/wagtailadmin/views/userbar.py
+++ b/wagtail/wagtailadmin/views/userbar.py
@@ -1,9 +1,13 @@
from django.shortcuts import render
+from django.contrib.auth.decorators import permission_required
from wagtail.wagtailadmin.userbar import EditPageItem, AddPageItem, ApproveModerationEditPageItem, RejectModerationEditPageItem
from wagtail.wagtailadmin import hooks
from wagtail.wagtailcore.models import Page, PageRevision
+
+
+@permission_required('wagtailadmin.access_admin')
def for_frontend(request, page_id):
items = [
EditPageItem(Page.objects.get(id=page_id)),
@@ -24,6 +28,8 @@ def for_frontend(request, page_id):
'items': rendered_items,
})
+
+@permission_required('wagtailadmin.access_admin')
def for_moderation(request, revision_id):
items = [
EditPageItem(PageRevision.objects.get(id=revision_id).page),
@@ -44,4 +50,4 @@ def for_moderation(request, revision_id):
# Render the edit bird
return render(request, 'wagtailadmin/userbar/base.html', {
'items': rendered_items,
- })
\ No newline at end of file
+ })
diff --git a/wagtail/wagtailcore/models.py b/wagtail/wagtailcore/models.py
index b7e3d349e..5c3f9d1c5 100644
--- a/wagtail/wagtailcore/models.py
+++ b/wagtail/wagtailcore/models.py
@@ -1,7 +1,6 @@
-import sys
-import os
from StringIO import StringIO
from urlparse import urlparse
+import warnings
from modelcluster.models import ClusterableModel
@@ -101,30 +100,29 @@ def get_page_types():
return _PAGE_CONTENT_TYPES
-LEAF_PAGE_MODEL_CLASSES = []
-_LEAF_PAGE_CONTENT_TYPE_IDS = []
-
-
def get_leaf_page_content_type_ids():
- global _LEAF_PAGE_CONTENT_TYPE_IDS
- if len(_LEAF_PAGE_CONTENT_TYPE_IDS) != len(LEAF_PAGE_MODEL_CLASSES):
- _LEAF_PAGE_CONTENT_TYPE_IDS = [
- ContentType.objects.get_for_model(cls).id for cls in LEAF_PAGE_MODEL_CLASSES
- ]
- return _LEAF_PAGE_CONTENT_TYPE_IDS
-
-
-NAVIGABLE_PAGE_MODEL_CLASSES = []
-_NAVIGABLE_PAGE_CONTENT_TYPE_IDS = []
-
+ warnings.warn("""
+ get_leaf_page_content_type_ids is deprecated, as it treats pages without an explicit subpage_types
+ setting as 'leaf' pages. Code that calls get_leaf_page_content_type_ids must be rewritten to avoid
+ this incorrect assumption.
+ """, DeprecationWarning)
+ return [
+ content_type.id
+ for content_type in get_page_types()
+ if not getattr(content_type.model_class(), 'subpage_types', None)
+ ]
def get_navigable_page_content_type_ids():
- global _NAVIGABLE_PAGE_CONTENT_TYPE_IDS
- if len(_NAVIGABLE_PAGE_CONTENT_TYPE_IDS) != len(NAVIGABLE_PAGE_MODEL_CLASSES):
- _NAVIGABLE_PAGE_CONTENT_TYPE_IDS = [
- ContentType.objects.get_for_model(cls).id for cls in NAVIGABLE_PAGE_MODEL_CLASSES
- ]
- return _NAVIGABLE_PAGE_CONTENT_TYPE_IDS
+ warnings.warn("""
+ get_navigable_page_content_type_ids is deprecated, as it treats pages without an explicit subpage_types
+ setting as 'leaf' pages. Code that calls get_navigable_page_content_type_ids must be rewritten to avoid
+ this incorrect assumption.
+ """, DeprecationWarning)
+ return [
+ content_type.id
+ for content_type in get_page_types()
+ if getattr(content_type.model_class(), 'subpage_types', None)
+ ]
class PageManager(models.Manager):
@@ -209,10 +207,6 @@ class PageBase(models.base.ModelBase):
if not cls.is_abstract:
# register this type in the list of page content types
PAGE_MODEL_CLASSES.append(cls)
- if cls.subpage_types:
- NAVIGABLE_PAGE_MODEL_CLASSES.append(cls)
- else:
- LEAF_PAGE_MODEL_CLASSES.append(cls)
class Page(MP_Node, ClusterableModel, Indexed):
@@ -259,9 +253,6 @@ class Page(MP_Node, ClusterableModel, Indexed):
def __unicode__(self):
return self.title
- # by default pages do not allow any kind of subpages
- subpage_types = []
-
is_abstract = True # don't offer Page in the list of page types a superuser can create
def set_url_path(self, parent):
@@ -416,10 +407,10 @@ class Page(MP_Node, ClusterableModel, Indexed):
def is_navigable(self):
"""
Return true if it's meaningful to browse subpages of this page -
- i.e. it currently has subpages, or its page type indicates that sub-pages are supported,
+ i.e. it currently has subpages,
or it's at the top level (this rule necessary for empty out-of-the-box sites to have working navigation)
"""
- return (not self.is_leaf()) or (self.content_type_id not in get_leaf_page_content_type_ids()) or self.depth == 2
+ return (not self.is_leaf()) or self.depth == 2
def get_other_siblings(self):
# get sibling pages excluding self
@@ -484,25 +475,30 @@ class Page(MP_Node, ClusterableModel, Indexed):
where required
"""
if cls._clean_subpage_types is None:
- res = []
- for page_type in cls.subpage_types:
- if isinstance(page_type, basestring):
- try:
- app_label, model_name = page_type.split(".")
- except ValueError:
- # If we can't split, assume a model in current app
- app_label = cls._meta.app_label
- model_name = page_type
+ subpage_types = getattr(cls, 'subpage_types', None)
+ if subpage_types is None:
+ # if subpage_types is not specified on the Page class, allow all page types as subpages
+ res = get_page_types()
+ else:
+ res = []
+ for page_type in cls.subpage_types:
+ if isinstance(page_type, basestring):
+ try:
+ app_label, model_name = page_type.split(".")
+ except ValueError:
+ # If we can't split, assume a model in current app
+ app_label = cls._meta.app_label
+ model_name = page_type
+
+ model = get_model(app_label, model_name)
+ if model:
+ res.append(ContentType.objects.get_for_model(model))
+ else:
+ raise NameError(_("name '{0}' (used in subpage_types list) is not defined.").format(page_type))
- model = get_model(app_label, model_name)
- if model:
- res.append(model)
else:
- raise NameError(_("name '{0}' (used in subpage_types list) is not defined.").format(page_type))
-
- else:
- # assume it's already a model class
- res.append(page_type)
+ # assume it's already a model class
+ res.append(ContentType.objects.get_for_model(page_type))
cls._clean_subpage_types = res
@@ -652,13 +648,8 @@ class Page(MP_Node, ClusterableModel, Indexed):
def get_navigation_menu_items():
# Get all pages that appear in the navigation menu: ones which have children,
- # or are a non-leaf type (indicating that they *could* have children),
# or are at the top-level (this rule required so that an empty site out-of-the-box has a working menu)
- navigable_content_type_ids = get_navigable_page_content_type_ids()
- if navigable_content_type_ids:
- pages = Page.objects.filter(Q(content_type__in=navigable_content_type_ids)|Q(depth=2)|Q(numchild__gt=0)).order_by('path')
- else:
- pages = Page.objects.filter(Q(depth=2)|Q(numchild__gt=0)).order_by('path')
+ pages = Page.objects.filter(Q(depth=2)|Q(numchild__gt=0)).order_by('path')
# Turn this into a tree structure:
# tree_node = (page, children)