diff --git a/.coveragerc b/.coveragerc
index 7f6f849cd..b49e59cd5 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -5,6 +5,7 @@ branch = True
source = wagtail
omit =
+ */south_migrations/*
*/migrations/*
wagtail/vendor/*
@@ -31,4 +32,4 @@ exclude_lines =
ignore_errors = True
[html]
-directory = coverage_html_report
\ No newline at end of file
+directory = coverage_html_report
diff --git a/.travis.yml b/.travis.yml
index 2245157e7..f54d1392e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,29 +1,38 @@
language: python
-# Test matrix
-python:
- - 2.7
- - 3.2
- - 3.4
env:
- - DJANGO_VERSION=Django==1.6.5
- #- DJANGO_VERSION=Django==1.7.0
+# - TOXENV=py26-dj16-postgres
+ - TOXENV=py26-dj16-sqlite
+ - TOXENV=py27-dj16-postgres
+# - TOXENV=py27-dj16-sqlite
+ - TOXENV=py32-dj16-postgres
+# - TOXENV=py33-dj16-postgres
+ - TOXENV=py34-dj16-postgres
+ - TOXENV=py27-dj17-postgres
+# - TOXENV=py27-dj17-sqlite
+# - TOXENV=py32-dj17-postgres
+# - TOXENV=py33-dj17-postgres
+ - TOXENV=py34-dj17-postgres
+
# Services
services:
- redis-server
- elasticsearch
+
# Package installation
install:
- - python setup.py install
- - pip install psycopg2 elasticsearch wand embedly mock python-dateutil
- - pip install coveralls
+ - pip install tox coveralls
+
# Pre-test configuration
before_script:
- psql -c 'create database wagtaildemo;' -U postgres
+
# Run the tests
script:
- coverage run runtests.py
+ tox
+
after_success:
coveralls
+
# Who to notify about build results
notifications:
email:
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 7530812f6..5122b8d1d 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -3,8 +3,12 @@ Changelog
0.6 (xx.xx.20xx)
~~~~~~~~~~~~~~~~
+ * Added Django 1.7 support
* Added {% routablepageurl %} template tag (@timheap)
+ * Added RoutablePageMixin (@timheap)
+ * Renamed wagtailsearch.indexed to wagtailsearch.index
* Fix: Page URL generation now returns correct URLs for sites that have the main 'serve' view rooted somewhere other than '/'
+ * Fix: Search results in the page chooser now respect the page_type parameter on PageChooserPanel
0.5 (01.08.2014)
~~~~~~~~~~~~~~~~
diff --git a/README.rst b/README.rst
index 5fa7f47f9..77d153f26 100644
--- a/README.rst
+++ b/README.rst
@@ -24,8 +24,8 @@ Wagtail is a Django content management system built originally for the `Royal Co
* Support for tree-based content organisation
* Optional preview->submit->approve workflow
* Fast out of the box. `Varnish `_-friendly if you need it
-* A simple `form builder `_
-* Optional `static site generation `_
+* A simple `form builder `_
+* Optional `static site generation `_
* Excellent `test coverage `_
Find out more at `wagtail.io `_.
@@ -45,9 +45,7 @@ Available at `wagtail.readthedocs.org `_ and al
Compatibility
~~~~~~~~~~~~~
-Wagtail supports Django 1.6.2+ on Python 2.6, 2.7, 3.2, 3.3 and 3.4.
-
-Django 1.7 support is in progress pending further release candidate testing.
+Wagtail supports Django 1.6.2+ and 1.7rc3+ on Python 2.6, 2.7, 3.2, 3.3 and 3.4.
Wagtail's dependencies are summarised at `requirements.io `_.
@@ -55,8 +53,8 @@ Contributing
~~~~~~~~~~~~
If you're a Python or Django developer, fork the repo and get stuck in!
-We suggest you start by checking the `Help develop me! `_ label and the `coding guidelines `_.
+We suggest you start by checking the `Help develop me! `_ label and the `coding guidelines `_.
Send us a useful pull request and we'll post you a `t-shirt `_.
-We also welcome `translations `_ for Wagtail's interface.
+We also welcome `translations `_ for Wagtail's interface.
diff --git a/docs/core_components/images/using_images_outside_wagtail.rst b/docs/core_components/images/using_images_outside_wagtail.rst
index 5671e8504..d3b63126b 100644
--- a/docs/core_components/images/using_images_outside_wagtail.rst
+++ b/docs/core_components/images/using_images_outside_wagtail.rst
@@ -71,7 +71,7 @@ Using the URLs on your website or blog
Once you have generated a URL, you can put it straight into the ``src`` attribute of an ``
`` tag:
-..code-block:: html
+ .. code-block:: html
diff --git a/docs/core_components/index.rst b/docs/core_components/index.rst
index cfa67d366..a28ddf1b7 100644
--- a/docs/core_components/index.rst
+++ b/docs/core_components/index.rst
@@ -4,6 +4,7 @@ Core components
.. toctree::
:maxdepth: 2
+ sites
pages/index
images/index
snippets
diff --git a/docs/core_components/pages/advanced_topics/routable_page.rst b/docs/core_components/pages/advanced_topics/routable_page_mixin.rst
similarity index 64%
rename from docs/core_components/pages/advanced_topics/routable_page.rst
rename to docs/core_components/pages/advanced_topics/routable_page_mixin.rst
index 465b0b148..36ca024c3 100644
--- a/docs/core_components/pages/advanced_topics/routable_page.rst
+++ b/docs/core_components/pages/advanced_topics/routable_page_mixin.rst
@@ -1,4 +1,4 @@
-.. _routable_page:
+.. _routable_page_mixin:
====================================
Embedding URL configuration in Pages
@@ -6,15 +6,15 @@ Embedding URL configuration in Pages
.. versionadded:: 0.5
-The ``RoutablePage`` class provides a convenient way for a page to respond on multiple sub-URLs with different views. For example, a blog section on a site might provide several different types of index page at URLs like ``/blog/2013/06/``, ``/blog/authors/bob/``, ``/blog/tagged/python/``, all served by the same ``BlogIndex`` page.
+The ``RoutablePageMixin`` mixin provides a convenient way for a page to respond on multiple sub-URLs with different views. For example, a blog section on a site might provide several different types of index page at URLs like ``/blog/2013/06/``, ``/blog/authors/bob/``, ``/blog/tagged/python/``, all served by the same ``BlogIndex`` page.
-A ``RoutablePage`` exists within the page tree like any other page, but URL paths underneath it are checked against a list of patterns, using Django's urlconf scheme. If none of the patterns match, control is passed to subpages as usual (or failing that, a 404 error is thrown).
+A ``Page`` using ``RoutablePageMixin`` exists within the page tree like any other page, but URL paths underneath it are checked against a list of patterns, using Django's urlconf scheme. If none of the patterns match, control is passed to subpages as usual (or failing that, a 404 error is thrown).
The basics
==========
-To use ``RoutablePage``, you need to make your class inherit from :class:`wagtail.contrib.wagtailroutablepage.models.RoutablePage` and configure the ``subpage_urls`` attribute with your URL configuration.
+To use ``RoutablePageMixin``, you need to make your class inherit from both :class:`wagtail.contrib.wagtailroutablepage.models.RoutablePageMixin` and :class:`wagtail.wagtailcore.models.Page`, and configure the ``subpage_urls`` attribute with your URL configuration.
Here's an example of an ``EventPage`` with three views:
@@ -22,10 +22,11 @@ Here's an example of an ``EventPage`` with three views:
from django.conf.urls import url
- from wagtail.contrib.wagtailroutablepage.models import RoutablePage
+ from wagtail.contrib.wagtailroutablepage.models import RoutablePageMixin
+ from wagtail.wagtailcore.models import Page
- class EventPage(RoutablePage):
+ class EventPage(RoutablePageMixin, Page):
subpage_urls = (
url(r'^$', 'current_events', name='current_events'),
url(r'^past/$', 'past_events', name='past_events'),
@@ -40,7 +41,7 @@ Here's an example of an ``EventPage`` with three views:
def past_events(self, request):
"""
- View function for the current events page
+ View function for the past events page
"""
...
@@ -51,11 +52,11 @@ Here's an example of an ``EventPage`` with three views:
...
-The ``RoutablePage`` class
-==========================
+The ``RoutablePageMixin`` class
+===============================
.. automodule:: wagtail.contrib.wagtailroutablepage.models
-.. autoclass:: RoutablePage
+.. autoclass:: RoutablePageMixin
.. autoattribute:: subpage_urls
@@ -65,7 +66,9 @@ The ``RoutablePage`` class
from django.conf.urls import url
- class MyPage(RoutablePage):
+ from wagtail.wagtailcore.models import Page
+
+ class MyPage(RoutablePageMixin, Page):
subpage_urls = (
url(r'^$', 'serve', name='main'),
url(r'^archive/$', 'archive', name='archive'),
diff --git a/docs/core_components/pages/creating_pages.rst b/docs/core_components/pages/creating_pages.rst
index 7806274fa..e6fb9a14b 100644
--- a/docs/core_components/pages/creating_pages.rst
+++ b/docs/core_components/pages/creating_pages.rst
@@ -2,40 +2,15 @@
Creating page models
====================
+Wagtail provides a ``Page`` class to represent types of page (a.k.a Content types). Developers should subclass ``Page`` for their own page models.
-The Page Class
-~~~~~~~~~~~~~~
-
-``Page`` uses Django's model interface, so you can include any field type and field options that Django allows. Wagtail provides some fields and editing handlers that simplify data entry in the Wagtail admin interface, so you may want to keep those in mind when deciding what properties to add to your models in addition to those already provided by ``Page``.
+``Page`` uses Django's model interface, so you can include any field type and field options that Django allows. Wagtail provides some field types of its own which simplify data entry in the Wagtail admin interface. Wagtail also wraps Django's field types and widgets with its own concept of "Edit Handlers" and "Panels". These further improve the data entry experience.
-Built-in Properties of the Page Class
--------------------------------------
+An example Wagtail Page Model
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Wagtail provides some properties in the ``Page`` class which are common to most webpages. Since you'll be subclassing ``Page``, you don't have to worry about implementing them.
-
-Public Properties
-`````````````````
-
- ``title`` (string, required)
- Human-readable title for the content
-
- ``slug`` (string, required)
- Machine-readable URL component for this piece of content. The name of the page as it will appear in URLs e.g ``http://domain.com/blog/[my-slug]/``
-
- ``seo_title`` (string)
- Alternate SEO-crafted title which overrides the normal title for use in the ```` of a page
-
- ``search_description`` (string)
- A SEO-crafted description of the content, used in both internal search indexing and for the meta description read by search engines
-
-The ``Page`` class actually has alot more to it, but these are probably the only built-in properties you'll need to worry about when creating templates for your models.
-
-
-Anatomy of a Wagtail Model
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-So what does a Wagtail model definition look like? Here's a model representing a typical blog post:
+This example represents a typical blog post:
.. code-block:: python
@@ -58,6 +33,8 @@ So what does a Wagtail model definition look like? Here's a model representing a
related_name='+'
)
+
+
BlogPage.content_panels = [
FieldPanel('title', classname="full title"),
FieldPanel('date'),
@@ -72,17 +49,48 @@ So what does a Wagtail model definition look like? Here's a model representing a
ImageChooserPanel('feed_image'),
]
-To keep track of your ``Page``-derived models, it might be helpful to include "Page" as the last part of your class name. ``BlogPage`` defines three properties: ``body``, ``date``, and ``feed_image``. These are a mix of basic Django models (``DateField``), Wagtail fields (``RichTextField``), and a pointer to a Wagtail model (``Image``).
+.. tip::
+ To keep track of ``Page`` models and avoid class name clashes, it can be helpful to suffix model class names with "Page" e.g BlogPage, ListingIndexPage.
-Next, the ``content_panels`` and ``promote_panels`` lists define the capabilities and layout of the Wagtail admin page edit interface. The lists are filled with "panels" and "choosers", which will provide a fine-grain interface for inputting the model's content. The ``ImageChooserPanel``, for instance, lets one browse the image library, upload new images, and input image metadata. The ``RichTextField`` is the basic field for creating web-ready website rich text, including text formatting and embedded media like images and video. The Wagtail admin offers other choices for fields, Panels, and Choosers, with the option of creating your own to precisely fit your content without workarounds or other compromises.
+In the example above the ``BlogPage`` class defines three properties: ``body``, ``date``, and ``feed_image``. These are a mix of basic Django models (``DateField``), Wagtail fields (``RichTextField``), and a pointer to a Wagtail model (``Image``).
+
+Below that the ``content_panels`` and ``promote_panels`` lists define the capabilities and layout of the page editing interface in the Wagtail admin. The lists are filled with "panels" and "choosers", which will provide a fine-grain interface for inputting the model's content. The ``ImageChooserPanel``, for instance, lets one browse the image library, upload new images and input image metadata. The ``RichTextField`` is the basic field for creating web-ready website rich text, including text formatting and embedded media like images and video. The Wagtail admin offers other choices for fields, Panels, and Choosers, with the option of creating your own to precisely fit your content without workarounds or other compromises.
Your models may be even more complex, with methods overriding the built-in functionality of the ``Page`` to achieve webdev magic. Or, you can keep your models simple and let Wagtail's built-in functionality do the work.
-Now that we have a basic idea of how our content is defined, lets look at relationships between pieces of content.
+
+``Page`` Class Reference
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Default fields
+--------------
+
+Wagtail provides some fields for the ``Page`` class by default, which will be common to all your pages. You don't need to add these fields to your own page models however you do need to allocate them to ``content_panels``, ``promote_panels`` or ``settings_panels``. See above example and for further information on the panels see :ref:`editing-api`.
+
+ ``title`` (string, required)
+ Human-readable title of the page - what you'd probably use as your ```` tag.
+
+ ``slug`` (string, required)
+ Machine-readable URL component for this page. The name of the page as it will appear in URLs e.g ``http://domain.com/blog/[my-slug]/``
+
+ ``seo_title`` (string)
+ Alternate SEO-crafted title, mainly for use in the page ```` tag.
+
+ ``search_description`` (string)
+ SEO-crafted description of the content, used for internal search indexing, suitable for your page's ```` tag.
+
+ ``show_in_menus`` (boolean)
+ Toggles whether the page should be considered for inclusion in any site-wide menus you create.
+
+ ``go_live_at`` (datetime)
+ A datetime on which the page should be automatically made published (made publicly accessible)
+
+ ``expire_at`` (datetime)
+ A datetime on which the page should be unpublished, rendering it inaccessible to the public.
-Page Properties and Methods Reference
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Page Attributes, Properties and Methods Reference
+-------------------------------------------------
In addition to the model fields provided, ``Page`` has many properties and methods that you may wish to reference, use, or override in creating your own models. Those listed here are relatively straightforward to use, but consult the Wagtail source code for a full view of what's possible.
@@ -121,14 +129,21 @@ In addition to the model fields provided, ``Page`` has many properties and metho
.. automethod:: search
+ .. attribute:: search_fields
+
+ A list of fields to be indexed by the search engine. See Search docs :ref:`wagtailsearch_for_python_developers`
-.. _wagtail_site_admin:
+ .. attribute:: subpage_types
-Site
-~~~~
+ A whitelist of page models which can be created as children of this page type e.g a ``BlogIndex`` page might allow ``BlogPage``, but not ``JobPage`` e.g
-Django's built-in admin interface provides the way to map a "site" (hostname or domain) to any node in the wagtail tree, using that node as the site's root.
+ .. code-block:: python
+
+ class BlogIndex(Page):
+ subpage_types = ['mysite.BlogPage', 'mysite.BlogArchivePage']
+
+ .. attribute:: password_required_template
+
+ Defines which template file should be used to render the login form for Protected pages using this model. This overrides the default, defined using ``PASSWORD_REQUIRED_TEMPLATE`` in your settings. See :ref:`private_pages`
-Access this by going to ``/django-admin/`` and then "Home › Wagtailcore › Sites." To try out a development site, add a single site with the hostname ``localhost`` at port ``8000`` and map it to one of the pieces of content you have created.
-Wagtail's developers plan to move the site settings into the Wagtail admin interface.
diff --git a/docs/core_components/pages/editing_api.rst b/docs/core_components/pages/editing_api.rst
index 4d346a815..f1025cbcb 100644
--- a/docs/core_components/pages/editing_api.rst
+++ b/docs/core_components/pages/editing_api.rst
@@ -1,3 +1,4 @@
+.. _editing-api:
Defining models with the Editing API
====================================
diff --git a/docs/core_components/pages/index.rst b/docs/core_components/pages/index.rst
index 1f350f35c..70d20d116 100644
--- a/docs/core_components/pages/index.rst
+++ b/docs/core_components/pages/index.rst
@@ -21,4 +21,4 @@ The presentation of your content, the actual webpages, includes the normal use o
editing_api
advanced_topics/queryset_methods
advanced_topics/private_pages
- advanced_topics/routable_page
+ advanced_topics/routable_page_mixin
diff --git a/docs/core_components/pages/theory.rst b/docs/core_components/pages/theory.rst
index 272ca3d1d..bcea079bb 100644
--- a/docs/core_components/pages/theory.rst
+++ b/docs/core_components/pages/theory.rst
@@ -1,3 +1,5 @@
+.. _pages-theory:
+
======
Theory
======
diff --git a/docs/core_components/search/for_python_developers.rst b/docs/core_components/search/for_python_developers.rst
index b99795e7b..b254a2e1f 100644
--- a/docs/core_components/search/for_python_developers.rst
+++ b/docs/core_components/search/for_python_developers.rst
@@ -10,6 +10,8 @@ For Python developers
Basic usage
===========
+By default using the :ref:`wagtailsearch_backends_database`, Wagtail's search will only index the ``title`` field of pages.
+
All searches are performed on Django QuerySets. Wagtail provides a ``search`` method on the queryset for all page models:
.. code-block:: python
@@ -35,20 +37,20 @@ Indexing extra fields
The ``indexed_fields`` configuration format was replaced with ``search_fields``
+.. versionchanged:: 0.6
-.. note::
-
- Searching on extra fields with the database backend is not currently supported.
+ The ``wagtail.wagtailsearch.indexed`` module was renamed to ``wagtail.wagtailsearch.index``
-Fields need to be explicitly added to the search configuration in order for you to be able to search/filter on them.
+.. warning::
-You can add new fields to the search index by overriding the ``search_fields`` property and appending a list of extra ``SearchField``/``FilterField`` objects to it.
-
-The default value of ``search_fields`` (as set in ``Page``) indexes the ``title`` field as a ``SearchField`` and some other generally useful fields as ``FilterField`` rules.
+ Indexing extra fields is only supported with ElasticSearch as your backend. If you're using the database backend, any other fields you define via ``search_fields`` will be ignored.
-Quick example
+Fields must be explicitly added to the ``search_fields`` property of your ``Page``-derived model, in order for you to be able to search/filter on them. This is done by overriding ``search_fields`` to append a list of extra ``SearchField``/``FilterField`` objects to it.
+
+
+Example
-------------
This creates an ``EventPage`` model with two fields ``description`` and ``date``. ``description`` is indexed as a ``SearchField`` and ``date`` is indexed as a ``FilterField``
@@ -56,15 +58,15 @@ This creates an ``EventPage`` model with two fields ``description`` and ``date``
.. code-block:: python
- from wagtail.wagtailsearch import indexed
+ from wagtail.wagtailsearch import index
class EventPage(Page):
description = models.TextField()
date = models.DateField()
search_fields = Page.search_fields + ( # Inherit search_fields from Page
- indexed.SearchField('description'),
- indexed.FilterField('date'),
+ index.SearchField('description'),
+ index.FilterField('date'),
)
@@ -72,7 +74,7 @@ This creates an ``EventPage`` model with two fields ``description`` and ``date``
>>> EventPage.objects.filter(date__gt=timezone.now()).search("Christmas")
-``indexed.SearchField``
+``index.SearchField``
-----------------------
These are added to the search index and are used for performing full-text searches on your models. These would usually be text fields.
@@ -86,7 +88,7 @@ Options
- **es_extra** (dict) - This field is to allow the developer to set or override any setting on the field in the ElasticSearch mapping. Use this if you want to make use of any ElasticSearch features that are not yet supported in Wagtail.
-``indexed.FilterField``
+``index.FilterField``
-----------------------
These are added to the search index but are not used for full-text searches. Instead, they allow you to run filters on your search results.
@@ -107,7 +109,7 @@ One use for this is indexing ``get_*_display`` methods Django creates automatica
.. code-block:: python
- from wagtail.wagtailsearch import indexed
+ from wagtail.wagtailsearch import index
class EventPage(Page):
IS_PRIVATE_CHOICES = (
@@ -119,10 +121,10 @@ One use for this is indexing ``get_*_display`` methods Django creates automatica
search_fields = Page.search_fields + (
# Index the human-readable string for searching
- indexed.SearchField('get_is_private_display'),
+ index.SearchField('get_is_private_display'),
# Index the boolean value for filtering
- indexed.FilterField('is_private'),
+ index.FilterField('is_private'),
)
@@ -131,25 +133,25 @@ Indexing non-page models
Any Django model can be indexed and searched.
-To do this, inherit from ``indexed.Indexed`` and add some ``search_fields`` to the model.
+To do this, inherit from ``index.Indexed`` and add some ``search_fields`` to the model.
.. code-block:: python
- from wagtail.wagtailsearch import indexed
+ from wagtail.wagtailsearch import index
- class Book(models.Model, indexed.Indexed):
+ class Book(models.Model, index.Indexed):
title = models.CharField(max_length=255)
genre = models.CharField(max_length=255, choices=GENRE_CHOICES)
author = models.ForeignKey(Author)
published_date = models.DateTimeField()
search_fields = (
- indexed.SearchField('title', partial_match=True, boost=10),
- indexed.SearchField('get_genre_display'),
+ index.SearchField('title', partial_match=True, boost=10),
+ index.SearchField('get_genre_display'),
- indexed.FilterField('genre'),
- indexed.FilterField('author'),
- indexed.FilterField('published_date'),
+ index.FilterField('genre'),
+ index.FilterField('author'),
+ index.FilterField('published_date'),
)
# As this model doesn't have a search method in its QuerySet, we have to call search directly on the backend
diff --git a/docs/core_components/sites.rst b/docs/core_components/sites.rst
new file mode 100644
index 000000000..89c490b65
--- /dev/null
+++ b/docs/core_components/sites.rst
@@ -0,0 +1,10 @@
+.. _wagtail_site_admin:
+
+Sites
+=====
+
+Django's built-in admin interface provides the way to map a "site" (hostname or domain) to any node in the wagtail tree, using that node as the site's root. See :ref:`pages-theory`.
+
+Access this by going to ``/django-admin/`` and then "Home › Wagtailcore › Sites." To try out a development site, add a single site with the hostname ``localhost`` at port ``8000`` and map it to one of the pieces of content you have created.
+
+Wagtail's developers plan to move the site settings into the Wagtail admin interface.
diff --git a/docs/howto/index.rst b/docs/howto/index.rst
index 01ce20d13..c9826e4d5 100644
--- a/docs/howto/index.rst
+++ b/docs/howto/index.rst
@@ -8,4 +8,5 @@ How to
settings
deploying
performance
+ multilingual_sites
contributing
diff --git a/docs/howto/multilingual_sites.rst b/docs/howto/multilingual_sites.rst
new file mode 100644
index 000000000..1d73a18fb
--- /dev/null
+++ b/docs/howto/multilingual_sites.rst
@@ -0,0 +1,146 @@
+===========================
+Creating multilingual sites
+===========================
+
+This tutorial will show you a method of creating multilingual sites in Wagtail.
+
+Currently, Wagtail doesn't support multiple languages in the same page. The recommended way of creating multilingual websites in Wagtail at the moment is to create one section of your website for each language.
+
+For example::
+
+ /
+ en/
+ about/
+ contact/
+ fr/
+ about/
+ contact/
+
+
+The root page
+=============
+
+The root page (``/``) should detect the browsers language and forward them to the correct language homepage (``/en/``, ``/fr/``). This page should sit at the site root (where the homepage would normally be).
+
+We must set Djangos ``LANGUAGES`` setting so we don't redirect non English/French users to pages that don't exist.
+
+
+.. code-block:: python
+
+ # settings.py
+ LANGUAGES = (
+ ('en', _("English")),
+ ('fr', _("French")),
+ )
+
+ # models.py
+ from django.utils import translation
+ from django.http import HttpResponseRedirect
+
+ from wagtail.wagtailcore.models import Page
+
+
+ class LanguageRedirectionPage(Page):
+
+ def serve(self, request):
+ # This will only return a language that is in the LANGUAGES Django setting
+ language = translation.get_language_from_request(request)
+
+ return HttpResponseRedirect(self.url + language + '/')
+
+
+Linking pages together
+======================
+
+It may be useful to link different versions of the same page together to allow the user to easily switch between languages. But we don't want to increse the burdon on the editor too much so ideally, editors should only need to link one of the pages to the other versions and the links between the other versions should be created implicitly.
+
+As this behaviour needs to be added to all page types that would be translated, its best to put this behaviour in a mixin.
+
+Heres an example of how this could be implemented (with English as the main language and French/Spanish as alternative languages):
+
+.. code-block:: python
+
+ class TranslatablePageMixin(models.Model):
+ # One link for each alternative language
+ # These should only be used on the main language page (english)
+ french_link = models.ForeignKey(Page, null=True, on_delete=models.SET_NULL, blank=True, related_name='+')
+ spanish_link = models.ForeignKey(Page, null=True, on_delete=models.SET_NULL, blank=True, related_name='+')
+
+ def get_language(self):
+ """
+ This returns the language code for this page.
+ """
+ # Look through ancestors of this page for its language homepage
+ # The language homepage is located at depth 3
+ language_homepage = self.get_ancestors(inclusive=True).get(depth=3)
+
+ # The slug of language homepages should always be set to the language code
+ return language_homepage.slug
+
+
+ # Method to find the main language version of this page
+ # This works by reversing the above links
+
+ def english_page(self):
+ """
+ This finds the english version of this page
+ """
+ language = self.get_language()
+
+ if language == 'en':
+ return self
+ elif language == 'fr':
+ return type(self).objects.filter(french_link=self).first().specific
+ elif language == 'es':
+ return type(self).objects.filter(spanish_link=self).first().specific
+
+
+ # We need a method to find a version of this page for each alternative language.
+ # These all work the same way. They firstly find the main version of the page
+ # (english), then from there they can just follow the link to the correct page.
+
+ def french_page(self):
+ """
+ This finds the french version of this page
+ """
+ english_page = self.english_page()
+
+ if english_page and english_page.french_link:
+ return english_page.french_link.specific
+
+ def spanish_page(self):
+ """
+ This finds the spanish version of this page
+ """
+ english_page = self.english_page()
+
+ if english_page and english_page.spanish_link:
+ return english_page.spanish_link.specific
+
+ class Meta:
+ abstract = True
+
+
+ class AboutPage(Page, TranslatablePageMixin):
+ ...
+
+
+ class ContactPage(Page, TranslatablePageMixin):
+ ...
+
+
+You can make use of these methods in your template by doing:
+
+.. code-block:: django
+
+ {% if self.english_page and self.get_language != 'en' %}
+ {% trans "View in English" %}
+ {% endif %}
+
+ {% if self.french_page and self.get_language != 'fr' %}
+ {% trans "View in French" %}
+ {% endif %}
+
+ {% if self.spanish_page and self.get_language != 'es' %}
+ {% trans "View in Spanish" %}
+ {% endif %}
diff --git a/docs/index.rst b/docs/index.rst
index 0ce7b02fa..55575fbff 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -3,7 +3,7 @@ Welcome to Wagtail's documentation
Wagtail is a modern, flexible CMS, built on Django.
-It supports Django 1.6.2+ on Python 2.6, 2.7, 3.2, 3.3 and 3.4. Django 1.7 support is in progress pending further release candidate testing.
+It supports Django 1.6.2+ and 1.7rc3+ on Python 2.6, 2.7, 3.2, 3.3 and 3.4.
.. toctree::
:maxdepth: 3
diff --git a/docs/releases/0.5.rst b/docs/releases/0.5.rst
index 1e440e27f..6f4912de5 100644
--- a/docs/releases/0.5.rst
+++ b/docs/releases/0.5.rst
@@ -37,7 +37,7 @@ RoutablePage
A ``RoutablePage`` model has been added to allow embedding Django-style URL routing within a page.
-:ref:`routable_page`
+:ref:`routable_page_mixin`
Usage stats for images, documents and snippets
@@ -129,4 +129,4 @@ South upgraded to 1.0
In preparation for Django 1.7 support in a future release, Wagtail now depends on South 1.0, and its migration files have been moved from ``migrations`` to ``south_migrations``. Older versions of South will fail to find the migrations in the new location.
-If your project's requirements file (most commonly requirements.txt or requirements/base.txt) references a specific older version of South, this must be updated to South 1.0.
\ No newline at end of file
+If your project's requirements file (most commonly requirements.txt or requirements/base.txt) references a specific older version of South, this must be updated to South 1.0.
diff --git a/docs/releases/0.6.rst b/docs/releases/0.6.rst
index 5ab1769da..a5fbad591 100644
--- a/docs/releases/0.6.rst
+++ b/docs/releases/0.6.rst
@@ -10,14 +10,29 @@ Wagtail 0.6 release notes - IN DEVELOPMENT
What's new
==========
+Django 1.7 support
+~~~~~~~~~~~~~~~~~~
+
+Wagtail can now be used with Django 1.7.
+
+
Minor features
~~~~~~~~~~~~~~
* A new template tag has been added for reversing URLs inside routable pages. See :ref:`routablepageurl_template_tag`.
+ * RoutablePage can now be used as a mixin. See :class:`wagtail.contrib.wagtailroutablepage.models.RoutablePageMixin`.
Bug fixes
~~~~~~~~~
* Page URL generation now returns correct URLs for sites that have the main 'serve' view rooted somewhere other than '/'.
+ * Search results in the page chooser now respect the page_type parameter on PageChooserPanel.
Upgrade considerations
======================
+
+
+Deprecated features
+===================
+
+ * The ``wagtail.wagtailsearch.indexed`` module has been renamed to ``wagtail.wagtailsearch.index``
+
\ No newline at end of file
diff --git a/runtests.py b/runtests.py
index 3a7399233..7961d2e19 100755
--- a/runtests.py
+++ b/runtests.py
@@ -2,6 +2,7 @@
import sys
import os
import shutil
+import warnings
from django.core.management import execute_from_command_line
@@ -12,6 +13,9 @@ os.environ['DJANGO_SETTINGS_MODULE'] = 'wagtail.tests.settings'
def runtests():
+ # Don't ignore DeprecationWarnings
+ warnings.simplefilter('default', DeprecationWarning)
+
argv = sys.argv[:1] + ['test'] + sys.argv[1:]
try:
execute_from_command_line(argv)
diff --git a/setup.py b/setup.py
index 4d22021ed..c722911ad 100644
--- a/setup.py
+++ b/setup.py
@@ -23,12 +23,12 @@ PY3 = sys.version_info[0] == 3
install_requires = [
- "Django>=1.6.2,<1.7",
+ "Django>=1.6.2,<1.8",
"South==1.0.0",
"django-compressor>=1.4",
"django-libsass>=0.2",
"django-modelcluster>=0.3",
- "django-taggit==0.12.0",
+ "django-taggit==0.12.1",
"django-treebeard==2.0",
"Pillow>=2.3.0",
"beautifulsoup4>=4.3.2",
diff --git a/tox.ini b/tox.ini
index 5e68ac99a..a3a691f2d 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,11 +1,40 @@
[deps]
-dj16=
- Django>=1.6,<1.7
+base =
+ South==1.0.0
+ django-compressor>=1.4
+ django-libsass>=0.2
+ django-modelcluster>=0.3
+ django-taggit==0.12.1
+ django-treebeard==2.0
+ Pillow>=2.3.0
+ beautifulsoup4>=4.3.2
+ html5lib==0.999
+ Unidecode>=0.04.14
+ six==1.7.3
+ requests==2.3.0
elasticsearch==1.1.0
mock==1.0.1
python-dateutil==2.2
+ Embedly
+ coverage
+
+dj16 =
+ Django>=1.6,<1.7
+
+
+dj17 =
+ https://github.com/django/django/archive/stable/1.7.x.zip#egg=django
+
+py2 =
+ unicodecsv>=0.9.4
+
+py3 =
+
[tox]
+skipsdist = True
+usedevelop = True
+
envlist =
py26-dj16-postgres,
py26-dj16-sqlite,
@@ -13,7 +42,16 @@ envlist =
py27-dj16-sqlite,
py32-dj16-postgres,
py33-dj16-postgres,
- py34-dj16-postgres
+ py34-dj16-postgres,
+
+ py27-dj17-postgres,
+ py27-dj17-sqlite,
+ py32-dj17-postgres,
+ py32-dj17-sqlite,
+ py33-dj17-postgres,
+ py33-dj17-sqlite,
+ py34-dj17-postgres,
+ py34-dj17-sqlite
# mysql not currently supported
# (wagtail.wagtailimages.tests.TestImageEditView currently fails with a
@@ -28,11 +66,13 @@ envlist =
[testenv]
-commands=./runtests.py
+commands=coverage run runtests.py
[testenv:py26-dj16-postgres]
basepython=python2.6
deps =
+ {[deps]base}
+ {[deps]py2}
{[deps]dj16}
psycopg2==2.5.3
setenv =
@@ -41,6 +81,8 @@ setenv =
[testenv:py26-dj16-sqlite]
basepython=python2.6
deps =
+ {[deps]base}
+ {[deps]py2}
{[deps]dj16}
setenv =
DATABASE_ENGINE=django.db.backends.sqlite3
@@ -48,6 +90,8 @@ setenv =
[testenv:py26-dj16-mysql]
basepython=python2.6
deps =
+ {[deps]base}
+ {[deps]py2}
{[deps]dj16}
MySQL-python==1.2.5
setenv =
@@ -57,6 +101,8 @@ setenv =
[testenv:py27-dj16-postgres]
basepython=python2.7
deps =
+ {[deps]base}
+ {[deps]py2}
{[deps]dj16}
psycopg2==2.5.3
setenv =
@@ -65,6 +111,8 @@ setenv =
[testenv:py27-dj16-sqlite]
basepython=python2.7
deps =
+ {[deps]base}
+ {[deps]py2}
{[deps]dj16}
setenv =
DATABASE_ENGINE=django.db.backends.sqlite3
@@ -72,6 +120,8 @@ setenv =
[testenv:py27-dj16-mysql]
basepython=python2.7
deps =
+ {[deps]base}
+ {[deps]py2}
{[deps]dj16}
MySQL-python==1.2.5
setenv =
@@ -81,6 +131,8 @@ setenv =
[testenv:py32-dj16-postgres]
basepython=python3.2
deps =
+ {[deps]base}
+ {[deps]py3}
{[deps]dj16}
psycopg2==2.5.3
setenv =
@@ -89,6 +141,8 @@ setenv =
[testenv:py32-dj16-sqlite]
basepython=python3.2
deps =
+ {[deps]base}
+ {[deps]py3}
{[deps]dj16}
setenv =
DATABASE_ENGINE=django.db.backends.sqlite3
@@ -96,6 +150,8 @@ setenv =
[testenv:py33-dj16-postgres]
basepython=python3.3
deps =
+ {[deps]base}
+ {[deps]py3}
{[deps]dj16}
psycopg2==2.5.3
setenv =
@@ -104,6 +160,8 @@ setenv =
[testenv:py33-dj16-sqlite]
basepython=python3.3
deps =
+ {[deps]base}
+ {[deps]py3}
{[deps]dj16}
setenv =
DATABASE_ENGINE=django.db.backends.sqlite3
@@ -111,6 +169,8 @@ setenv =
[testenv:py34-dj16-postgres]
basepython=python3.4
deps =
+ {[deps]base}
+ {[deps]py3}
{[deps]dj16}
psycopg2==2.5.3
setenv =
@@ -119,6 +179,95 @@ setenv =
[testenv:py34-dj16-sqlite]
basepython=python3.4
deps =
+ {[deps]base}
+ {[deps]py3}
{[deps]dj16}
setenv =
DATABASE_ENGINE=django.db.backends.sqlite3
+
+[testenv:py27-dj17-postgres]
+basepython=python2.7
+deps =
+ {[deps]base}
+ {[deps]py2}
+ {[deps]dj17}
+ psycopg2==2.5.3
+setenv =
+ DATABASE_ENGINE=django.db.backends.postgresql_psycopg2
+
+[testenv:py27-dj17-sqlite]
+basepython=python2.7
+deps =
+ {[deps]base}
+ {[deps]py2}
+ {[deps]dj17}
+setenv =
+ DATABASE_ENGINE=django.db.backends.sqlite3
+
+[testenv:py27-dj17-mysql]
+basepython=python2.7
+deps =
+ {[deps]base}
+ {[deps]py2}
+ {[deps]dj17}
+ MySQL-python==1.2.5
+setenv =
+ DATABASE_ENGINE=django.db.backends.mysql
+ DATABASE_USER=wagtail
+
+[testenv:py32-dj17-postgres]
+basepython=python3.2
+deps =
+ {[deps]base}
+ {[deps]py3}
+ {[deps]dj17}
+ psycopg2==2.5.3
+setenv =
+ DATABASE_ENGINE=django.db.backends.postgresql_psycopg2
+
+[testenv:py32-dj17-sqlite]
+basepython=python3.2
+deps =
+ {[deps]base}
+ {[deps]py3}
+ {[deps]dj17}
+setenv =
+ DATABASE_ENGINE=django.db.backends.sqlite3
+
+[testenv:py33-dj17-postgres]
+basepython=python3.3
+deps =
+ {[deps]base}
+ {[deps]py3}
+ {[deps]dj17}
+ psycopg2==2.5.3
+setenv =
+ DATABASE_ENGINE=django.db.backends.postgresql_psycopg2
+
+[testenv:py33-dj17-sqlite]
+basepython=python3.3
+deps =
+ {[deps]base}
+ {[deps]py3}
+ {[deps]dj17}
+setenv =
+ DATABASE_ENGINE=django.db.backends.sqlite3
+
+[testenv:py34-dj17-postgres]
+basepython=python3.4
+deps =
+ {[deps]base}
+ {[deps]py3}
+ {[deps]dj17}
+ psycopg2==2.5.3
+setenv =
+ DATABASE_ENGINE=django.db.backends.postgresql_psycopg2
+
+[testenv:py34-dj17-sqlite]
+basepython=python3.4
+deps =
+ {[deps]base}
+ {[deps]py3}
+ {[deps]dj17}
+setenv =
+ DATABASE_ENGINE=django.db.backends.sqlite3
diff --git a/wagtail/contrib/wagtailfrontendcache/__init__.py b/wagtail/contrib/wagtailfrontendcache/__init__.py
index e69de29bb..2cd2d60cb 100644
--- a/wagtail/contrib/wagtailfrontendcache/__init__.py
+++ b/wagtail/contrib/wagtailfrontendcache/__init__.py
@@ -0,0 +1 @@
+default_app_config = 'wagtail.contrib.wagtailfrontendcache.apps.WagtailFrontendCacheAppConfig'
diff --git a/wagtail/contrib/wagtailfrontendcache/apps.py b/wagtail/contrib/wagtailfrontendcache/apps.py
new file mode 100644
index 000000000..2fa34a0da
--- /dev/null
+++ b/wagtail/contrib/wagtailfrontendcache/apps.py
@@ -0,0 +1,7 @@
+from django.apps import AppConfig
+
+
+class WagtailFrontendCacheAppConfig(AppConfig):
+ name = 'wagtail.contrib.wagtailfrontendcache'
+ label = 'wagtailfrontendcache'
+ verbose_name = "Wagtail frontend cache"
diff --git a/wagtail/contrib/wagtailmedusa/__init__.py b/wagtail/contrib/wagtailmedusa/__init__.py
index e69de29bb..5d5f8ce96 100644
--- a/wagtail/contrib/wagtailmedusa/__init__.py
+++ b/wagtail/contrib/wagtailmedusa/__init__.py
@@ -0,0 +1 @@
+default_app_config = 'wagtail.contrib.wagtailmedusa.apps.WagtailMedusaAppConfig'
diff --git a/wagtail/contrib/wagtailmedusa/apps.py b/wagtail/contrib/wagtailmedusa/apps.py
new file mode 100644
index 000000000..46143d137
--- /dev/null
+++ b/wagtail/contrib/wagtailmedusa/apps.py
@@ -0,0 +1,7 @@
+from django.apps import AppConfig
+
+
+class WagtailMedusaAppConfig(AppConfig):
+ name = 'wagtail.contrib.wagtailmedusa'
+ label = 'wagtailmedusa'
+ verbose_name = "Wagtail medusa"
diff --git a/wagtail/contrib/wagtailroutablepage/__init__.py b/wagtail/contrib/wagtailroutablepage/__init__.py
index e69de29bb..286151ff0 100644
--- a/wagtail/contrib/wagtailroutablepage/__init__.py
+++ b/wagtail/contrib/wagtailroutablepage/__init__.py
@@ -0,0 +1 @@
+default_app_config = 'wagtail.contrib.wagtailroutablepage.apps.WagtailRoutablePageAppConfig'
diff --git a/wagtail/contrib/wagtailroutablepage/apps.py b/wagtail/contrib/wagtailroutablepage/apps.py
new file mode 100644
index 000000000..c3b9c9573
--- /dev/null
+++ b/wagtail/contrib/wagtailroutablepage/apps.py
@@ -0,0 +1,7 @@
+from django.apps import AppConfig
+
+
+class WagtailRoutablePageAppConfig(AppConfig):
+ name = 'wagtail.contrib.wagtailroutablepage'
+ label = 'wagtailroutablepage'
+ verbose_name = "Wagtail routablepage"
diff --git a/wagtail/contrib/wagtailroutablepage/models.py b/wagtail/contrib/wagtailroutablepage/models.py
index 926e5f48a..88379b37a 100644
--- a/wagtail/contrib/wagtailroutablepage/models.py
+++ b/wagtail/contrib/wagtailroutablepage/models.py
@@ -8,9 +8,10 @@ from wagtail.wagtailcore.models import Page
from wagtail.wagtailcore.url_routing import RouteResult
-class RoutablePage(Page):
+class RoutablePageMixin(object):
"""
- This class extends Page by adding methods to allow urlconfs to be embedded inside pages
+ This class can be mixed in to a Page subclass to allow urlconfs to be
+ embedded inside pages.
"""
#: Set this to a tuple of ``django.conf.urls.url`` objects.
subpage_urls = None
@@ -59,7 +60,7 @@ class RoutablePage(Page):
except Http404:
pass
- return super(RoutablePage, self).route(request, path_components)
+ return super(RoutablePageMixin, self).route(request, path_components)
def serve(self, request, view, args, kwargs):
return view(request, *args, **kwargs)
@@ -68,6 +69,13 @@ class RoutablePage(Page):
view, args, kwargs = self.resolve_subpage('/')
return view(*args, **kwargs)
+
+class RoutablePage(RoutablePageMixin, Page):
+ """
+ This class extends Page by adding methods to allow urlconfs
+ to be embedded inside pages
+ """
+
is_abstract = True
class Meta:
diff --git a/wagtail/contrib/wagtailsitemaps/__init__.py b/wagtail/contrib/wagtailsitemaps/__init__.py
index e69de29bb..e647b7be1 100644
--- a/wagtail/contrib/wagtailsitemaps/__init__.py
+++ b/wagtail/contrib/wagtailsitemaps/__init__.py
@@ -0,0 +1 @@
+default_app_config = 'wagtail.contrib.wagtailsitemaps.apps.WagtailSitemapsAppConfig'
diff --git a/wagtail/contrib/wagtailsitemaps/apps.py b/wagtail/contrib/wagtailsitemaps/apps.py
new file mode 100644
index 000000000..d8b249a2a
--- /dev/null
+++ b/wagtail/contrib/wagtailsitemaps/apps.py
@@ -0,0 +1,7 @@
+from django.apps import AppConfig
+
+
+class WagtailSitemapsAppConfig(AppConfig):
+ name = 'wagtail.contrib.wagtailsitemaps'
+ label = 'wagtailsitemaps'
+ verbose_name = "Wagtail sitemaps"
diff --git a/wagtail/contrib/wagtailstyleguide/__init__.py b/wagtail/contrib/wagtailstyleguide/__init__.py
index e69de29bb..18ff63cfe 100644
--- a/wagtail/contrib/wagtailstyleguide/__init__.py
+++ b/wagtail/contrib/wagtailstyleguide/__init__.py
@@ -0,0 +1 @@
+default_app_config = 'wagtail.contrib.wagtailstyleguide.apps.WagtailStyleGuideAppConfig'
diff --git a/wagtail/contrib/wagtailstyleguide/apps.py b/wagtail/contrib/wagtailstyleguide/apps.py
new file mode 100644
index 000000000..093003ac8
--- /dev/null
+++ b/wagtail/contrib/wagtailstyleguide/apps.py
@@ -0,0 +1,7 @@
+from django.apps import AppConfig
+
+
+class WagtailStyleGuideAppConfig(AppConfig):
+ name = 'wagtail.contrib.wagtailstyleguide'
+ label = 'wagtailstyleguide'
+ verbose_name = "Wagtail style guide"
diff --git a/wagtail/contrib/wagtailstyleguide/templates/wagtailstyleguide/base.html b/wagtail/contrib/wagtailstyleguide/templates/wagtailstyleguide/base.html
index bca7ce8a1..667798c80 100644
--- a/wagtail/contrib/wagtailstyleguide/templates/wagtailstyleguide/base.html
+++ b/wagtail/contrib/wagtailstyleguide/templates/wagtailstyleguide/base.html
@@ -30,6 +30,7 @@
Page editor
Tabs
Breadcrumbs
+ Progress indicators
Misc formatters
Icons
@@ -406,6 +407,20 @@
+
+ Progress indicators
+
+
+
+
+
+
+
+
Misc formatters
Avatar icons
@@ -506,7 +521,16 @@
{% block extra_js %}
{% endblock %}
\ No newline at end of file
diff --git a/wagtail/tests/migrations/0001_initial.py b/wagtail/tests/migrations/0001_initial.py
new file mode 100644
index 000000000..c1dd942f5
--- /dev/null
+++ b/wagtail/tests/migrations/0001_initial.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('auth', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='CustomUser',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('password', models.CharField(max_length=128, verbose_name='password')),
+ ('last_login', models.DateTimeField(default=django.utils.timezone.now, verbose_name='last login')),
+ ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
+ ('username', models.CharField(unique=True, max_length=100)),
+ ('email', models.EmailField(max_length=255, blank=True)),
+ ('is_staff', models.BooleanField(default=True)),
+ ('is_active', models.BooleanField(default=True)),
+ ('first_name', models.CharField(max_length=50, blank=True)),
+ ('last_name', models.CharField(max_length=50, blank=True)),
+ ('groups', models.ManyToManyField(to='auth.Group', verbose_name='groups', blank=True)),
+ ('user_permissions', models.ManyToManyField(to='auth.Permission', verbose_name='user permissions', blank=True)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=(models.Model,),
+ ),
+ ]
diff --git a/wagtail/tests/migrations/0002_auto_20140827_0908.py b/wagtail/tests/migrations/0002_auto_20140827_0908.py
new file mode 100644
index 000000000..66c85ffb0
--- /dev/null
+++ b/wagtail/tests/migrations/0002_auto_20140827_0908.py
@@ -0,0 +1,386 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+import django.db.models.deletion
+import modelcluster.fields
+import wagtail.contrib.wagtailroutablepage.models
+import wagtail.wagtailcore.fields
+import wagtail.wagtailsearch.index
+import modelcluster.tags
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('wagtailcore', '0002_initial_data'),
+ ('wagtaildocs', '0002_initial_data'),
+ ('taggit', '0001_initial'),
+ ('wagtailimages', '0002_initial_data'),
+ ('tests', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Advert',
+ fields=[
+ ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)),
+ ('url', models.URLField(null=True, blank=True)),
+ ('text', models.CharField(max_length=255)),
+ ],
+ options={
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='AdvertPlacement',
+ fields=[
+ ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)),
+ ('advert', models.ForeignKey(to='tests.Advert', related_name='+')),
+ ],
+ options={
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='AlphaSnippet',
+ fields=[
+ ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)),
+ ('text', models.CharField(max_length=255)),
+ ],
+ options={
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='BusinessChild',
+ fields=[
+ ('page_ptr', models.OneToOneField(parent_link=True, to='wagtailcore.Page', serialize=False, auto_created=True, primary_key=True)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=('wagtailcore.page',),
+ ),
+ migrations.CreateModel(
+ name='BusinessIndex',
+ fields=[
+ ('page_ptr', models.OneToOneField(parent_link=True, to='wagtailcore.Page', serialize=False, auto_created=True, primary_key=True)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=('wagtailcore.page',),
+ ),
+ migrations.CreateModel(
+ name='BusinessSubIndex',
+ fields=[
+ ('page_ptr', models.OneToOneField(parent_link=True, to='wagtailcore.Page', serialize=False, auto_created=True, primary_key=True)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=('wagtailcore.page',),
+ ),
+ migrations.CreateModel(
+ name='EventIndex',
+ fields=[
+ ('page_ptr', models.OneToOneField(parent_link=True, to='wagtailcore.Page', serialize=False, auto_created=True, primary_key=True)),
+ ('intro', wagtail.wagtailcore.fields.RichTextField(blank=True)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=('wagtailcore.page',),
+ ),
+ migrations.CreateModel(
+ name='EventPage',
+ fields=[
+ ('page_ptr', models.OneToOneField(parent_link=True, to='wagtailcore.Page', serialize=False, auto_created=True, primary_key=True)),
+ ('date_from', models.DateField(null=True, verbose_name='Start date')),
+ ('date_to', models.DateField(null=True, help_text='Not required if event is on a single day', blank=True, verbose_name='End date')),
+ ('time_from', models.TimeField(null=True, blank=True, verbose_name='Start time')),
+ ('time_to', models.TimeField(null=True, blank=True, verbose_name='End time')),
+ ('audience', models.CharField(choices=[('public', 'Public'), ('private', 'Private')], max_length=255)),
+ ('location', models.CharField(max_length=255)),
+ ('body', wagtail.wagtailcore.fields.RichTextField(blank=True)),
+ ('cost', models.CharField(max_length=255)),
+ ('signup_link', models.URLField(blank=True)),
+ ('feed_image', models.ForeignKey(related_name='+', blank=True, to='wagtailimages.Image', on_delete=django.db.models.deletion.SET_NULL, null=True)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=('wagtailcore.page',),
+ ),
+ migrations.CreateModel(
+ name='EventPageCarouselItem',
+ fields=[
+ ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)),
+ ('sort_order', models.IntegerField(null=True, blank=True, editable=False)),
+ ('link_external', models.URLField(blank=True, verbose_name='External link')),
+ ('embed_url', models.URLField(blank=True, verbose_name='Embed URL')),
+ ('caption', models.CharField(blank=True, max_length=255)),
+ ('image', models.ForeignKey(related_name='+', blank=True, to='wagtailimages.Image', on_delete=django.db.models.deletion.SET_NULL, null=True)),
+ ('link_document', models.ForeignKey(related_name='+', blank=True, to='wagtaildocs.Document', null=True)),
+ ],
+ options={
+ 'abstract': False,
+ 'ordering': ['sort_order'],
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='EventPageRelatedLink',
+ fields=[
+ ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)),
+ ('sort_order', models.IntegerField(null=True, blank=True, editable=False)),
+ ('link_external', models.URLField(blank=True, verbose_name='External link')),
+ ('title', models.CharField(help_text='Link title', max_length=255)),
+ ('link_document', models.ForeignKey(related_name='+', blank=True, to='wagtaildocs.Document', null=True)),
+ ],
+ options={
+ 'abstract': False,
+ 'ordering': ['sort_order'],
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='EventPageSpeaker',
+ fields=[
+ ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)),
+ ('sort_order', models.IntegerField(null=True, blank=True, editable=False)),
+ ('link_external', models.URLField(blank=True, verbose_name='External link')),
+ ('first_name', models.CharField(blank=True, verbose_name='Name', max_length=255)),
+ ('last_name', models.CharField(blank=True, verbose_name='Surname', max_length=255)),
+ ('image', models.ForeignKey(related_name='+', blank=True, to='wagtailimages.Image', on_delete=django.db.models.deletion.SET_NULL, null=True)),
+ ('link_document', models.ForeignKey(related_name='+', blank=True, to='wagtaildocs.Document', null=True)),
+ ],
+ options={
+ 'abstract': False,
+ 'ordering': ['sort_order'],
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='FormField',
+ fields=[
+ ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)),
+ ('sort_order', models.IntegerField(null=True, blank=True, editable=False)),
+ ('label', models.CharField(help_text='The label of the form field', max_length=255)),
+ ('field_type', models.CharField(choices=[('singleline', 'Single line text'), ('multiline', 'Multi-line text'), ('email', 'Email'), ('number', 'Number'), ('url', 'URL'), ('checkbox', 'Checkbox'), ('checkboxes', 'Checkboxes'), ('dropdown', 'Drop down'), ('radio', 'Radio buttons'), ('date', 'Date'), ('datetime', 'Date/time')], max_length=16)),
+ ('required', models.BooleanField(default=True)),
+ ('choices', models.CharField(blank=True, help_text='Comma seperated list of choices. Only applicable in checkboxes, radio and dropdown.', max_length=512)),
+ ('default_value', models.CharField(blank=True, help_text='Default value. Comma seperated values supported for checkboxes.', max_length=255)),
+ ('help_text', models.CharField(blank=True, max_length=255)),
+ ],
+ options={
+ 'abstract': False,
+ 'ordering': ['sort_order'],
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='FormPage',
+ fields=[
+ ('page_ptr', models.OneToOneField(parent_link=True, to='wagtailcore.Page', serialize=False, auto_created=True, primary_key=True)),
+ ('to_address', models.CharField(blank=True, help_text='Optional - form submissions will be emailed to this address', max_length=255)),
+ ('from_address', models.CharField(blank=True, max_length=255)),
+ ('subject', models.CharField(blank=True, max_length=255)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=('wagtailcore.page',),
+ ),
+ migrations.CreateModel(
+ name='PageWithOldStyleRouteMethod',
+ fields=[
+ ('page_ptr', models.OneToOneField(parent_link=True, to='wagtailcore.Page', serialize=False, auto_created=True, primary_key=True)),
+ ('content', models.TextField()),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=('wagtailcore.page',),
+ ),
+ migrations.CreateModel(
+ name='RoutablePageTest',
+ fields=[
+ ('page_ptr', models.OneToOneField(parent_link=True, to='wagtailcore.Page', serialize=False, auto_created=True, primary_key=True)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=(wagtail.contrib.wagtailroutablepage.models.RoutablePageMixin, 'wagtailcore.page'),
+ ),
+ migrations.CreateModel(
+ name='SearchTest',
+ fields=[
+ ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)),
+ ('title', models.CharField(max_length=255)),
+ ('content', models.TextField()),
+ ('live', models.BooleanField(default=False)),
+ ('published_date', models.DateField(null=True)),
+ ],
+ options={
+ },
+ bases=(models.Model, wagtail.wagtailsearch.index.Indexed),
+ ),
+ migrations.CreateModel(
+ name='SearchTestChild',
+ fields=[
+ ('searchtest_ptr', models.OneToOneField(parent_link=True, to='tests.SearchTest', serialize=False, auto_created=True, primary_key=True)),
+ ('subtitle', models.CharField(null=True, blank=True, max_length=255)),
+ ('extra_content', models.TextField()),
+ ],
+ options={
+ },
+ bases=('tests.searchtest',),
+ ),
+ migrations.CreateModel(
+ name='SearchTestOldConfig',
+ fields=[
+ ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)),
+ ],
+ options={
+ },
+ bases=(models.Model, wagtail.wagtailsearch.index.Indexed),
+ ),
+ migrations.CreateModel(
+ name='SearchTestOldConfigList',
+ fields=[
+ ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)),
+ ],
+ options={
+ },
+ bases=(models.Model, wagtail.wagtailsearch.index.Indexed),
+ ),
+ migrations.CreateModel(
+ name='SimplePage',
+ fields=[
+ ('page_ptr', models.OneToOneField(parent_link=True, to='wagtailcore.Page', serialize=False, auto_created=True, primary_key=True)),
+ ('content', models.TextField()),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=('wagtailcore.page',),
+ ),
+ migrations.CreateModel(
+ name='StandardChild',
+ fields=[
+ ('page_ptr', models.OneToOneField(parent_link=True, to='wagtailcore.Page', serialize=False, auto_created=True, primary_key=True)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=('wagtailcore.page',),
+ ),
+ migrations.CreateModel(
+ name='StandardIndex',
+ fields=[
+ ('page_ptr', models.OneToOneField(parent_link=True, to='wagtailcore.Page', serialize=False, auto_created=True, primary_key=True)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=('wagtailcore.page',),
+ ),
+ migrations.CreateModel(
+ name='TaggedPage',
+ fields=[
+ ('page_ptr', models.OneToOneField(parent_link=True, to='wagtailcore.Page', serialize=False, auto_created=True, primary_key=True)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=('wagtailcore.page',),
+ ),
+ migrations.CreateModel(
+ name='TaggedPageTag',
+ fields=[
+ ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)),
+ ('content_object', modelcluster.fields.ParentalKey(to='tests.TaggedPage', related_name='tagged_items')),
+ ('tag', models.ForeignKey(to='taggit.Tag', related_name='tests_taggedpagetag_items')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='ZuluSnippet',
+ fields=[
+ ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)),
+ ('text', models.CharField(max_length=255)),
+ ],
+ options={
+ },
+ bases=(models.Model,),
+ ),
+ migrations.AddField(
+ model_name='taggedpage',
+ name='tags',
+ field=modelcluster.tags.ClusterTaggableManager(through='tests.TaggedPageTag', blank=True, verbose_name='Tags', to='taggit.Tag', help_text='A comma-separated list of tags.'),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='formfield',
+ name='page',
+ field=modelcluster.fields.ParentalKey(to='tests.FormPage', related_name='form_fields'),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='eventpagespeaker',
+ name='link_page',
+ field=models.ForeignKey(related_name='+', blank=True, to='wagtailcore.Page', null=True),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='eventpagespeaker',
+ name='page',
+ field=modelcluster.fields.ParentalKey(to='tests.EventPage', related_name='speakers'),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='eventpagerelatedlink',
+ name='link_page',
+ field=models.ForeignKey(related_name='+', blank=True, to='wagtailcore.Page', null=True),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='eventpagerelatedlink',
+ name='page',
+ field=modelcluster.fields.ParentalKey(to='tests.EventPage', related_name='related_links'),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='eventpagecarouselitem',
+ name='link_page',
+ field=models.ForeignKey(related_name='+', blank=True, to='wagtailcore.Page', null=True),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='eventpagecarouselitem',
+ name='page',
+ field=modelcluster.fields.ParentalKey(to='tests.EventPage', related_name='carousel_items'),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='advertplacement',
+ name='page',
+ field=modelcluster.fields.ParentalKey(to='wagtailcore.Page', related_name='advert_placements'),
+ preserve_default=True,
+ ),
+ migrations.AlterField(
+ model_name='customuser',
+ name='groups',
+ field=models.ManyToManyField(related_name='user_set', blank=True, verbose_name='groups', to='auth.Group', related_query_name='user', help_text='The groups this user belongs to. A user will get all permissions granted to each of his/her group.'),
+ ),
+ migrations.AlterField(
+ model_name='customuser',
+ name='user_permissions',
+ field=models.ManyToManyField(related_name='user_set', blank=True, verbose_name='user permissions', to='auth.Permission', related_query_name='user', help_text='Specific permissions for this user.'),
+ ),
+ ]
diff --git a/wagtail/tests/migrations/__init__.py b/wagtail/tests/migrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/wagtail/tests/models.py b/wagtail/tests/models.py
index a38d889bd..0fc74441e 100644
--- a/wagtail/tests/models.py
+++ b/wagtail/tests/models.py
@@ -17,7 +17,7 @@ from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
from wagtail.wagtaildocs.edit_handlers import DocumentChooserPanel
from wagtail.wagtailforms.models import AbstractEmailForm, AbstractFormField
from wagtail.wagtailsnippets.models import register_snippet
-from wagtail.wagtailsearch import indexed
+from wagtail.wagtailsearch import index
from wagtail.contrib.wagtailroutablepage.models import RoutablePage
@@ -228,8 +228,11 @@ class EventPage(Page):
related_name='+'
)
- indexed_fields = ('get_audience_display', 'location', 'body')
- search_name = "Event"
+ search_fields = (
+ index.SearchField('get_audience_display'),
+ index.SearchField('location'),
+ index.SearchField('body'),
+ )
password_required_template = 'tests/event_page_password_required.html'
@@ -387,19 +390,19 @@ class BusinessChild(Page):
subpage_types = []
-class SearchTest(models.Model, indexed.Indexed):
+class SearchTest(models.Model, index.Indexed):
title = models.CharField(max_length=255)
content = models.TextField()
live = models.BooleanField(default=False)
published_date = models.DateField(null=True)
search_fields = [
- indexed.SearchField('title', partial_match=True),
- indexed.SearchField('content'),
- indexed.SearchField('callable_indexed_field'),
- indexed.FilterField('title'),
- indexed.FilterField('live'),
- indexed.FilterField('published_date'),
+ index.SearchField('title', partial_match=True),
+ index.SearchField('content'),
+ index.SearchField('callable_indexed_field'),
+ index.FilterField('title'),
+ index.FilterField('live'),
+ index.FilterField('published_date'),
]
def callable_indexed_field(self):
@@ -411,40 +414,11 @@ class SearchTestChild(SearchTest):
extra_content = models.TextField()
search_fields = SearchTest.search_fields + [
- indexed.SearchField('subtitle', partial_match=True),
- indexed.SearchField('extra_content'),
+ index.SearchField('subtitle', partial_match=True),
+ index.SearchField('extra_content'),
]
-class SearchTestOldConfig(models.Model, indexed.Indexed):
- """
- This tests that the Indexed class can correctly handle models that
- use the old "indexed_fields" configuration format.
- """
- indexed_fields = {
- # A search field with predictive search and boosting
- 'title': {
- 'type': 'string',
- 'analyzer': 'edgengram_analyzer',
- 'boost': 100,
- },
-
- # A filter field
- 'live': {
- 'type': 'boolean',
- 'index': 'not_analyzed',
- },
- }
-
-
-class SearchTestOldConfigList(models.Model, indexed.Indexed):
- """
- This tests that the Indexed class can correctly handle models that
- use the old "indexed_fields" configuration format using a list.
- """
- indexed_fields = ['title', 'content']
-
-
def routable_page_external_view(request, arg):
return HttpResponse("EXTERNAL VIEW: " + arg)
diff --git a/wagtail/tests/settings.py b/wagtail/tests/settings.py
index 1ef1c5ab1..e9c1172b4 100644
--- a/wagtail/tests/settings.py
+++ b/wagtail/tests/settings.py
@@ -1,5 +1,6 @@
import os
+import django
from django.conf import global_settings
@@ -57,7 +58,6 @@ INSTALLED_APPS = [
'django.contrib.admin',
'taggit',
- 'south',
'compressor',
'wagtail.wagtailcore',
@@ -68,7 +68,6 @@ INSTALLED_APPS = [
'wagtail.wagtailimages',
'wagtail.wagtailembeds',
'wagtail.wagtailsearch',
- 'wagtail.wagtailredirects',
'wagtail.wagtailforms',
'wagtail.contrib.wagtailstyleguide',
'wagtail.contrib.wagtailsitemaps',
@@ -76,6 +75,27 @@ INSTALLED_APPS = [
'wagtail.tests',
]
+# If we are using Django 1.6, add South to INSTALLED_APPS
+if django.VERSION < (1, 7):
+ INSTALLED_APPS.append('south')
+
+
+# If we are using Django 1.7 install wagtailredirects with its appconfig
+# Theres nothing special about wagtailredirects, we just need to have one
+# app which uses AppConfigs to test that hooks load properly
+
+if django.VERSION < (1, 7):
+ INSTALLED_APPS.append('wagtail.wagtailredirects')
+else:
+ INSTALLED_APPS.append('wagtail.wagtailredirects.apps.WagtailRedirectsAppConfig')
+
+# As we don't have south migrations for tests, South thinks
+# the Django 1.7 migrations are South migrations.
+SOUTH_MIGRATION_MODULES = {
+ 'tests': 'ignore',
+}
+
+
# Using DatabaseCache to make sure that the cache is cleared between tests.
# This prevents false-positives in some wagtail core tests where we are
# changing the 'wagtail_root_paths' key which may cause future tests to fail.
diff --git a/wagtail/utils/apps.py b/wagtail/utils/apps.py
new file mode 100644
index 000000000..2bee7484e
--- /dev/null
+++ b/wagtail/utils/apps.py
@@ -0,0 +1,35 @@
+try:
+ from importlib import import_module
+except ImportError:
+ # for Python 2.6, fall back on django.utils.importlib (deprecated as of Django 1.7)
+ from django.utils.importlib import import_module
+
+import django
+from django.conf import settings
+from django.utils.module_loading import module_has_submodule
+
+
+def get_app_modules():
+ """
+ Generator function that yields a module object for each installed app
+ yields tuples of (app_name, module)
+ """
+ if django.VERSION < (1, 7):
+ # Django 1.6
+ for app in settings.INSTALLED_APPS:
+ yield app, import_module(app)
+ else:
+ # Django 1.7+
+ from django.apps import apps
+ for app in apps.get_app_configs():
+ yield app.name, app.module
+
+
+def get_app_submodules(submodule_name):
+ """
+ Searches each app module for the specified submodule
+ yields tuples of (app_name, module)
+ """
+ for name, module in get_app_modules():
+ if module_has_submodule(module, submodule_name):
+ yield name, import_module('%s.%s' % (name, submodule_name))
diff --git a/wagtail/utils/deprecation.py b/wagtail/utils/deprecation.py
index 24dc4e589..6b4765a5e 100644
--- a/wagtail/utils/deprecation.py
+++ b/wagtail/utils/deprecation.py
@@ -1,6 +1,10 @@
-class RemovedInWagtail06Warning(DeprecationWarning):
+class RemovedInWagtail07Warning(DeprecationWarning):
pass
-class RemovedInWagtail07Warning(PendingDeprecationWarning):
+class RemovedInWagtail08Warning(PendingDeprecationWarning):
+ pass
+
+
+class RemovedInWagtail08Warning(PendingDeprecationWarning):
pass
diff --git a/wagtail/wagtailadmin/__init__.py b/wagtail/wagtailadmin/__init__.py
index e69de29bb..c0779fd93 100644
--- a/wagtail/wagtailadmin/__init__.py
+++ b/wagtail/wagtailadmin/__init__.py
@@ -0,0 +1 @@
+default_app_config = 'wagtail.wagtailadmin.apps.WagtailAdminAppConfig'
diff --git a/wagtail/wagtailadmin/apps.py b/wagtail/wagtailadmin/apps.py
new file mode 100644
index 000000000..c963dadef
--- /dev/null
+++ b/wagtail/wagtailadmin/apps.py
@@ -0,0 +1,7 @@
+from django.apps import AppConfig
+
+
+class WagtailAdminAppConfig(AppConfig):
+ name = 'wagtail.wagtailadmin'
+ label = 'wagtailadmin'
+ verbose_name = "Wagtail admin"
diff --git a/wagtail/wagtailadmin/edit_handlers.py b/wagtail/wagtailadmin/edit_handlers.py
index d2eb181c3..3e463523d 100644
--- a/wagtail/wagtailadmin/edit_handlers.py
+++ b/wagtail/wagtailadmin/edit_handlers.py
@@ -491,7 +491,11 @@ class BasePageChooserPanel(BaseChooserPanel):
except ValueError:
raise ImproperlyConfigured("The page_type passed to PageChooserPanel must be of the form 'app_label.model_name'")
- page_type = get_model(app_label, model_name)
+ try:
+ page_type = get_model(app_label, model_name)
+ except LookupError:
+ page_type = None
+
if page_type is None:
raise ImproperlyConfigured("PageChooserPanel refers to model '%s' that has not been installed" % cls.page_type)
else:
diff --git a/wagtail/wagtailadmin/hooks.py b/wagtail/wagtailadmin/hooks.py
deleted file mode 100644
index 8c8a2374a..000000000
--- a/wagtail/wagtailadmin/hooks.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# The 'hooks' module is now part of wagtailcore.
-# Imports are provided here for backwards compatibility
-
-import warnings
-
-from wagtail.utils.deprecation import RemovedInWagtail06Warning
-
-
-warnings.warn(
- "The wagtail.wagtailadmin.hooks module has been moved. "
- "Use wagtail.wagtailcore.hooks instead.", RemovedInWagtail06Warning)
-
-
-from wagtail.wagtailcore.hooks import register, get_hooks
diff --git a/wagtail/wagtailadmin/locale/pt_PT/LC_MESSAGES/django.mo b/wagtail/wagtailadmin/locale/pt_PT/LC_MESSAGES/django.mo
new file mode 100644
index 000000000..6568f80a9
Binary files /dev/null and b/wagtail/wagtailadmin/locale/pt_PT/LC_MESSAGES/django.mo differ
diff --git a/wagtail/wagtailadmin/locale/pt_PT/LC_MESSAGES/django.po b/wagtail/wagtailadmin/locale/pt_PT/LC_MESSAGES/django.po
new file mode 100644
index 000000000..cb6f0a69e
--- /dev/null
+++ b/wagtail/wagtailadmin/locale/pt_PT/LC_MESSAGES/django.po
@@ -0,0 +1,1127 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Douglas Miranda , 2014
+# Gladson , 2014
+msgid ""
+msgstr ""
+"Project-Id-Version: Wagtail 0.5.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2014-08-01 16:38+0100\n"
+"PO-Revision-Date: 2014-08-03 01:50+0100\n"
+"Last-Translator: Jose Lourenco \n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: pt_PT\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Poedit 1.5.4\n"
+
+#: edit_handlers.py:627
+msgid "Scheduled publishing"
+msgstr "Publicação agendada"
+
+#: edit_handlers.py:641
+msgid "Common page configuration"
+msgstr "Configuração comum de página"
+
+#: forms.py:19
+msgid "Search term"
+msgstr "Procurar termo"
+
+#: forms.py:43
+msgid "Enter your username"
+msgstr "Introduza o seu nome de utilizador"
+
+#: forms.py:46
+msgid "Enter password"
+msgstr "Introduza a sua senha"
+
+#: forms.py:51
+msgid "Enter your email address to reset your password"
+msgstr "Introduza o seu email para alterar a sua senha"
+
+#: forms.py:60
+msgid "Please fill your email address."
+msgstr "Por favor, insira o seu e-mail."
+
+#: forms.py:73
+msgid ""
+"Sorry, you cannot reset your password here as your user account is managed "
+"by another server."
+msgstr ""
+"Desculpe, você não pode alterar a sua senha aqui porque a sua conta de "
+"utilizador é gerida por outro servidor."
+
+#: forms.py:76
+msgid "This email address is not recognised."
+msgstr "Este email não é reconhecido."
+
+#: forms.py:88
+msgid "New title"
+msgstr "Novo título"
+
+#: forms.py:89
+msgid "New slug"
+msgstr "Novo endereço"
+
+#: forms.py:95
+msgid "Copy subpages"
+msgstr "Copiar sub-páginas"
+
+#: forms.py:97
+#, python-format
+msgid "This will copy %(count)s subpage."
+msgid_plural "This will copy %(count)s subpages."
+msgstr[0] "Será copiada %(count)s sub-página."
+msgstr[1] "Serão copiadas %(count)s sub-páginas."
+
+#: forms.py:106
+msgid "Publish copied page"
+msgstr "Publicar página copiada"
+
+#: forms.py:107
+msgid "This page is live. Would you like to publish its copy as well?"
+msgstr "Esta página está publicada. Gostaria de publicar também a sua cópia?"
+
+#: forms.py:109
+msgid "Publish copies"
+msgstr "Publicar cópias"
+
+#: forms.py:111
+#, python-format
+msgid ""
+"%(count)s of the pages being copied is live. Would you like to publish its "
+"copy?"
+msgid_plural ""
+"%(count)s of the pages being copied are live. Would you like to publish "
+"their copies?"
+msgstr[0] ""
+"%(count)s das páginas a copiar estão publicadas. Gostaria de publicar "
+"também a sua cópia?"
+msgstr[1] ""
+"%(count)s das páginas a copiar estão publicadas. Gostaria de publicar "
+"também as suas cópias?"
+
+#: forms.py:124 views/pages.py:155 views/pages.py:272
+msgid "This slug is already in use"
+msgstr "Esse endereço já existe"
+
+#: forms.py:130 templates/wagtailadmin/pages/_privacy_indicator.html:13
+#: templates/wagtailadmin/pages/_privacy_indicator.html:18
+msgid "Public"
+msgstr "Público"
+
+#: forms.py:131
+msgid "Private, accessible with the following password"
+msgstr "Privado, acessível com a seguinte senha"
+
+#: forms.py:139
+msgid "This field is required."
+msgstr "Este campo é obrigatório."
+
+#: templates/wagtailadmin/base.html:7 templates/wagtailadmin/home.html:4
+msgid "Dashboard"
+msgstr "Painel de controlo"
+
+#: templates/wagtailadmin/base.html:27
+msgid "Menu"
+msgstr "Menu"
+
+#: templates/wagtailadmin/home.html:22
+#, python-format
+msgid "Welcome to the %(site_name)s Wagtail CMS"
+msgstr "Bem vindo ao %(site_name)s Wagtail CMS"
+
+#: templates/wagtailadmin/home.html:33
+msgid ""
+"This is your dashboard on which helpful information about content you've "
+"created will be displayed."
+msgstr ""
+"Este é o seu painel de controlo no qual serão mostradas informações úteis "
+"sobre os conteúdos criados por si."
+
+#: templates/wagtailadmin/login.html:4 templates/wagtailadmin/login.html:59
+msgid "Sign in"
+msgstr "Entrar"
+
+#: templates/wagtailadmin/login.html:18
+msgid "Your username and password didn't match. Please try again."
+msgstr "O seu nome de utilizador ou senha não estão corretos. Tente novamente."
+
+#: templates/wagtailadmin/login.html:26
+msgid "Sign in to Wagtail"
+msgstr "Entrar no Wagtail"
+
+#: templates/wagtailadmin/login.html:46
+msgid "Forgotten it?"
+msgstr "Esqueceu-a?"
+
+#: templates/wagtailadmin/account/account.html:4
+#: templates/wagtailadmin/account/account.html:6
+msgid "Account"
+msgstr "Conta"
+
+#: templates/wagtailadmin/account/account.html:13
+msgid "Set gravatar"
+msgstr "Atribuir gravatar"
+
+#: templates/wagtailadmin/account/account.html:17
+msgid ""
+"Your avatar image is provided by Gravatar and is connected to your email "
+"address. With a Gravatar account you can set an avatar for any number of "
+"other email addresses you use."
+msgstr ""
+"A sua imagem avatar é fornecida por Gravatar e está associada ao seu email. "
+"Com uma conta do Gravatar você pode definir uma imagem avatar para qualquer "
+"número de emails que você use."
+
+#: templates/wagtailadmin/account/account.html:25
+#: templates/wagtailadmin/account/change_password.html:4
+#: templates/wagtailadmin/account/change_password.html:6
+msgid "Change password"
+msgstr "Alterar senha"
+
+#: templates/wagtailadmin/account/account.html:29
+msgid "Change the password you use to log in."
+msgstr "Altere a senha que utiliza para entrar."
+
+#: templates/wagtailadmin/account/account.html:36
+msgid "Notification preferences"
+msgstr "Preferências de notificação"
+
+#: templates/wagtailadmin/account/account.html:40
+msgid "Choose which email notifications to receive."
+msgstr "Escolha quais as notificações de email a receber."
+
+#: templates/wagtailadmin/account/change_password.html:18
+msgid "Change Password"
+msgstr "Alterar Senha"
+
+#: templates/wagtailadmin/account/change_password.html:21
+msgid ""
+"Your password can't be changed here. Please contact a site administrator."
+msgstr "A sua senha não pode ser alterada. Contacte um administrador do site."
+
+#: templates/wagtailadmin/account/notification_preferences.html:4
+#: templates/wagtailadmin/account/notification_preferences.html:6
+msgid "Notification Preferences"
+msgstr "Preferências de notificação"
+
+#: templates/wagtailadmin/account/notification_preferences.html:16
+msgid "Update"
+msgstr "Atualizar"
+
+#: templates/wagtailadmin/account/password_reset/complete.html:4
+#: templates/wagtailadmin/account/password_reset/confirm.html:42
+#: templates/wagtailadmin/account/password_reset/done.html:4
+#: templates/wagtailadmin/account/password_reset/form.html:4
+#: templates/wagtailadmin/account/password_reset/form.html:37
+msgid "Reset password"
+msgstr "Alterar senha"
+
+#: templates/wagtailadmin/account/password_reset/complete.html:15
+msgid "Password change successful"
+msgstr "Senha alterada com sucesso"
+
+#: templates/wagtailadmin/account/password_reset/complete.html:16
+msgid "Login"
+msgstr "Login"
+
+#: templates/wagtailadmin/account/password_reset/confirm.html:4
+#: templates/wagtailadmin/account/password_reset/confirm.html:26
+msgid "Set your new password"
+msgstr "Insira a sua nova senha"
+
+#: templates/wagtailadmin/account/password_reset/confirm.html:19
+msgid "The passwords do not match. Please try again."
+msgstr "As senhas são diferentes. Tente novamente."
+
+#: templates/wagtailadmin/account/password_reset/done.html:15
+msgid "Check your email"
+msgstr "Verifique o seu email"
+
+#: templates/wagtailadmin/account/password_reset/done.html:16
+msgid "A link to reset your password has been emailed to you."
+msgstr "Um link para alterar a sua senha foi-lhe enviado para o seu email."
+
+#: templates/wagtailadmin/account/password_reset/email.txt:2
+msgid "Please follow the link below to reset your password"
+msgstr "Por favor clique no link abaixo para alterar a sua senha"
+
+#: templates/wagtailadmin/account/password_reset/email_subject.txt:2
+msgid "Password reset"
+msgstr "Alterar senha"
+
+#: templates/wagtailadmin/account/password_reset/form.html:27
+msgid "Reset your password"
+msgstr "Alterar a sua senha"
+
+#: templates/wagtailadmin/chooser/_link_types.html:5
+#: templates/wagtailadmin/chooser/_link_types.html:7
+msgid "Internal link"
+msgstr "Link interno"
+
+#: templates/wagtailadmin/chooser/_link_types.html:11
+#: templates/wagtailadmin/chooser/_link_types.html:13
+msgid "External link"
+msgstr "Link externo"
+
+#: templates/wagtailadmin/chooser/_link_types.html:17
+#: templates/wagtailadmin/chooser/_link_types.html:19
+msgid "Email link"
+msgstr "Link de email"
+
+#: templates/wagtailadmin/chooser/_search_form.html:7
+#: templates/wagtailadmin/pages/search.html:3
+#: templates/wagtailadmin/pages/search.html:16
+#: templatetags/wagtailadmin_tags.py:36
+msgid "Search"
+msgstr "Procurar"
+
+#: templates/wagtailadmin/chooser/_search_results.html:3
+#: templatetags/wagtailadmin_tags.py:35
+msgid "Explorer"
+msgstr "Explorador"
+
+#: templates/wagtailadmin/chooser/_search_results.html:8
+#, python-format
+msgid ""
+"\n"
+" There is one match\n"
+" "
+msgid_plural ""
+"\n"
+" There are %(counter)s matches\n"
+" "
+msgstr[0] ""
+"\n"
+" Existe uma correspondência\n"
+" "
+msgstr[1] ""
+"\n"
+" Existem %(counter)s correspondências\n"
+" "
+
+#: templates/wagtailadmin/chooser/browse.html:2
+#: templates/wagtailadmin/chooser/search.html:2
+#: templates/wagtailadmin/edit_handlers/page_chooser_panel.html:13
+msgid "Choose a page"
+msgstr "Escolher uma página"
+
+#: templates/wagtailadmin/chooser/email_link.html:2
+msgid "Add an email link"
+msgstr "Adicionar link de email"
+
+#: templates/wagtailadmin/chooser/email_link.html:14
+#: templates/wagtailadmin/chooser/external_link.html:14
+msgid "Insert link"
+msgstr "Inserir link"
+
+#: templates/wagtailadmin/chooser/external_link.html:2
+msgid "Add an external link"
+msgstr "Adicionar link externo"
+
+#: templates/wagtailadmin/edit_handlers/chooser_panel.html:20
+msgid "Clear choice"
+msgstr "Limpar escolha"
+
+#: templates/wagtailadmin/edit_handlers/chooser_panel.html:22
+msgid "Choose another item"
+msgstr "Escolher outro item"
+
+#: templates/wagtailadmin/edit_handlers/chooser_panel.html:27
+msgid "Choose an item"
+msgstr "Escolher um item"
+
+#: templates/wagtailadmin/edit_handlers/inline_panel_child.html:5
+msgid "Move up"
+msgstr "Mover para cima"
+
+#: templates/wagtailadmin/edit_handlers/inline_panel_child.html:6
+msgid "Move down"
+msgstr "Mover para baixo"
+
+#: templates/wagtailadmin/edit_handlers/inline_panel_child.html:8
+#: templates/wagtailadmin/pages/confirm_delete.html:7
+#: templates/wagtailadmin/pages/edit.html:45
+#: templates/wagtailadmin/pages/list.html:84
+#: templates/wagtailadmin/pages/list.html:205
+msgid "Delete"
+msgstr "Eliminar"
+
+#: templates/wagtailadmin/edit_handlers/page_chooser_panel.html:12
+msgid "Choose another page"
+msgstr "Escolher outra página"
+
+#: templates/wagtailadmin/home/pages_for_moderation.html:5
+msgid "Pages awaiting moderation"
+msgstr "Páginas a aguardar moderação"
+
+#: templates/wagtailadmin/home/pages_for_moderation.html:13
+#: templates/wagtailadmin/home/recent_edits.html:12
+#: templates/wagtailadmin/pages/list.html:121
+#: templates/wagtailadmin/pages/list.html:124
+msgid "Title"
+msgstr "Título"
+
+#: templates/wagtailadmin/home/pages_for_moderation.html:14
+#: templates/wagtailadmin/pages/list.html:22
+msgid "Parent"
+msgstr "Ascendente (pai)"
+
+#: templates/wagtailadmin/home/pages_for_moderation.html:15
+#: templates/wagtailadmin/pages/list.html:24
+#: templates/wagtailadmin/pages/list.html:133
+#: templates/wagtailadmin/pages/list.html:136
+msgid "Type"
+msgstr "Tipo"
+
+#: templates/wagtailadmin/home/pages_for_moderation.html:16
+msgid "Edited"
+msgstr "Editado"
+
+#: templates/wagtailadmin/home/pages_for_moderation.html:23
+#: templates/wagtailadmin/home/recent_edits.html:21
+#: templates/wagtailadmin/pages/list.html:176
+#: templates/wagtailadmin/pages/list.html:190
+msgid "Edit this page"
+msgstr "Editar esta página"
+
+#: templates/wagtailadmin/home/pages_for_moderation.html:28
+#: templates/wagtailadmin/pages/_moderator_userbar.html:12
+#: templates/wagtailadmin/userbar/item_page_approve.html:8
+msgid "Approve"
+msgstr "Aprovar"
+
+#: templates/wagtailadmin/home/pages_for_moderation.html:34
+#: templates/wagtailadmin/pages/_moderator_userbar.html:17
+#: templates/wagtailadmin/userbar/item_page_reject.html:8
+msgid "Reject"
+msgstr "Rejeitar"
+
+#: templates/wagtailadmin/home/pages_for_moderation.html:37
+#: templates/wagtailadmin/home/recent_edits.html:23
+#: templates/wagtailadmin/pages/_moderator_userbar.html:9
+#: templates/wagtailadmin/pages/list.html:72
+#: templates/wagtailadmin/pages/list.html:190
+#: templates/wagtailadmin/userbar/item_page_edit.html:5
+msgid "Edit"
+msgstr "Editar"
+
+#: templates/wagtailadmin/home/pages_for_moderation.html:38
+#: templates/wagtailadmin/pages/create.html:41
+#: templates/wagtailadmin/pages/edit.html:56
+#: templates/wagtailadmin/pages/preview.html:5
+msgid "Preview"
+msgstr "Pre-visualizar"
+
+#: templates/wagtailadmin/home/recent_edits.html:5
+msgid "Your most recent edits"
+msgstr "Suas últimas edições"
+
+#: templates/wagtailadmin/home/recent_edits.html:13
+msgid "Date"
+msgstr "Data"
+
+#: templates/wagtailadmin/home/recent_edits.html:14
+#: templates/wagtailadmin/pages/edit.html:18
+#: templates/wagtailadmin/pages/list.html:25
+#: templates/wagtailadmin/pages/list.html:142
+#: templates/wagtailadmin/pages/list.html:145
+msgid "Status"
+msgstr "Estado"
+
+#: templates/wagtailadmin/home/recent_edits.html:25
+#: templates/wagtailadmin/pages/list.html:75
+#: templates/wagtailadmin/pages/list.html:193
+msgid "Draft"
+msgstr "Rascunho"
+
+#: templates/wagtailadmin/home/recent_edits.html:28
+#: templates/wagtailadmin/pages/list.html:78
+#: templates/wagtailadmin/pages/list.html:196
+msgid "Live"
+msgstr "Publicado"
+
+#: templates/wagtailadmin/home/site_summary.html:3
+msgid "Site summary"
+msgstr "Resumo do Site"
+
+#: templates/wagtailadmin/home/site_summary.html:7
+#, python-format
+msgid ""
+"\n"
+" %(total_pages)s Page\n"
+" "
+msgid_plural ""
+"\n"
+" %(total_pages)s Pages\n"
+" "
+msgstr[0] ""
+"\n"
+" %(total_pages)s Página\n"
+" "
+msgstr[1] ""
+"\n"
+" %(total_pages)s Páginas\n"
+" "
+
+#: templates/wagtailadmin/home/site_summary.html:16
+#, python-format
+msgid ""
+"\n"
+" %(total_images)s Image\n"
+" "
+msgid_plural ""
+"\n"
+" %(total_images)s Images\n"
+" "
+msgstr[0] ""
+"\n"
+" %(total_images)s Imagem\n"
+" "
+msgstr[1] ""
+"\n"
+" %(total_images)s Imagens\n"
+" "
+
+#: templates/wagtailadmin/home/site_summary.html:25
+#, python-format
+msgid ""
+"\n"
+" %(total_docs)s Document\n"
+" "
+msgid_plural ""
+"\n"
+" %(total_docs)s Documents\n"
+" "
+msgstr[0] ""
+"\n"
+" %(total_docs)s Documento\n"
+" "
+msgstr[1] ""
+"\n"
+" %(total_docs)s Documentos\n"
+" "
+
+#: templates/wagtailadmin/notifications/approved.html:1
+#, python-format
+msgid "The page \"%(title)s\" has been approved"
+msgstr "A página \"%(title)s\" foi aprovada"
+
+#: templates/wagtailadmin/notifications/approved.html:2
+#, python-format
+msgid "The page \"%(title)s\" has been approved."
+msgstr "A página \"%(title)s\" foi aprovada."
+
+#: templates/wagtailadmin/notifications/approved.html:4
+msgid "You can view the page here:"
+msgstr "Pode visualizar a página aqui:"
+
+#: templates/wagtailadmin/notifications/base_notification.html:3
+msgid "Edit your notification preferences here:"
+msgstr "Edite as suas preferências de notificação aqui:"
+
+#: templates/wagtailadmin/notifications/rejected.html:1
+#, python-format
+msgid "The page \"%(title)s\" has been rejected"
+msgstr "A página \"%(title)s\" foi rejeitada"
+
+#: templates/wagtailadmin/notifications/rejected.html:2
+#, python-format
+msgid "The page \"%(title)s\" has been rejected."
+msgstr "A página \"%(title)s\" foi rejeitada"
+
+#: templates/wagtailadmin/notifications/rejected.html:4
+#: templates/wagtailadmin/notifications/submitted.html:5
+msgid "You can edit the page here:"
+msgstr "Você pode editar a página aqui:"
+
+#: templates/wagtailadmin/notifications/submitted.html:1
+#, python-format
+msgid "The page \"%(page)s\" has been submitted for moderation"
+msgstr "A página \"%(page)s\" foi enviada para moderação"
+
+#: templates/wagtailadmin/notifications/submitted.html:2
+#, python-format
+msgid "The page \"%(page)s\" has been submitted for moderation."
+msgstr "A página \"%(page)s\" foi enviada para moderação."
+
+#: templates/wagtailadmin/notifications/submitted.html:4
+msgid "You can preview the page here:"
+msgstr "Pode pre-visualizar a página aqui:"
+
+#: templates/wagtailadmin/page_privacy/ancestor_privacy.html:2
+#: templates/wagtailadmin/page_privacy/set_privacy.html:2
+msgid "Page privacy"
+msgstr "Privacidade da página"
+
+#: templates/wagtailadmin/page_privacy/ancestor_privacy.html:6
+msgid "This page has been made private by a parent page."
+msgstr "Esta página foi classificada como privada por uma página ascendente"
+
+#: templates/wagtailadmin/page_privacy/ancestor_privacy.html:7
+msgid "You can edit the privacy settings on:"
+msgstr "Você pode editar as configurações de privacidade em:"
+
+#: templates/wagtailadmin/page_privacy/set_privacy.html:6
+msgid "Note: privacy changes apply to all children of this page too."
+msgstr ""
+"Nota: as alterações de privacidade também serão aplicadas a todas as "
+"páginas filhas desta página."
+
+#: templates/wagtailadmin/pages/_moderator_userbar.html:4
+#, python-format
+msgid ""
+"\n"
+" Previewing '%(title)s', submitted by %(submitted_by)s on "
+"%(submitted_on)s.\n"
+" "
+msgstr ""
+"\n"
+" Pre-visualizando '%(title)s', enviado por %(submitted_by)s em "
+"%(submitted_on)s.\n"
+" "
+
+#: templates/wagtailadmin/pages/_privacy_indicator.html:9
+msgid "Privacy"
+msgstr "Privacidade"
+
+#: templates/wagtailadmin/pages/_privacy_indicator.html:14
+#: templates/wagtailadmin/pages/_privacy_indicator.html:20
+msgid "Private"
+msgstr "Privada"
+
+#: templates/wagtailadmin/pages/add_subpage.html:6
+#, python-format
+msgid "Create a page in %(title)s"
+msgstr "Criar uma página em %(title)s"
+
+#: templates/wagtailadmin/pages/add_subpage.html:9
+msgid "Create a page in"
+msgstr "Criar uma página em"
+
+#: templates/wagtailadmin/pages/add_subpage.html:13
+msgid "Choose which type of page you'd like to create."
+msgstr "Escolha o tipo de página que gostaria de criar."
+
+#: templates/wagtailadmin/pages/add_subpage.html:26
+#, python-format
+msgid "Pages using %(page_type)s"
+msgstr "Páginas usando %(page_type)s"
+
+#: templates/wagtailadmin/pages/confirm_delete.html:3
+#, python-format
+msgid "Delete %(title)s"
+msgstr "Eliminar %(title)s"
+
+#: templates/wagtailadmin/pages/confirm_delete.html:12
+msgid "Are you sure you want to delete this page?"
+msgstr "Tem certeza que deseja eliminar esta página?"
+
+#: templates/wagtailadmin/pages/confirm_delete.html:14
+#, python-format
+msgid ""
+"\n"
+" This will also delete one more subpage.\n"
+" "
+msgid_plural ""
+"\n"
+" This will also delete %(descendant_count)s more "
+"subpages.\n"
+" "
+msgstr[0] ""
+"\n"
+" Isto também eliminará uma ou mais sub-páginas.\n"
+" "
+msgstr[1] ""
+"\n"
+" Isto também eliminará mais %(descendant_count)s sub-"
+"páginas.\n"
+" "
+
+#: templates/wagtailadmin/pages/confirm_delete.html:22
+msgid ""
+"Alternatively you can unpublish the page. This removes the page from public "
+"view and you can edit or publish it again later."
+msgstr ""
+"Em alternativa, poderá despublicar a página. Assim, a página deixará de "
+"estar visível publicamente, podendo você voltar a editá-la ou publicá-la "
+"mais tarde."
+
+#: templates/wagtailadmin/pages/confirm_delete.html:26
+msgid "Delete it"
+msgstr "Eliminá-la"
+
+#: templates/wagtailadmin/pages/confirm_delete.html:26
+msgid "Unpublish it"
+msgstr "Despublicá-la"
+
+#: templates/wagtailadmin/pages/confirm_move.html:3
+#, python-format
+msgid "Move %(title)s"
+msgstr "Mover %(title)s"
+
+#: templates/wagtailadmin/pages/confirm_move.html:6
+#: templates/wagtailadmin/pages/list.html:81
+#: templates/wagtailadmin/pages/list.html:199
+msgid "Move"
+msgstr "Mover"
+
+#: templates/wagtailadmin/pages/confirm_move.html:11
+#, python-format
+msgid "Are you sure you want to move this page into '%(title)s'?"
+msgstr "Tem certeza que deseja mover esta página para dentro de '%(title)s'?"
+
+#: templates/wagtailadmin/pages/confirm_move.html:13
+#, python-format
+msgid ""
+"Are you sure you want to move this page and all of its children into "
+"'%(title)s'?"
+msgstr ""
+"Tem a certeza que deseja mover esta página e todas as suas páginas filhas "
+"para dentro de '%(title)s'?"
+
+#: templates/wagtailadmin/pages/confirm_move.html:18
+msgid "Yes, move this page"
+msgstr "Sim, mover esta página"
+
+#: templates/wagtailadmin/pages/confirm_unpublish.html:3
+#, python-format
+msgid "Unpublish %(title)s"
+msgstr "Despublicar %(title)s"
+
+#: templates/wagtailadmin/pages/confirm_unpublish.html:6
+#: templates/wagtailadmin/pages/edit.html:42
+#: templates/wagtailadmin/pages/list.html:87
+#: templates/wagtailadmin/pages/list.html:208
+msgid "Unpublish"
+msgstr "Despublicar"
+
+#: templates/wagtailadmin/pages/confirm_unpublish.html:10
+msgid "Are you sure you want to unpublish this page?"
+msgstr "Tem certeza que deseja despublicar esta página?"
+
+#: templates/wagtailadmin/pages/confirm_unpublish.html:13
+msgid "Yes, unpublish it"
+msgstr "Sim, quero despublicar"
+
+#: templates/wagtailadmin/pages/content_type_use.html:7
+msgid "Pages using"
+msgstr "Páginas usando"
+
+#: templates/wagtailadmin/pages/copy.html:3
+#, python-format
+msgid "Copy %(title)s"
+msgstr "Copiar %(title)s"
+
+#: templates/wagtailadmin/pages/copy.html:6
+#: templates/wagtailadmin/pages/list.html:202
+msgid "Copy"
+msgstr "Copiar"
+
+#: templates/wagtailadmin/pages/copy.html:26
+msgid "Copy this page"
+msgstr "Copiar esta página"
+
+#: templates/wagtailadmin/pages/create.html:5
+#, python-format
+msgid "New %(page_type)s"
+msgstr "Nova %(page_type)s"
+
+#: templates/wagtailadmin/pages/create.html:15
+msgid "New"
+msgstr "Nova"
+
+#: templates/wagtailadmin/pages/create.html:29
+msgid "Save as draft"
+msgstr "Guardar como rascunho"
+
+#: templates/wagtailadmin/pages/create.html:33
+#: templates/wagtailadmin/pages/edit.html:48
+msgid "Publish"
+msgstr "Publicar"
+
+#: templates/wagtailadmin/pages/create.html:35
+#: templates/wagtailadmin/pages/edit.html:50
+msgid "Submit for moderation"
+msgstr "Enviar para moderação"
+
+#: templates/wagtailadmin/pages/edit.html:5
+#, python-format
+msgid "Editing %(title)s"
+msgstr "Editando %(title)s"
+
+#: templates/wagtailadmin/pages/edit.html:15
+#, python-format
+msgid "Editing %(title)s"
+msgstr "Editando %(title)s"
+
+#: templates/wagtailadmin/pages/edit.html:38
+msgid "Save draft"
+msgstr "Guardar rascunho"
+
+#: templates/wagtailadmin/pages/edit.html:77
+#, python-format
+msgid "Last modified: %(last_mod)s"
+msgstr "Última modificação: %(last_mod)s"
+
+#: templates/wagtailadmin/pages/edit.html:79
+#, python-format
+msgid "by %(modified_by)s"
+msgstr "por %(modified_by)s"
+
+#: templates/wagtailadmin/pages/index.html:3
+#, python-format
+msgid "Exploring %(title)s"
+msgstr "Explorando %(title)s"
+
+#: templates/wagtailadmin/pages/list.html:90
+#: templates/wagtailadmin/pages/list.html:211
+msgid "Add child page"
+msgstr "Adicionar página filha"
+
+#: templates/wagtailadmin/pages/list.html:112
+msgid "Disable ordering of child pages"
+msgstr "Desativar ordenação de páginas filhas"
+
+#: templates/wagtailadmin/pages/list.html:112
+#: templates/wagtailadmin/pages/list.html:114
+msgid "Order"
+msgstr "Ordem"
+
+#: templates/wagtailadmin/pages/list.html:114
+msgid "Enable ordering of child pages"
+msgstr "Ativar ordenação de páginas filhas"
+
+#: templates/wagtailadmin/pages/list.html:158
+msgid "Drag"
+msgstr "Arrastar"
+
+#: templates/wagtailadmin/pages/list.html:237
+#: templates/wagtailadmin/pages/list.html:241
+#, python-format
+msgid "Explorer subpages of '%(title)s'"
+msgstr "Explorar sub-páginas de %(title)s"
+
+#: templates/wagtailadmin/pages/list.html:237
+#: templates/wagtailadmin/pages/list.html:241
+#: templates/wagtailadmin/pages/list.html:245
+msgid "Explore"
+msgstr "Explorar"
+
+#: templates/wagtailadmin/pages/list.html:245
+#, python-format
+msgid "Explore child pages of '%(title)s'"
+msgstr "Explorar as páginas filhas de '%(title)s'"
+
+#: templates/wagtailadmin/pages/list.html:247
+#, python-format
+msgid "Add a child page to '%(title)s'"
+msgstr "Adicionar página filha a %(title)s"
+
+#: templates/wagtailadmin/pages/list.html:247
+msgid "Add subpage"
+msgstr "Adicionar sub-página"
+
+#: templates/wagtailadmin/pages/list.html:256
+msgid "No pages have been created."
+msgstr "Nenhuma página foi criada."
+
+#: templates/wagtailadmin/pages/list.html:256
+#, python-format
+msgid "Why not add one?"
+msgstr "Porque não adicionar uma?"
+
+#: templates/wagtailadmin/pages/list.html:263
+#, python-format
+msgid ""
+"\n"
+" Page %(page_number)s of %(num_pages)s.\n"
+" "
+msgstr ""
+"\n"
+" Página %(page_number)s de %(num_pages)s.\n"
+" "
+
+#: templates/wagtailadmin/pages/list.html:269
+#: templates/wagtailadmin/pages/search_results.html:35
+#: templates/wagtailadmin/pages/search_results.html:37
+#: templates/wagtailadmin/pages/usage_results.html:13
+#: templates/wagtailadmin/shared/pagination_nav.html:21
+#: templates/wagtailadmin/shared/pagination_nav.html:23
+#: templates/wagtailadmin/shared/pagination_nav.html:25
+msgid "Previous"
+msgstr "Anterior"
+
+#: templates/wagtailadmin/pages/list.html:274
+#: templates/wagtailadmin/pages/search_results.html:44
+#: templates/wagtailadmin/pages/search_results.html:46
+#: templates/wagtailadmin/pages/usage_results.html:18
+#: templates/wagtailadmin/shared/pagination_nav.html:32
+#: templates/wagtailadmin/shared/pagination_nav.html:34
+#: templates/wagtailadmin/shared/pagination_nav.html:36
+msgid "Next"
+msgstr "Próxima"
+
+#: templates/wagtailadmin/pages/move_choose_destination.html:3
+#, python-format
+msgid "Select a new parent page for %(title)s"
+msgstr "Selecione uma nova página ascendente para %(title)s"
+
+#: templates/wagtailadmin/pages/move_choose_destination.html:7
+#, python-format
+msgid "Select a new parent page for %(title)s"
+msgstr "Selecione uma nova página ascendente para %(title)s"
+
+#: templates/wagtailadmin/pages/search_results.html:6
+#, python-format
+msgid ""
+"\n"
+" There is one matching page\n"
+" "
+msgid_plural ""
+"\n"
+" There are %(counter)s matching pages\n"
+" "
+msgstr[0] ""
+"\n"
+" Existe uma página correspondente\n"
+" "
+msgstr[1] ""
+"\n"
+" Existem %(counter)s páginas correspondentes\n"
+" "
+
+#: templates/wagtailadmin/pages/search_results.html:16
+msgid "Other searches"
+msgstr "Outras pesquisas"
+
+#: templates/wagtailadmin/pages/search_results.html:18
+msgid "Images"
+msgstr "Imagens"
+
+#: templates/wagtailadmin/pages/search_results.html:19
+msgid "Documents"
+msgstr "Documentos"
+
+#: templates/wagtailadmin/pages/search_results.html:20
+msgid "Users"
+msgstr "Utilizadores"
+
+#: templates/wagtailadmin/pages/search_results.html:28
+#: templates/wagtailadmin/pages/usage_results.html:7
+#, python-format
+msgid ""
+"\n"
+" Page %(page_number)s of %(num_pages)s.\n"
+" "
+msgstr ""
+"\n"
+" Página %(page_number)s de %(num_pages)s.\n"
+" "
+
+#: templates/wagtailadmin/pages/search_results.html:54
+#, python-format
+msgid "Sorry, no pages match \"%(query_string)s\""
+msgstr "Desculpe, nenhuma página satisfaz \"%(query_string)s\""
+
+#: templates/wagtailadmin/pages/search_results.html:56
+msgid "Enter a search term above"
+msgstr "Introduza um termo de pesquisa acima"
+
+#: templates/wagtailadmin/pages/usage_results.html:24
+msgid "No pages use"
+msgstr "Nenhuma página usa"
+
+#: templates/wagtailadmin/shared/breadcrumb.html:6
+msgid "Home"
+msgstr "Início"
+
+#: templates/wagtailadmin/shared/datetimepicker_translations.html:6
+msgid "January"
+msgstr "Janeiro"
+
+#: templates/wagtailadmin/shared/datetimepicker_translations.html:7
+msgid "February"
+msgstr "Fevereiro"
+
+#: templates/wagtailadmin/shared/datetimepicker_translations.html:8
+msgid "March"
+msgstr "Março"
+
+#: templates/wagtailadmin/shared/datetimepicker_translations.html:9
+msgid "April"
+msgstr "Abril"
+
+#: templates/wagtailadmin/shared/datetimepicker_translations.html:10
+msgid "May"
+msgstr "Maio"
+
+#: templates/wagtailadmin/shared/datetimepicker_translations.html:11
+msgid "June"
+msgstr "Junho"
+
+#: templates/wagtailadmin/shared/datetimepicker_translations.html:12
+msgid "July"
+msgstr "Julho"
+
+#: templates/wagtailadmin/shared/datetimepicker_translations.html:13
+msgid "August"
+msgstr "Agosto"
+
+#: templates/wagtailadmin/shared/datetimepicker_translations.html:14
+msgid "September"
+msgstr "Setembro"
+
+#: templates/wagtailadmin/shared/datetimepicker_translations.html:15
+msgid "October"
+msgstr "Outubro"
+
+#: templates/wagtailadmin/shared/datetimepicker_translations.html:16
+msgid "November"
+msgstr "Novembro"
+
+#: templates/wagtailadmin/shared/datetimepicker_translations.html:17
+msgid "December"
+msgstr "Dezembro"
+
+#: templates/wagtailadmin/shared/datetimepicker_translations.html:20
+msgid "Sun"
+msgstr "Dom"
+
+#: templates/wagtailadmin/shared/datetimepicker_translations.html:21
+msgid "Mon"
+msgstr "Seg"
+
+#: templates/wagtailadmin/shared/datetimepicker_translations.html:22
+msgid "Tue"
+msgstr "Ter"
+
+#: templates/wagtailadmin/shared/datetimepicker_translations.html:23
+msgid "Wed"
+msgstr "Qua"
+
+#: templates/wagtailadmin/shared/datetimepicker_translations.html:24
+msgid "Thu"
+msgstr "Qui"
+
+#: templates/wagtailadmin/shared/datetimepicker_translations.html:25
+msgid "Fri"
+msgstr "Sex"
+
+#: templates/wagtailadmin/shared/datetimepicker_translations.html:26
+msgid "Sat"
+msgstr "Sáb"
+
+#: templates/wagtailadmin/shared/header.html:23
+#, python-format
+msgid "Used %(useage_count)s time"
+msgid_plural "Used %(useage_count)s times"
+msgstr[0] "Em uso %(useage_count)s time"
+msgstr[1] "Em uso %(useage_count)s times"
+
+#: templates/wagtailadmin/shared/main_nav.html:10
+msgid "Account settings"
+msgstr "Configurações da Conta"
+
+#: templates/wagtailadmin/shared/main_nav.html:11
+msgid "Log out"
+msgstr "Sair"
+
+#: templates/wagtailadmin/shared/pagination_nav.html:16
+#, python-format
+msgid "Page %(page_num)s of %(total_pages)s."
+msgstr "Página %(page_num)s de %(total_pages)s."
+
+#: templates/wagtailadmin/userbar/base.html:4
+msgid "User bar"
+msgstr "Barra de utilizador"
+
+#: templates/wagtailadmin/userbar/base.html:14
+msgid "Go to Wagtail admin interface"
+msgstr "Ir para a página admin do Wagtail"
+
+#: templates/wagtailadmin/userbar/item_page_add.html:5
+msgid "Add another page at this level"
+msgstr "Adicionar outra página a este nível"
+
+#: templates/wagtailadmin/userbar/item_page_add.html:5
+msgid "Add"
+msgstr "Adicionar"
+
+#: views/account.py:39
+msgid "Your password has been changed successfully!"
+msgstr "A sua senha foi alterada com sucesso!"
+
+#: views/account.py:60
+msgid "Your preferences have been updated successfully!"
+msgstr "As suas preferências foram atualizadas com sucesso!"
+
+#: views/pages.py:169 views/pages.py:286
+msgid "Go live date/time must be before expiry date/time"
+msgstr "A data/hora de publicação tem de ser anterior à data/hora terminal"
+
+#: views/pages.py:179 views/pages.py:296
+msgid "Expiry date/time must be in the future"
+msgstr "A data/hora terminal tem de ocorrer no futuro"
+
+#: views/pages.py:219 views/pages.py:351 views/pages.py:791
+msgid "Page '{0}' published."
+msgstr "Página '{0}' publicada."
+
+#: views/pages.py:221 views/pages.py:353
+msgid "Page '{0}' submitted for moderation."
+msgstr "Página '{0}' enviada para moderação."
+
+#: views/pages.py:224
+msgid "Page '{0}' created."
+msgstr "Página '{0}' criada."
+
+#: views/pages.py:233
+msgid "The page could not be created due to validation errors"
+msgstr "Esta página não pôde ser criada devido a erros na validação"
+
+#: views/pages.py:356
+msgid "Page '{0}' updated."
+msgstr "Página '{0}' atualizada."
+
+#: views/pages.py:365
+msgid "The page could not be saved due to validation errors"
+msgstr "A página não pôde ser guardada devido a erros na validação"
+
+#: views/pages.py:378
+msgid "This page is currently awaiting moderation"
+msgstr "Essa página aguarda por moderação"
+
+#: views/pages.py:409
+msgid "Page '{0}' deleted."
+msgstr "Página '{0}' eliminada."
+
+#: views/pages.py:575
+msgid "Page '{0}' unpublished."
+msgstr "Página '{0}' despublicada."
+
+#: views/pages.py:627
+msgid "Page '{0}' moved."
+msgstr "Página '{0}' movida."
+
+#: views/pages.py:709
+msgid "Page '{0}' and {1} subpages copied."
+msgstr "Página '{0}' e {1} sub-páginas copiadas."
+
+#: views/pages.py:711
+msgid "Page '{0}' copied."
+msgstr "Página '{0}' copiada."
+
+#: views/pages.py:785 views/pages.py:804 views/pages.py:824
+msgid "The page '{0}' is not currently awaiting moderation."
+msgstr "A página '{0}' já não está aguarda moderação."
+
+#: views/pages.py:810
+msgid "Page '{0}' rejected for publication."
+msgstr "Página '{0}' rejeitada para publicação."
diff --git a/wagtail/wagtailadmin/migrations/0001_create_admin_access_permissions.py b/wagtail/wagtailadmin/migrations/0001_create_admin_access_permissions.py
new file mode 100644
index 000000000..5f198a01d
--- /dev/null
+++ b/wagtail/wagtailadmin/migrations/0001_create_admin_access_permissions.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+def create_admin_access_permissions(apps, schema_editor):
+ ContentType = apps.get_model('contenttypes.ContentType')
+ Permission = apps.get_model('auth.Permission')
+ Group = apps.get_model('auth.Group')
+
+ # Add a fake content type to hang the 'can access Wagtail admin' permission off.
+ # The fact that this doesn't correspond to an actual defined model shouldn't matter, I hope...
+ wagtailadmin_content_type = ContentType.objects.create(
+ app_label='wagtailadmin',
+ model='admin',
+ name='Wagtail admin'
+ )
+
+ # Create admin permission
+ admin_permission = Permission.objects.create(
+ content_type=wagtailadmin_content_type,
+ codename='access_admin',
+ name='Can access Wagtail admin'
+ )
+
+ # Assign it to Editors and Moderators groups
+ for group in Group.objects.filter(name__in=['Editors', 'Moderators']):
+ group.permissions.add(admin_permission)
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ # Need to run wagtailcores initial data migration to make sure the groups are created
+ ('wagtailcore', '0002_initial_data'),
+ ]
+
+ operations = [
+ migrations.RunPython(create_admin_access_permissions),
+ ]
diff --git a/wagtail/wagtailadmin/migrations/__init__.py b/wagtail/wagtailadmin/migrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/wagtail/wagtailadmin/static/wagtailadmin/js/vendor/modernizr-2.6.2.min.js b/wagtail/wagtailadmin/static/wagtailadmin/js/vendor/modernizr-2.6.2.min.js
index 7eb2abe75..f18b5c2c9 100644
--- a/wagtail/wagtailadmin/static/wagtailadmin/js/vendor/modernizr-2.6.2.min.js
+++ b/wagtail/wagtailadmin/static/wagtailadmin/js/vendor/modernizr-2.6.2.min.js
@@ -1,4 +1,4 @@
/* Modernizr 2.8.3 (Custom Build) | MIT & BSD
- * Build: http://modernizr.com/download/#-cssanimations-cssreflections-csstransforms-csstransforms3d-csstransitions-draganddrop-shiv-mq-cssclasses-addtest-prefixed-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes-file_api-load
+ * Build: http://modernizr.com/download/#-cssanimations-cssreflections-csstransforms-csstransforms3d-csstransitions-draganddrop-touch-shiv-mq-cssclasses-addtest-prefixed-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes-file_api-load
*/
-;window.Modernizr=function(a,b,c){function B(a){j.cssText=a}function C(a,b){return B(m.join(a+";")+(b||""))}function D(a,b){return typeof a===b}function E(a,b){return!!~(""+a).indexOf(b)}function F(a,b){for(var d in a){var e=a[d];if(!E(e,"-")&&j[e]!==c)return b=="pfx"?e:!0}return!1}function G(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:D(f,"function")?f.bind(d||b):f}return!1}function H(a,b,c){var d=a.charAt(0).toUpperCase()+a.slice(1),e=(a+" "+o.join(d+" ")+d).split(" ");return D(b,"string")||D(b,"undefined")?F(e,b):(e=(a+" "+p.join(d+" ")+d).split(" "),G(e,b,c))}var d="2.8.3",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k,l={}.toString,m=" -webkit- -moz- -o- -ms- ".split(" "),n="Webkit Moz O ms",o=n.split(" "),p=n.toLowerCase().split(" "),q={},r={},s={},t=[],u=t.slice,v,w=function(a,c,d,e){var f,i,j,k,l=b.createElement("div"),m=b.body,n=m||b.createElement("body");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:h+(d+1),l.appendChild(j);return f=["",'"].join(""),l.id=h,(m?l:n).innerHTML+=f,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=g.style.overflow,g.style.overflow="hidden",g.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),g.style.overflow=k),!!i},x=function(b){var c=a.matchMedia||a.msMatchMedia;if(c)return c(b)&&c(b).matches||!1;var d;return w("@media "+b+" { #"+h+" { position: absolute; } }",function(b){d=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle)["position"]=="absolute"}),d},y=function(){function d(d,e){e=e||b.createElement(a[d]||"div"),d="on"+d;var f=d in e;return f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=D(e[d],"function"),D(e[d],"undefined")||(e[d]=c),e.removeAttribute(d))),e=null,f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),z={}.hasOwnProperty,A;!D(z,"undefined")&&!D(z.call,"undefined")?A=function(a,b){return z.call(a,b)}:A=function(a,b){return b in a&&D(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=u.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(u.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(u.call(arguments)))};return e}),q.draganddrop=function(){var a=b.createElement("div");return"draggable"in a||"ondragstart"in a&&"ondrop"in a},q.cssanimations=function(){return H("animationName")},q.cssreflections=function(){return H("boxReflect")},q.csstransforms=function(){return!!H("transform")},q.csstransforms3d=function(){var a=!!H("perspective");return a&&"webkitPerspective"in g.style&&w("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(b,c){a=b.offsetLeft===9&&b.offsetHeight===3}),a},q.csstransitions=function(){return H("transition")};for(var I in q)A(q,I)&&(v=I.toLowerCase(),e[v]=q[I](),t.push((e[v]?"":"no-")+v));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)A(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},B(""),i=k=null,function(a,b){function l(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function m(){var a=s.elements;return typeof a=="string"?a.split(" "):a}function n(a){var b=j[a[h]];return b||(b={},i++,a[h]=i,j[i]=b),b}function o(a,c,d){c||(c=b);if(k)return c.createElement(a);d||(d=n(c));var g;return d.cache[a]?g=d.cache[a].cloneNode():f.test(a)?g=(d.cache[a]=d.createElem(a)).cloneNode():g=d.createElem(a),g.canHaveChildren&&!e.test(a)&&!g.tagUrn?d.frag.appendChild(g):g}function p(a,c){a||(a=b);if(k)return a.createDocumentFragment();c=c||n(a);var d=c.frag.cloneNode(),e=0,f=m(),g=f.length;for(;e",g="hidden"in a,k=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){g=!0,k=!0}})();var s={elements:d.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:c,shivCSS:d.shivCSS!==!1,supportsUnknownElements:k,shivMethods:d.shivMethods!==!1,type:"default",shivDocument:r,createElement:o,createDocumentFragment:p};a.html5=s,r(b)}(this,b),e._version=d,e._prefixes=m,e._domPrefixes=p,e._cssomPrefixes=o,e.mq=x,e.hasEvent=y,e.testProp=function(a){return F([a])},e.testAllProps=H,e.testStyles=w,e.prefixed=function(a,b,c){return b?H(a,b,c):H(a,"pfx")},g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+t.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f',a,""].join(""),l.id=h,(m?l:n).innerHTML+=f,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=g.style.overflow,g.style.overflow="hidden",g.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),g.style.overflow=k),!!i},x=function(b){var c=a.matchMedia||a.msMatchMedia;if(c)return c(b)&&c(b).matches||!1;var d;return w("@media "+b+" { #"+h+" { position: absolute; } }",function(b){d=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle)["position"]=="absolute"}),d},y=function(){function d(d,e){e=e||b.createElement(a[d]||"div"),d="on"+d;var f=d in e;return f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=D(e[d],"function"),D(e[d],"undefined")||(e[d]=c),e.removeAttribute(d))),e=null,f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),z={}.hasOwnProperty,A;!D(z,"undefined")&&!D(z.call,"undefined")?A=function(a,b){return z.call(a,b)}:A=function(a,b){return b in a&&D(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=u.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(u.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(u.call(arguments)))};return e}),q.touch=function(){var c;return"ontouchstart"in a||a.DocumentTouch&&b instanceof DocumentTouch?c=!0:w(["@media (",m.join("touch-enabled),("),h,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(a){c=a.offsetTop===9}),c},q.draganddrop=function(){var a=b.createElement("div");return"draggable"in a||"ondragstart"in a&&"ondrop"in a},q.cssanimations=function(){return H("animationName")},q.cssreflections=function(){return H("boxReflect")},q.csstransforms=function(){return!!H("transform")},q.csstransforms3d=function(){var a=!!H("perspective");return a&&"webkitPerspective"in g.style&&w("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(b,c){a=b.offsetLeft===9&&b.offsetHeight===3}),a},q.csstransitions=function(){return H("transition")};for(var I in q)A(q,I)&&(v=I.toLowerCase(),e[v]=q[I](),t.push((e[v]?"":"no-")+v));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)A(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},B(""),i=k=null,function(a,b){function l(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function m(){var a=s.elements;return typeof a=="string"?a.split(" "):a}function n(a){var b=j[a[h]];return b||(b={},i++,a[h]=i,j[i]=b),b}function o(a,c,d){c||(c=b);if(k)return c.createElement(a);d||(d=n(c));var g;return d.cache[a]?g=d.cache[a].cloneNode():f.test(a)?g=(d.cache[a]=d.createElem(a)).cloneNode():g=d.createElem(a),g.canHaveChildren&&!e.test(a)&&!g.tagUrn?d.frag.appendChild(g):g}function p(a,c){a||(a=b);if(k)return a.createDocumentFragment();c=c||n(a);var d=c.frag.cloneNode(),e=0,f=m(),g=f.length;for(;e",g="hidden"in a,k=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){g=!0,k=!0}})();var s={elements:d.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:c,shivCSS:d.shivCSS!==!1,supportsUnknownElements:k,shivMethods:d.shivMethods!==!1,type:"default",shivDocument:r,createElement:o,createDocumentFragment:p};a.html5=s,r(b)}(this,b),e._version=d,e._prefixes=m,e._domPrefixes=p,e._cssomPrefixes=o,e.mq=x,e.hasEvent=y,e.testProp=function(a){return F([a])},e.testAllProps=H,e.testStyles=w,e.prefixed=function(a,b,c){return b?H(a,b,c):H(a,"pfx")},g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+t.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f
+ {% blocktrans with type=page_type.get_verbose_name %}
+ Only pages of type "{{ type }}" may be chosen for this field. Search results will exclude pages of other types.
+ {% endblocktrans %}
+
+{% endif %}
+
{% if not is_searching %}
{% trans "Explorer" %}
{% include "wagtailadmin/shared/breadcrumb.html" with page=parent_page choosing=1 %}
-
{% else %}
- {% blocktrans count counter=pages.count %}
+ {% blocktrans count counter=pages|length %}
There is one match
{% plural %}
There are {{ counter }} matches
@@ -13,8 +21,10 @@
{% endif %}
-{% if is_searching %}
- {% include "wagtailadmin/pages/list.html" with choosing=1 show_parent=1 pages=pages parent_page=parent_page %}
-{% else %}
- {% include "wagtailadmin/pages/list.html" with choosing=1 allow_navigation=1 orderable=0 pages=pages parent_page=parent_page %}
+{% if pages %}
+ {% if is_searching %}
+ {% include "wagtailadmin/pages/list.html" with choosing=1 show_parent=1 pages=pages parent_page=parent_page %}
+ {% else %}
+ {% include "wagtailadmin/pages/list.html" with choosing=1 allow_navigation=1 orderable=0 pages=pages parent_page=parent_page %}
+ {% endif %}
{% endif %}
\ No newline at end of file
diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/chooser/browse.html b/wagtail/wagtailadmin/templates/wagtailadmin/chooser/browse.html
index 5714ddb45..98f758204 100644
--- a/wagtail/wagtailadmin/templates/wagtailadmin/chooser/browse.html
+++ b/wagtail/wagtailadmin/templates/wagtailadmin/chooser/browse.html
@@ -1,13 +1,17 @@
{% load i18n %}
-{% trans "Choose a page" as choose_str %}
-{% include "wagtailadmin/shared/header.html" with title=choose_str search_url="wagtailadmin_choose_page" icon="doc-empty-inverse" %}
+{% if page_types_restricted %}
+ {% trans "Choose" as choose_str %}
+ {% trans page_type.get_verbose_name as subtitle %}
+{% else %}
+ {% trans "Choose a page" as choose_str %}
+{% endif %}
+
+{% include "wagtailadmin/shared/header.html" with title=choose_str subtitle=subtitle search_url="wagtailadmin_choose_page" query_parameters="page_type="|add:page_type_string icon="doc-empty-inverse" %}
{% include 'wagtailadmin/chooser/_link_types.html' with current='internal' %}
- {# content in here will be replaced by live search results #}
-
{% include 'wagtailadmin/chooser/_search_results.html' %}
diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/page_privacy/set_privacy.html b/wagtail/wagtailadmin/templates/wagtailadmin/page_privacy/set_privacy.html
index 6298d6f5f..263908a26 100644
--- a/wagtail/wagtailadmin/templates/wagtailadmin/page_privacy/set_privacy.html
+++ b/wagtail/wagtailadmin/templates/wagtailadmin/page_privacy/set_privacy.html
@@ -3,7 +3,7 @@
{% include "wagtailadmin/shared/header.html" with title=title_str icon="locked" %}
-
{% trans "Note: privacy changes apply to all children of this page too." %}
+
{% trans "Privacy changes apply to all children of this page too." %}
{% if search_url %}
-