Merge branch 'master' into tests-changes2

This commit is contained in:
Karl Hobley 2014-06-18 15:49:26 +01:00
commit e76785d96d
8 changed files with 126 additions and 62 deletions

View file

@ -8,6 +8,7 @@ Changelog
* Snippets are now ordered alphabetically
* Removed the "More" section from the admin menu
* Added pagination to page listings in admin
* Support for setting a subpage_types property on page models, to define which page types are allowed as subpages
* Fix: Animated GIFs are now coalesced before resizing
* Fix: Wand backend clones images before modifying them
* Fix: Admin breadcrumb now positioned correctly on mobile

View file

@ -28,6 +28,7 @@ Contributors
* Ben Margolis
* Tom Talbot
* Jeffrey Hearn
* Robert Clark
Translators
===========

View file

@ -294,3 +294,16 @@ class ZuluSnippet(models.Model):
def __unicode__(self):
return self.text
class StandardIndex(Page):
pass
class StandardChild(Page):
pass
class BusinessIndex(Page):
subpage_types = ['tests.BusinessChild']
class BusinessChild(Page):
subpage_types = []

View file

@ -12,9 +12,9 @@
<div class="nice-padding">
<p>{% trans "Choose which type of page you'd like to create." %}</p>
{% if all_page_types %}
{% if page_types %}
<ul class="listing">
{% for content_type in all_page_types %}
{% for content_type in page_types %}
<li>
<div class="row row-flush">
<div class="col6">

View file

@ -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']))

View file

@ -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

View file

@ -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,
})
})

View file

@ -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)