Merge branch 'master' into multiple-page-types-in-page-chooser

Conflicts:
	wagtail/wagtailadmin/views/chooser.py
This commit is contained in:
Nick Smith 2015-08-03 18:11:38 +01:00
commit b67426c86b
200 changed files with 2754 additions and 1993 deletions

View file

@ -12,7 +12,7 @@
"**/*.min.js",
"**/vendor/**/*.js",
"./wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/inline_panel.js",
"./wagtail/wagtailsearch/templates/wagtailsearch/editorspicks/includes/editorspicks_formset.js",
"./wagtail/contrib/wagtailsearchpromotions/templates/wagtailsearchpromotions/includes/searchpromotions_formset.js",
"./wagtail/wagtailusers/templates/wagtailusers/groups/includes/page_permissions_formset.js",
"./wagtail/wagtailsnippets/templates/wagtailsnippets/chooser/chosen.js",
"./wagtail/wagtailimages/templates/wagtailimages/chooser/image_chosen.js",

View file

@ -5,13 +5,23 @@ Changelog
~~~~~~~~~~~~~~~~
* Implemented the `specific()` method on PageQuerySet, to return pages as their most specific type
* "Promoted search results" has moved into its own module
* Elasticsearch backend now supports an experimental `ATOMIC_REBUILD` flag to keep the existing index available while the `update_index` task is running
* The wagtailapi module has been refactored to use Django REST Framework
* Implemented pagination in the page chooser modal
* Changed INSTALLED_APPS in project template to list apps in precedence order (Piet Delport)
* The `{% image %}` tag now supports filters on the image variable, e.g. `{% image primary_img|default:secondary_img width-500 %}`
* Moved the style guide menu item into the Settings sub-menu
* Search backends can now be specified by module (e.g. `wagtail.wagtailsearch.backends.elasticsearch`), rather than a specific class (`wagtail.wagtailsearch.backends.elasticsearch.ElasticSearch`)
* Added ``descendant_of`` filter to the API (Michael Fillier)
* Added optional directory argument to "wagtail start" command (Mitchel Cabuloy)
* Non-superusers can now view/edit/delete sites if they have the correct permissions
* Image file size is now stored in the database, to avoid unnecessary filesystem lookups
* Updated URLs within the admin backend to use namespaces
* The `update_index` task now indexes objects in batches of 1000, to indicate progress and avoid excessive memory use
* Fix: Text areas in the non-default tab of the page editor now resize to the correct height
* Fix: Tabs in "insert link" modal in the rich text editor no longer disappear (Tim Heap)
* Fix: H2 elements in rich text fields were accidentally given a click() binding when put insite a collapsible multi field panel
1.0 (16.07.2015)

View file

@ -56,6 +56,8 @@ Contributors
* Nar Chhantyal
* Michael Fillier
* Mitchel Cabuloy
* Piet Delport
* Tom Christie
Translators
===========

View file

@ -147,7 +147,7 @@ Create a template at ``blog/templates/blog/blog_page.html``:
{% load wagtailcore_tags %}
{% block body_class %}templage-blogpage{% endblock %}
{% block body_class %}template-blogpage{% endblock %}
{% block content %}
<h1>{{ self.title }}</h1>
@ -217,7 +217,7 @@ Adjust your blog page template to include the image:
{% load wagtailcore_tags wagtailimages_tags %}
{% block body_class %}templage-blogpage{% endblock %}
{% block body_class %}template-blogpage{% endblock %}
{% block content %}
<h1>{{ self.title }}</h1>

View file

@ -13,6 +13,7 @@ Wagtail ships with a variety of extra optional modules.
frontendcache
routablepage
api/index
searchpromotions
:doc:`forms`
@ -49,3 +50,9 @@ Provides a way of embedding Django URLconfs into pages.
----------------
A module for adding a read only, JSON based web API to your Wagtail site
:doc:`searchpromotions`
-----------------------
A module for managing "Promoted Search Results"

View file

@ -0,0 +1,63 @@
.. _editors-picks:
=======================
Promoted search results
=======================
.. module:: wagtail.contrib.wagtailsearchpromotions
.. versionchanged:: 1.1
Before Wagtail 1.1, promoted search results were implemented in the :mod:`wagtail.wagtailsearch` core module and called "editors picks".
The ``searchpromotions`` module provides the models and user interface for managing "Promoted search results" and displaying them in a search results page.
"Promoted search results" allow editors to explicitly link relevant content to search terms, so results pages can contain curated content in addition to results from the search engine.
Installation
============
The ``searchpromotions`` module is not enabled by default. To install it, add ``wagtail.contrib.wagtailsearchpromotions`` to ``INSTALLED_APPS`` in your project's Django settings file.
.. code-block:: python
INSTALLED_APPS = [
...
'wagtail.contrib.wagtailsearchpromotions',
]
This app contains migrations so make sure you run the ``migrate`` django-admin command after installing.
Usage
=====
Once installed, a new menu item called "Promoted search results" should appear in the "Settings" menu. This is where you can assign pages to popular search terms.
Displaying on a search results page
-----------------------------------
To retrieve a list of promoted search results for a particular search query, you can use the ``{% get_search_promotions %}`` template tag from the ``wagtailsearchpromotions_tags`` templatetag library:
.. code-block:: HTML+Django
{% load wagtailcore_tags wagtailsearchpromotions_tags %}
...
{% get_search_promotions search_query as search_promotions %}
<ul>
{% for search_promotion in search_promotions %}
<li>
<a href="{% pageurl search_promotion.page %}">
<h2>{{ search_promotion.page.title }}</h2>
<p>{{ search_promotion.description }}</p>
</a>
</li>
{% endfor %}
</ul>

View file

@ -15,17 +15,56 @@ What's new
Usually, an operation that retrieves a queryset of pages (such as ``homepage.get_children()``) will return them as basic Page instances, which only include the core page data such as title. The ``specific()`` method (e.g. ``homepage.get_children().specific()``) now allows them to be retrieved as their most specific type, using the minimum number of queries.
"Promoted search results" has moved into its own module
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Previously, this was implemented in :mod:`~wagtail.wagtailsearch` but now has
been moved into a separate module: :mod:`wagtail.contrib.wagtailsearchpromotions`
Atomic rebuilding of Elasticsearch indexes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The Elasticsearch search backend now accepts an experimental ``ATOMIC_REBUILD`` flag which ensures that the existing search index continues to be available while the ``update_index`` task is running. See :ref:`wagtailsearch_backends_atomic_rebuild`.
Minor features
~~~~~~~~~~~~~~
* The :mod:`~wagtail.contrib.wagtailapi` module has been refactored to use Django REST Framework
* Implemented pagination in the page chooser modal
* Changed INSTALLED_APPS in project template to list apps in precedence order
* The ``{% image %}`` tag now supports filters on the image variable, e.g. ``{% image primary_img|default:secondary_img width-500 %}``
* Moved the style guide menu item into the Settings sub-menu
* Search backends can now be specified by module (e.g. ``wagtail.wagtailsearch.backends.elasticsearch``), rather than a specific class (``wagtail.wagtailsearch.backends.elasticsearch.ElasticSearch``)
* Added ``descendant_of`` filter to the API
* Added optional directory argument to "wagtail start" command
* Non-superusers can now view/edit/delete sites if they have the correct permissions
* Image file size is now stored in the database, to avoid unnecessary filesystem lookups
* Updated URLs within the admin backend to use namespaces
* The ``update_index`` task now indexes objects in batches of 1000, to indicate progress and avoid excessive memory use
Bug fixes
~~~~~~~~~
* Text areas in the non-default tab of the page editor now resize to the correct height
* Tabs in "insert link" modal in the rich text editor no longer disappear (Tim Heap)
* H2 elements in rich text fields were accidentally given a click() binding when put insite a collapsible multi field panel
Upgrade considerations
======================
"Promoted search results" no longer in :mod:`~wagtail.wagtailsearch`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This feature has moved into a contrib module so is no longer enabled by default.
To re-enable it, add :mod:`wagtail.contrib.wagtailsearchpromotions` to your ``INSTALLED_APPS``:
.. code-block:: python
INSTALLED_APPS = [
...
'wagtail.contrib.wagtailsearchpromotions',
...

View file

@ -41,6 +41,22 @@ The ``AUTO_UPDATE`` setting allows you to disable this on a per-index basis:
If you have disabled auto update, you must run the :ref:`update_index` command on a regular basis to keep the index in sync with the database.
.. _wagtailsearch_backends_atomic_rebuild:
``ATOMIC_REBUILD``
==================
.. versionadded:: 1.1
By default (when using the Elasticsearch backend), when the ``update_index`` command is run, Wagtail deletes the index and rebuilds it from scratch. This causes the search engine to not return results until the rebuild is complete and is also risky as you can't rollback if an error occurs.
Setting the ``ATOMIC_REBUILD`` setting to ``True`` makes Wagtail rebuild into a separate index while keep the old index active until the new one is fully built. When the rebuild is finished, the indexes are swapped atomically and the old index is deleted.
.. warning:: Experimental feature
This feature is currently experimental. Please use it with caution.
``BACKEND``
===========

View file

@ -100,28 +100,12 @@ And here's a template to go with it:
{% endblock %}
.. _editors-picks:
Promoted search results
=======================
"Promoted search results" allow editors to explicitly link relevant content to search terms, so results pages can contain curated content in addition to results from the search engine.
Editor's picks
==============
Editor's picks are a way of explicitly linking relevant content to search terms, so results pages can contain curated content in addition to results from the search algorithm.
You can get a list of editors picks for a particular query using the ``Query`` class:
.. code-block:: python
editors_picks = Query.get(search_query).editors_picks.all()
Each editors pick contains the following fields:
``page``
The page object associated with the pick. Use ``{% pageurl editors_pick.page %}`` to generate a URL or provide other properties of the page object.
``description``
The description entered when choosing the pick, perhaps explaining why the page is relevant to the search terms.
This functionality is provided by the :mod:`~wagtail.contrib.wagtailsearchpromotions` contrib module.
Searching Images, Documents and custom models

View file

@ -28,6 +28,7 @@ install_requires = [
"django-modelcluster>=0.6",
"django-taggit>=0.13.0",
"django-treebeard==3.0",
"djangorestframework==3.1.3",
"Pillow>=2.6.1",
"beautifulsoup4>=4.3.2",
"html5lib==0.999",

View file

@ -22,6 +22,7 @@ deps =
django-taggit==0.13.0
django-treebeard==3.0
django-sendfile==0.3.6
djangorestframework==3.1.3
Pillow>=2.3.0
beautifulsoup4>=4.3.2
html5lib==0.999

View file

@ -1,95 +0,0 @@
import json
from functools import wraps
from django.conf.urls import url, include
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseNotFound, Http404
from django.core.serializers.json import DjangoJSONEncoder
from django.core.urlresolvers import reverse
from taggit.managers import _TaggableManager
from taggit.models import Tag
from wagtail.utils.urlpatterns import decorate_urlpatterns
from wagtail.wagtailcore.blocks import StreamValue
from .endpoints import URLPath, ObjectDetailURL, PagesAPIEndpoint, ImagesAPIEndpoint, DocumentsAPIEndpoint
from .utils import BadRequestError, get_base_url
def get_full_url(request, path):
base_url = get_base_url(request) or ''
return base_url + path
class API(object):
def __init__(self, endpoints):
self.endpoints = endpoints
def find_model_detail_view(self, model):
for endpoint_name, endpoint in self.endpoints.items():
if endpoint.has_model(model):
return 'wagtailapi_v1:%s:detail' % endpoint_name
def make_response(self, request, data, response_cls=HttpResponse):
api = self
class WagtailAPIJSONEncoder(DjangoJSONEncoder):
def default(self, o):
if isinstance(o, _TaggableManager):
return list(o.all())
elif isinstance(o, Tag):
return o.name
elif isinstance(o, URLPath):
return get_full_url(request, o.path)
elif isinstance(o, ObjectDetailURL):
view = api.find_model_detail_view(o.model)
if view:
return get_full_url(request, reverse(view, args=(o.pk, )))
else:
return None
elif isinstance(o, StreamValue):
return o.stream_block.get_prep_value(o)
else:
return super(WagtailAPIJSONEncoder, self).default(o)
return response_cls(
json.dumps(data, indent=4, cls=WagtailAPIJSONEncoder),
content_type='application/json'
)
def api_view(self, view):
"""
This is a decorator that is applied to all API views.
It is responsible for serialising the responses from the endpoints
and handling errors.
"""
@wraps(view)
def wrapper(request, *args, **kwargs):
# Catch exceptions and format them as JSON documents
try:
return self.make_response(request, view(request, *args, **kwargs))
except Http404 as e:
return self.make_response(request, {
'message': str(e)
}, response_cls=HttpResponseNotFound)
except BadRequestError as e:
return self.make_response(request, {
'message': str(e)
}, response_cls=HttpResponseBadRequest)
return wrapper
def get_urlpatterns(self):
return decorate_urlpatterns([
url(r'^%s/' % name, include(endpoint.get_urlpatterns(), namespace=name))
for name, endpoint in self.endpoints.items()
], self.api_view)
v1 = API({
'pages': PagesAPIEndpoint(),
'images': ImagesAPIEndpoint(),
'documents': DocumentsAPIEndpoint(),
})

View file

@ -1,100 +1,34 @@
from __future__ import absolute_import
from collections import OrderedDict
from modelcluster.models import get_all_child_relations
from taggit.managers import _TaggableManager
from django.db import models
from django.utils.encoding import force_text
from django.shortcuts import get_object_or_404
from django.conf.urls import url
from django.conf import settings
from django.http import Http404
from rest_framework import status
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet
from wagtail.wagtailcore.models import Page
from wagtail.wagtailimages.models import get_image_model
from wagtail.wagtaildocs.models import Document
from wagtail.wagtailcore.utils import resolve_model_string
from wagtail.wagtailsearch.backends import get_search_backend
from wagtail.utils.compat import get_related_model
from .filters import (
FieldsFilter, OrderingFilter, SearchFilter,
ChildOfFilter, DescendantOfFilter
)
from .renderers import WagtailJSONRenderer
from .pagination import WagtailPagination
from .serializers import WagtailSerializer, PageSerializer, DocumentSerializer
from .utils import BadRequestError
class URLPath(object):
"""
This class represents a URL path that should be converted to a full URL.
class BaseAPIEndpoint(GenericViewSet):
renderer_classes = [WagtailJSONRenderer]
pagination_class = WagtailPagination
serializer_class = WagtailSerializer
filter_classes = []
queryset = None # Set on subclasses or implement `get_queryset()`.
It is used when the domain that should be used is not known at the time
the URL was generated. It will get resolved to a full URL during
serialisation in api.py.
One example use case is the documents endpoint adding download URLs into
the JSON. The endpoint does not know the domain name to use at the time so
returns one of these instead.
"""
def __init__(self, path):
self.path = path
class ObjectDetailURL(object):
def __init__(self, model, pk):
self.model = model
self.pk = pk
def get_api_data(obj, fields):
# Find any child relations (pages only)
child_relations = {}
if isinstance(obj, Page):
child_relations = {
child_relation.field.rel.related_name: get_related_model(child_relation)
for child_relation in get_all_child_relations(type(obj))
}
# Loop through fields
for field_name in fields:
# Check child relations
if field_name in child_relations and hasattr(child_relations[field_name], 'api_fields'):
yield field_name, [
dict(get_api_data(child_object, child_relations[field_name].api_fields))
for child_object in getattr(obj, field_name).all()
]
continue
# Check django fields
try:
field = obj._meta.get_field(field_name)
if field.rel and isinstance(field.rel, models.ManyToOneRel):
# Foreign key
val = field._get_val_from_obj(obj)
if val:
yield field_name, OrderedDict([
('id', field._get_val_from_obj(obj)),
('meta', OrderedDict([
('type', field.rel.to._meta.app_label + '.' + field.rel.to.__name__),
('detail_url', ObjectDetailURL(field.rel.to, val)),
])),
])
else:
yield field_name, None
else:
yield field_name, field._get_val_from_obj(obj)
continue
except models.fields.FieldDoesNotExist:
pass
# Check attributes
if hasattr(obj, field_name):
value = getattr(obj, field_name)
yield field_name, force_text(value, strings_only=True)
continue
class BaseAPIEndpoint(object):
known_query_parameters = frozenset([
'limit',
'offset',
@ -102,76 +36,48 @@ class BaseAPIEndpoint(object):
'order',
'search',
])
extra_api_fields = []
name = None # Set on subclass.
def listing_view(self, request):
return NotImplemented
queryset = self.get_queryset()
self.check_query_parameters(queryset)
queryset = self.filter_queryset(queryset)
queryset = self.paginate_queryset(queryset)
serializer = self.get_serializer(queryset, many=True)
return self.get_paginated_response(serializer.data)
def detail_view(self, request, pk):
return NotImplemented
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
def handle_exception(self, exc):
if isinstance(exc, Http404):
data = {'message': str(exc)}
return Response(data, status=status.HTTP_404_NOT_FOUND)
elif isinstance(exc, BadRequestError):
data = {'message': str(exc)}
return Response(data, status=status.HTTP_400_BAD_REQUEST)
return super(BaseAPIEndpoint, self).handle_exception(exc)
def get_api_fields(self, model):
"""
This returns a list of field names that are allowed to
be used in the API (excluding the id field).
"""
api_fields = []
api_fields = self.extra_api_fields[:]
if hasattr(model, 'api_fields'):
api_fields.extend(model.api_fields)
return api_fields
def serialize_object_metadata(self, request, obj, show_details=False):
def check_query_parameters(self, queryset):
"""
This returns a JSON-serialisable dict to use for the "meta"
section of a particlular object.
Ensure that only valid query paramters are included in the URL.
"""
data = OrderedDict()
# Add type
data['type'] = type(obj)._meta.app_label + '.' + type(obj).__name__
data['detail_url'] = ObjectDetailURL(type(obj), obj.pk)
return data
def serialize_object(self, request, obj, fields=frozenset(), extra_data=(), all_fields=False, show_details=False):
"""
This converts an object into JSON-serialisable dict so it can
be used in the API.
"""
data = [
('id', obj.id),
]
# Add meta
metadata = self.serialize_object_metadata(request, obj, show_details=show_details)
if metadata:
data.append(('meta', metadata))
# Add extra data
data.extend(extra_data)
# Add other fields
api_fields = self.get_api_fields(type(obj))
api_fields = list(OrderedDict.fromkeys(api_fields)) # Removes any duplicates in case the user put "title" in api_fields
if all_fields:
fields = api_fields
else:
unknown_fields = fields - set(api_fields)
if unknown_fields:
raise BadRequestError("unknown fields: %s" % ', '.join(sorted(unknown_fields)))
# Reorder fields so it matches the order of api_fields
fields = [field for field in api_fields if field in fields]
data.extend(get_api_data(obj, fields))
return OrderedDict(data)
def check_query_parameters(self, request, queryset):
query_parameters = set(request.GET.keys())
query_parameters = set(self.request.GET.keys())
# All query paramters must be either a field or an operation
allowed_query_parameters = set(self.get_api_fields(queryset.model)).union(self.known_query_parameters).union({'id'})
@ -179,147 +85,87 @@ class BaseAPIEndpoint(object):
if unknown_parameters:
raise BadRequestError("query parameter is not an operation or a recognised field: %s" % ', '.join(sorted(unknown_parameters)))
def do_field_filtering(self, request, queryset):
def get_serializer_context(self):
"""
This performs field level filtering on the result set
Eg: ?title=James Joyce
The serialization context differs between listing and detail views.
"""
fields = set(self.get_api_fields(queryset.model)).union({'id'})
request = self.request
if self.action == 'listing_view':
for field_name, value in request.GET.items():
if field_name in fields:
field = getattr(queryset.model, field_name, None)
if isinstance(field, _TaggableManager):
for tag in value.split(','):
queryset = queryset.filter(**{field_name + '__name': tag})
# Stick a message on the queryset to indicate that tag filtering has been performed
# This will let the do_search method know that it must raise an error as searching
# and tag filtering at the same time is not supported
queryset._filtered_by_tag = True
else:
queryset = queryset.filter(**{field_name: value})
return queryset
def do_ordering(self, request, queryset):
"""
This applies ordering to the result set
Eg: ?order=title
It also supports reverse ordering
Eg: ?order=-title
And random ordering
Eg: ?order=random
"""
if 'order' in request.GET:
# Prevent ordering while searching
if 'search' in request.GET:
raise BadRequestError("ordering with a search query is not supported")
order_by = request.GET['order']
# Random ordering
if order_by == 'random':
# Prevent ordering by random with offset
if 'offset' in request.GET:
raise BadRequestError("random ordering with offset is not supported")
return queryset.order_by('?')
# Check if reverse ordering is set
if order_by.startswith('-'):
reverse_order = True
order_by = order_by[1:]
if 'fields' in request.GET:
fields = set(request.GET['fields'].split(','))
else:
reverse_order = False
fields = {'title'}
# Add ordering
if order_by == 'id' or order_by in self.get_api_fields(queryset.model):
queryset = queryset.order_by(order_by)
else:
# Unknown field
raise BadRequestError("cannot order by '%s' (unknown field)" % order_by)
return {
'request': request,
'view': self,
'fields': fields
}
# Reverse order
if reverse_order:
queryset = queryset.reverse()
return {
'request': request,
'view': self,
'all_fields': True,
'show_details': True
}
return queryset
def get_renderer_context(self):
context = super(BaseAPIEndpoint, self).get_renderer_context()
context['endpoints'] = [
PagesAPIEndpoint,
ImagesAPIEndpoint,
DocumentsAPIEndpoint
]
return context
def do_search(self, request, queryset):
"""
This performs a full-text search on the result set
Eg: ?search=James Joyce
"""
search_enabled = getattr(settings, 'WAGTAILAPI_SEARCH_ENABLED', True)
if 'search' in request.GET:
if not search_enabled:
raise BadRequestError("search is disabled")
# Searching and filtering by tag at the same time is not supported
if getattr(queryset, '_filtered_by_tag', False):
raise BadRequestError("filtering by tag with a search query is not supported")
search_query = request.GET['search']
sb = get_search_backend()
queryset = sb.search(search_query, queryset)
return queryset
def do_pagination(self, request, queryset):
"""
This performs limit/offset based pagination on the result set
Eg: ?limit=10&offset=20 -- Returns 10 items starting at item 20
"""
limit_max = getattr(settings, 'WAGTAILAPI_LIMIT_MAX', 20)
try:
offset = int(request.GET.get('offset', 0))
assert offset >= 0
except (ValueError, AssertionError):
raise BadRequestError("offset must be a positive integer")
try:
limit = int(request.GET.get('limit', min(20, limit_max)))
if limit > limit_max:
raise BadRequestError("limit cannot be higher than %d" % limit_max)
assert limit >= 0
except (ValueError, AssertionError):
raise BadRequestError("limit must be a positive integer")
start = offset
stop = offset + limit
return queryset[start:stop]
def get_urlpatterns(self):
@classmethod
def get_urlpatterns(cls):
"""
This returns a list of URL patterns for the endpoint
"""
return [
url(r'^$', self.listing_view, name='listing'),
url(r'^(\d+)/$', self.detail_view, name='detail'),
url(r'^$', cls.as_view({'get': 'listing_view'}), name='listing'),
url(r'^(?P<pk>\d+)/$', cls.as_view({'get': 'detail_view'}), name='detail'),
]
def has_model(self, model):
return False
@classmethod
def has_model(cls, model):
return NotImplemented
class PagesAPIEndpoint(BaseAPIEndpoint):
serializer_class = PageSerializer
filter_backends = [
FieldsFilter,
ChildOfFilter,
DescendantOfFilter,
OrderingFilter,
SearchFilter
]
known_query_parameters = BaseAPIEndpoint.known_query_parameters.union([
'type',
'child_of',
'descendant_of',
])
extra_api_fields = ['title']
name = 'pages'
def get_queryset(self):
request = self.request
# Allow pages to be filtered to a specific type
if 'type' not in request.GET:
model = Page
else:
model_name = request.GET['type']
try:
model = resolve_model_string(model_name)
except LookupError:
raise BadRequestError("type doesn't exist")
if not issubclass(model, Page):
raise BadRequestError("type doesn't exist")
def get_queryset(self, request, model=Page):
# Get live pages that are not in a private section
queryset = model.objects.public().live()
@ -328,245 +174,33 @@ class PagesAPIEndpoint(BaseAPIEndpoint):
return queryset
def get_api_fields(self, model):
api_fields = ['title']
api_fields.extend(super(PagesAPIEndpoint, self).get_api_fields(model))
return api_fields
def get_object(self):
base = super(PagesAPIEndpoint, self).get_object()
return base.specific
def serialize_object_metadata(self, request, page, show_details=False):
data = super(PagesAPIEndpoint, self).serialize_object_metadata(request, page, show_details=show_details)
# Add type
data['type'] = page.specific_class._meta.app_label + '.' + page.specific_class.__name__
return data
def serialize_object(self, request, page, fields=frozenset(), extra_data=(), all_fields=False, show_details=False):
# Add parent
if show_details:
parent = page.get_parent()
# Make sure the parent is visible in the API
if self.get_queryset(request).filter(id=parent.id).exists():
parent_class = parent.specific_class
extra_data += (
('parent', OrderedDict([
('id', parent.id),
('meta', OrderedDict([
('type', parent_class._meta.app_label + '.' + parent_class.__name__),
('detail_url', ObjectDetailURL(parent_class, parent.id)),
])),
])),
)
return super(PagesAPIEndpoint, self).serialize_object(request, page, fields=fields, extra_data=extra_data, all_fields=all_fields, show_details=show_details)
def get_model(self, request):
if 'type' not in request.GET:
return Page
model_name = request.GET['type']
try:
model = resolve_model_string(model_name)
if not issubclass(model, Page):
raise BadRequestError("type doesn't exist")
return model
except LookupError:
raise BadRequestError("type doesn't exist")
def do_child_of_filter(self, request, queryset):
if 'child_of' in request.GET:
try:
parent_page_id = int(request.GET['child_of'])
assert parent_page_id >= 0
except (ValueError, AssertionError):
raise BadRequestError("child_of must be a positive integer")
try:
parent_page = self.get_queryset(request).get(id=parent_page_id)
queryset = queryset.child_of(parent_page)
queryset._filtered_by_child_of = True
return queryset
except Page.DoesNotExist:
raise BadRequestError("parent page doesn't exist")
return queryset
def do_descendant_of_filter(self, request, queryset):
if 'descendant_of' in request.GET:
if getattr(queryset, '_filtered_by_child_of', False):
raise BadRequestError("filtering by descendant_of with child_of is not supported")
try:
ancestor_page_id = int(request.GET['descendant_of'])
assert ancestor_page_id >= 0
except (ValueError, AssertionError):
raise BadRequestError("descendant_of must be a positive integer")
try:
ancestor_page = self.get_queryset(request).get(id=ancestor_page_id)
return queryset.descendant_of(ancestor_page)
except Page.DoesNotExist:
raise BadRequestError("ancestor page doesn't exist")
return queryset
def listing_view(self, request):
# Get model and queryset
model = self.get_model(request)
queryset = self.get_queryset(request, model=model)
# Check query paramters
self.check_query_parameters(request, queryset)
# Filtering
queryset = self.do_field_filtering(request, queryset)
queryset = self.do_child_of_filter(request, queryset)
queryset = self.do_descendant_of_filter(request, queryset)
# Ordering
queryset = self.do_ordering(request, queryset)
# Search
queryset = self.do_search(request, queryset)
# Pagination
total_count = queryset.count()
queryset = self.do_pagination(request, queryset)
# Get list of fields to show in results
if 'fields' in request.GET:
fields = set(request.GET['fields'].split(','))
else:
fields = {'title'}
return OrderedDict([
('meta', OrderedDict([
('total_count', total_count),
])),
('pages', [
self.serialize_object(request, page, fields=fields)
for page in queryset
]),
])
def detail_view(self, request, pk):
page = get_object_or_404(self.get_queryset(request), pk=pk).specific
return self.serialize_object(request, page, all_fields=True, show_details=True)
def has_model(self, model):
@classmethod
def has_model(cls, model):
return issubclass(model, Page)
class ImagesAPIEndpoint(BaseAPIEndpoint):
model = get_image_model()
queryset = get_image_model().objects.all().order_by('id')
filter_backends = [FieldsFilter, OrderingFilter, SearchFilter]
extra_api_fields = ['title', 'tags', 'width', 'height']
name = 'images'
def get_queryset(self, request):
return self.model.objects.all().order_by('id')
def get_api_fields(self, model):
api_fields = ['title', 'tags', 'width', 'height']
api_fields.extend(super(ImagesAPIEndpoint, self).get_api_fields(model))
return api_fields
def listing_view(self, request):
queryset = self.get_queryset(request)
# Check query paramters
self.check_query_parameters(request, queryset)
# Filtering
queryset = self.do_field_filtering(request, queryset)
# Ordering
queryset = self.do_ordering(request, queryset)
# Search
queryset = self.do_search(request, queryset)
# Pagination
total_count = queryset.count()
queryset = self.do_pagination(request, queryset)
# Get list of fields to show in results
if 'fields' in request.GET:
fields = set(request.GET['fields'].split(','))
else:
fields = {'title'}
return OrderedDict([
('meta', OrderedDict([
('total_count', total_count),
])),
('images', [
self.serialize_object(request, image, fields=fields)
for image in queryset
]),
])
def detail_view(self, request, pk):
image = get_object_or_404(self.get_queryset(request), pk=pk)
return self.serialize_object(request, image, all_fields=True)
def has_model(self, model):
return model == self.model
@classmethod
def has_model(cls, model):
return model == get_image_model()
class DocumentsAPIEndpoint(BaseAPIEndpoint):
def get_api_fields(self, model):
api_fields = ['title', 'tags']
api_fields.extend(super(DocumentsAPIEndpoint, self).get_api_fields(model))
return api_fields
queryset = Document.objects.all().order_by('id')
serializer_class = DocumentSerializer
filter_backends = [FieldsFilter, OrderingFilter, SearchFilter]
extra_api_fields = ['title', 'tags']
name = 'documents'
def serialize_object_metadata(self, request, document, show_details=False):
data = super(DocumentsAPIEndpoint, self).serialize_object_metadata(request, document, show_details=show_details)
# Download URL
if show_details:
data['download_url'] = URLPath(document.url)
return data
def listing_view(self, request):
queryset = Document.objects.all().order_by('id')
# Check query paramters
self.check_query_parameters(request, queryset)
# Filtering
queryset = self.do_field_filtering(request, queryset)
# Ordering
queryset = self.do_ordering(request, queryset)
# Search
queryset = self.do_search(request, queryset)
# Pagination
total_count = queryset.count()
queryset = self.do_pagination(request, queryset)
# Get list of fields to show in results
if 'fields' in request.GET:
fields = set(request.GET['fields'].split(','))
else:
fields = {'title'}
return OrderedDict([
('meta', OrderedDict([
('total_count', total_count),
])),
('documents', [
self.serialize_object(request, document, fields=fields)
for document in queryset
]),
])
def detail_view(self, request, pk):
document = get_object_or_404(Document, pk=pk)
return self.serialize_object(request, document, all_fields=True, show_details=True)
def has_model(self, model):
@classmethod
def has_model(cls, model):
return model == Document

View file

@ -0,0 +1,150 @@
from django.conf import settings
from rest_framework.filters import BaseFilterBackend
from taggit.managers import _TaggableManager
from wagtail.wagtailcore.models import Page
from wagtail.wagtailsearch.backends import get_search_backend
from .utils import BadRequestError, pages_for_site
class FieldsFilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
"""
This performs field level filtering on the result set
Eg: ?title=James Joyce
"""
fields = set(view.get_api_fields(queryset.model)).union({'id'})
for field_name, value in request.GET.items():
if field_name in fields:
field = getattr(queryset.model, field_name, None)
if isinstance(field, _TaggableManager):
for tag in value.split(','):
queryset = queryset.filter(**{field_name + '__name': tag})
# Stick a message on the queryset to indicate that tag filtering has been performed
# This will let the do_search method know that it must raise an error as searching
# and tag filtering at the same time is not supported
queryset._filtered_by_tag = True
else:
queryset = queryset.filter(**{field_name: value})
return queryset
class OrderingFilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
"""
This applies ordering to the result set
Eg: ?order=title
It also supports reverse ordering
Eg: ?order=-title
And random ordering
Eg: ?order=random
"""
if 'order' in request.GET:
# Prevent ordering while searching
if 'search' in request.GET:
raise BadRequestError("ordering with a search query is not supported")
order_by = request.GET['order']
# Random ordering
if order_by == 'random':
# Prevent ordering by random with offset
if 'offset' in request.GET:
raise BadRequestError("random ordering with offset is not supported")
return queryset.order_by('?')
# Check if reverse ordering is set
if order_by.startswith('-'):
reverse_order = True
order_by = order_by[1:]
else:
reverse_order = False
# Add ordering
if order_by == 'id' or order_by in view.get_api_fields(queryset.model):
queryset = queryset.order_by(order_by)
else:
# Unknown field
raise BadRequestError("cannot order by '%s' (unknown field)" % order_by)
# Reverse order
if reverse_order:
queryset = queryset.reverse()
return queryset
class SearchFilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
"""
This performs a full-text search on the result set
Eg: ?search=James Joyce
"""
search_enabled = getattr(settings, 'WAGTAILAPI_SEARCH_ENABLED', True)
if 'search' in request.GET:
if not search_enabled:
raise BadRequestError("search is disabled")
# Searching and filtering by tag at the same time is not supported
if getattr(queryset, '_filtered_by_tag', False):
raise BadRequestError("filtering by tag with a search query is not supported")
search_query = request.GET['search']
sb = get_search_backend()
queryset = sb.search(search_query, queryset)
return queryset
class ChildOfFilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
if 'child_of' in request.GET:
try:
parent_page_id = int(request.GET['child_of'])
assert parent_page_id >= 0
except (ValueError, AssertionError):
raise BadRequestError("child_of must be a positive integer")
site_pages = pages_for_site(request.site)
try:
parent_page = site_pages.get(id=parent_page_id)
queryset = queryset.child_of(parent_page)
queryset._filtered_by_child_of = True
return queryset
except Page.DoesNotExist:
raise BadRequestError("parent page doesn't exist")
return queryset
class DescendantOfFilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
if 'descendant_of' in request.GET:
if getattr(queryset, '_filtered_by_child_of', False):
raise BadRequestError("filtering by descendant_of with child_of is not supported")
try:
ancestor_page_id = int(request.GET['descendant_of'])
assert ancestor_page_id >= 0
except (ValueError, AssertionError):
raise BadRequestError("descendant_of must be a positive integer")
site_pages = pages_for_site(request.site)
try:
ancestor_page = site_pages.get(id=ancestor_page_id)
return queryset.descendant_of(ancestor_page)
except Page.DoesNotExist:
raise BadRequestError("ancestor page doesn't exist")
return queryset

View file

@ -0,0 +1,45 @@
from collections import OrderedDict
from django.conf import settings
from rest_framework.pagination import BasePagination
from rest_framework.response import Response
from .utils import BadRequestError
class WagtailPagination(BasePagination):
def paginate_queryset(self, queryset, request, view=None):
limit_max = getattr(settings, 'WAGTAILAPI_LIMIT_MAX', 20)
try:
offset = int(request.GET.get('offset', 0))
assert offset >= 0
except (ValueError, AssertionError):
raise BadRequestError("offset must be a positive integer")
try:
limit = int(request.GET.get('limit', min(20, limit_max)))
if limit > limit_max:
raise BadRequestError("limit cannot be higher than %d" % limit_max)
assert limit >= 0
except (ValueError, AssertionError):
raise BadRequestError("limit must be a positive integer")
start = offset
stop = offset + limit
self.view = view
self.total_count = queryset.count()
return queryset[start:stop]
def get_paginated_response(self, data):
data = OrderedDict([
('meta', OrderedDict([
('total_count', self.total_count),
])),
(self.view.name, data),
])
return Response(data)

View file

@ -0,0 +1,61 @@
import json
from django.core.serializers.json import DjangoJSONEncoder
from django.core.urlresolvers import reverse
from django.utils.six import text_type
from rest_framework import renderers
from taggit.managers import _TaggableManager
from taggit.models import Tag
from wagtail.wagtailcore.blocks import StreamValue
from .utils import URLPath, ObjectDetailURL, get_base_url
def get_full_url(request, path):
base_url = get_base_url(request) or ''
return base_url + path
def find_model_detail_view(model, endpoints):
for endpoint in endpoints:
if endpoint.has_model(model):
return 'wagtailapi_v1:%s:detail' % endpoint.name
class WagtailJSONRenderer(renderers.BaseRenderer):
media_type = 'application/json'
charset = None
def render(self, data, media_type=None, renderer_context=None):
request = renderer_context['request']
endpoints = renderer_context['endpoints']
class WagtailAPIJSONEncoder(DjangoJSONEncoder):
def default(self, o):
if isinstance(o, _TaggableManager):
return list(o.all())
elif isinstance(o, Tag):
return o.name
elif isinstance(o, URLPath):
return get_full_url(request, o.path)
elif isinstance(o, ObjectDetailURL):
detail_view = find_model_detail_view(o.model, endpoints)
if detail_view:
return get_full_url(request, reverse(detail_view, args=(o.pk, )))
else:
return None
elif isinstance(o, StreamValue):
return o.stream_block.get_prep_value(o)
else:
return super(WagtailAPIJSONEncoder, self).default(o)
ret = json.dumps(data, indent=4, cls=WagtailAPIJSONEncoder)
# Deal with inconsistent py2/py3 behavior, and always return bytes.
if isinstance(ret, text_type):
return bytes(ret.encode('utf-8'))
return ret

View file

@ -0,0 +1,172 @@
from __future__ import absolute_import
from collections import OrderedDict
from django.db import models
from django.utils.encoding import force_text
from modelcluster.models import get_all_child_relations
from rest_framework.serializers import BaseSerializer
from wagtail.utils.compat import get_related_model
from wagtail.wagtailcore.models import Page
from .utils import ObjectDetailURL, URLPath, BadRequestError, pages_for_site
def get_api_data(obj, fields):
# Find any child relations (pages only)
child_relations = {}
if isinstance(obj, Page):
child_relations = {
child_relation.field.rel.related_name: get_related_model(child_relation)
for child_relation in get_all_child_relations(type(obj))
}
# Loop through fields
for field_name in fields:
# Check child relations
if field_name in child_relations and hasattr(child_relations[field_name], 'api_fields'):
yield field_name, [
dict(get_api_data(child_object, child_relations[field_name].api_fields))
for child_object in getattr(obj, field_name).all()
]
continue
# Check django fields
try:
field = obj._meta.get_field(field_name)
if field.rel and isinstance(field.rel, models.ManyToOneRel):
# Foreign key
val = field._get_val_from_obj(obj)
if val:
yield field_name, OrderedDict([
('id', field._get_val_from_obj(obj)),
('meta', OrderedDict([
('type', field.rel.to._meta.app_label + '.' + field.rel.to.__name__),
('detail_url', ObjectDetailURL(field.rel.to, val)),
])),
])
else:
yield field_name, None
else:
yield field_name, field._get_val_from_obj(obj)
continue
except models.fields.FieldDoesNotExist:
pass
# Check attributes
if hasattr(obj, field_name):
value = getattr(obj, field_name)
yield field_name, force_text(value, strings_only=True)
continue
class WagtailSerializer(BaseSerializer):
def to_representation(self, instance):
request = self.context['request']
fields = self.context.get('fields', frozenset())
all_fields = self.context.get('all_fields', False)
show_details = self.context.get('show_details', False)
return self.serialize_object(
request,
instance,
fields=fields,
all_fields=all_fields,
show_details=show_details
)
def serialize_object_metadata(self, request, obj, show_details=False):
"""
This returns a JSON-serialisable dict to use for the "meta"
section of a particlular object.
"""
data = OrderedDict()
# Add type
data['type'] = type(obj)._meta.app_label + '.' + type(obj).__name__
data['detail_url'] = ObjectDetailURL(type(obj), obj.pk)
return data
def serialize_object(self, request, obj, fields=frozenset(), extra_data=(), all_fields=False, show_details=False):
"""
This converts an object into JSON-serialisable dict so it can
be used in the API.
"""
data = [
('id', obj.id),
]
# Add meta
metadata = self.serialize_object_metadata(request, obj, show_details=show_details)
if metadata:
data.append(('meta', metadata))
# Add extra data
data.extend(extra_data)
# Add other fields
api_fields = self.context['view'].get_api_fields(type(obj))
api_fields = list(OrderedDict.fromkeys(api_fields)) # Removes any duplicates in case the user put "title" in api_fields
if all_fields:
fields = api_fields
else:
unknown_fields = fields - set(api_fields)
if unknown_fields:
raise BadRequestError("unknown fields: %s" % ', '.join(sorted(unknown_fields)))
# Reorder fields so it matches the order of api_fields
fields = [field for field in api_fields if field in fields]
data.extend(get_api_data(obj, fields))
return OrderedDict(data)
class PageSerializer(WagtailSerializer):
def serialize_object_metadata(self, request, page, show_details=False):
data = super(PageSerializer, self).serialize_object_metadata(request, page, show_details=show_details)
# Add type
data['type'] = page.specific_class._meta.app_label + '.' + page.specific_class.__name__
return data
def serialize_object(self, request, page, fields=frozenset(), extra_data=(), all_fields=False, show_details=False):
# Add parent
if show_details:
parent = page.get_parent()
site_pages = pages_for_site(request.site)
if site_pages.filter(id=parent.id).exists():
parent_class = parent.specific_class
extra_data += (
('parent', OrderedDict([
('id', parent.id),
('meta', OrderedDict([
('type', parent_class._meta.app_label + '.' + parent_class.__name__),
('detail_url', ObjectDetailURL(parent_class, parent.id)),
])),
])),
)
return super(PageSerializer, self).serialize_object(request, page, fields=fields, extra_data=extra_data, all_fields=all_fields, show_details=show_details)
class DocumentSerializer(WagtailSerializer):
def serialize_object_metadata(self, request, document, show_details=False):
data = super(DocumentSerializer, self).serialize_object_metadata(request, document, show_details=show_details)
# Download URL
if show_details:
data['download_url'] = URLPath(document.url)
return data

View file

@ -2,9 +2,16 @@ from __future__ import absolute_import
from django.conf.urls import url, include
from . import api
from .endpoints import PagesAPIEndpoint, ImagesAPIEndpoint, DocumentsAPIEndpoint
v1 = [
url(r'^pages/', include(PagesAPIEndpoint.get_urlpatterns(), namespace='pages')),
url(r'^images/', include(ImagesAPIEndpoint.get_urlpatterns(), namespace='images')),
url(r'^documents/', include(DocumentsAPIEndpoint.get_urlpatterns(), namespace='documents'))
]
urlpatterns = [
url(r'^v1/', include(api.v1.get_urlpatterns(), namespace='wagtailapi_v1')),
url(r'^v1/', include(v1, namespace='wagtailapi_v1')),
]

View file

@ -1,11 +1,35 @@
from django.conf import settings
from django.utils.six.moves.urllib.parse import urlparse
from wagtail.wagtailcore.models import Page
class BadRequestError(Exception):
pass
class URLPath(object):
"""
This class represents a URL path that should be converted to a full URL.
It is used when the domain that should be used is not known at the time
the URL was generated. It will get resolved to a full URL during
serialisation in api.py.
One example use case is the documents endpoint adding download URLs into
the JSON. The endpoint does not know the domain name to use at the time so
returns one of these instead.
"""
def __init__(self, path):
self.path = path
class ObjectDetailURL(object):
def __init__(self, model, pk):
self.model = model
self.pk = pk
def get_base_url(request=None):
base_url = getattr(settings, 'WAGTAILAPI_BASE_URL', request.site.root_url if request else None)
@ -14,3 +38,9 @@ def get_base_url(request=None):
base_url_parsed = urlparse(base_url)
return base_url_parsed.scheme + '://' + base_url_parsed.netloc
def pages_for_site(site):
pages = Page.objects.public().live()
pages = pages.descendant_of(site.root_page, inclusive=True)
return pages

View file

@ -0,0 +1 @@
default_app_config = 'wagtail.contrib.wagtailsearchpromotions.apps.WagtailSearchPromotionsAppConfig'

View file

@ -0,0 +1,10 @@
from django.conf.urls import url
from wagtail.contrib.wagtailsearchpromotions import views
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'^add/$', views.add, name='add'),
url(r'^(\d+)/$', views.edit, name='edit'),
url(r'^(\d+)/delete/$', views.delete, name='delete'),
]

View file

@ -0,0 +1,7 @@
from django.apps import AppConfig
class WagtailSearchPromotionsAppConfig(AppConfig):
name = 'wagtail.contrib.wagtailsearchpromotions'
label = 'wagtailsearchpromotions'
verbose_name = "Wagtail search promotions"

View file

@ -0,0 +1,58 @@
from django import forms
from django.forms.models import inlineformset_factory
from django.utils.translation import ugettext_lazy as _
from wagtail.wagtailadmin.widgets import AdminPageChooser
from wagtail.wagtailsearch.models import Query
from wagtail.contrib.wagtailsearchpromotions.models import SearchPromotion
class SearchPromotionForm(forms.ModelForm):
sort_order = forms.IntegerField(required=False)
def __init__(self, *args, **kwargs):
super(SearchPromotionForm, self).__init__(*args, **kwargs)
self.fields['page'].widget = AdminPageChooser()
class Meta:
model = SearchPromotion
fields = ('query', 'page', 'description')
widgets = {
'description': forms.Textarea(attrs=dict(rows=3)),
}
SearchPromotionsFormSetBase = inlineformset_factory(Query, SearchPromotion, form=SearchPromotionForm, can_order=True, can_delete=True, extra=0)
class SearchPromotionsFormSet(SearchPromotionsFormSetBase):
minimum_forms = 1
minimum_forms_message = _("Please specify at least one recommendation for this search term.")
def add_fields(self, form, *args, **kwargs):
super(SearchPromotionsFormSet, self).add_fields(form, *args, **kwargs)
# Hide delete and order fields
form.fields['DELETE'].widget = forms.HiddenInput()
form.fields['ORDER'].widget = forms.HiddenInput()
# Remove query field
del form.fields['query']
def clean(self):
# Search pick must have at least one recommended page to be valid
# Check there is at least one non-deleted form.
non_deleted_forms = self.total_form_count()
non_empty_forms = 0
for i in range(0, self.total_form_count()):
form = self.forms[i]
if self.can_delete and self._should_delete_form(form):
non_deleted_forms -= 1
if not (form.instance.id is None and not form.has_changed()):
non_empty_forms += 1
if (
non_deleted_forms < self.minimum_forms
or non_empty_forms < self.minimum_forms
):
raise forms.ValidationError(self.minimum_forms_message)

View file

@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('wagtailcore', '0015_add_more_verbose_names'),
('wagtailsearch', '0003_remove_editors_pick'),
]
operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.CreateModel(
name='EditorsPick',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
('sort_order', models.IntegerField(editable=False, null=True, blank=True)),
('description', models.TextField(verbose_name='Description', blank=True)),
('page', models.ForeignKey(verbose_name='Page', to='wagtailcore.Page')),
('query', models.ForeignKey(to='wagtailsearch.Query', related_name='editors_picks')),
],
options={
'db_table': 'wagtailsearch_editorspick',
'verbose_name': "Editor's Pick",
'ordering': ('sort_order',),
},
),
],
database_operations=[]
),
migrations.AlterModelTable(
name='editorspick',
table=None,
),
migrations.RenameModel(
old_name='EditorsPick',
new_name='SearchPromotion'
),
migrations.AlterModelOptions(
name='searchpromotion',
options={'ordering': ('sort_order',), 'verbose_name': 'Search promotion'},
),
]

View file

@ -0,0 +1,18 @@
from django.db import models
from django.utils.translation import ugettext_lazy as _
from wagtail.wagtailsearch.models import Query
class SearchPromotion(models.Model):
query = models.ForeignKey(Query, db_index=True, related_name='editors_picks')
page = models.ForeignKey('wagtailcore.Page', verbose_name=_('Page'))
sort_order = models.IntegerField(null=True, blank=True, editable=False)
description = models.TextField(verbose_name=_('Description'), blank=True)
def __repr__(self):
return 'SearchPromotion(query="' + self.query.query_string + '", page="' + self.page.title + '")'
class Meta:
ordering = ('sort_order', )
verbose_name = _("Search promotion")

View file

@ -1,8 +1,8 @@
{% extends "wagtailadmin/base.html" %}
{% load i18n %}
{% block titletag %}{% trans "Add editor's pick" %}{% endblock %}
{% block titletag %}{% trans "Add search pick" %}{% endblock %}
{% block content %}
{% trans "Add editor's pick" as add_str %}
{% trans "Add search pick" as add_str %}
{% include "wagtailadmin/shared/header.html" with title=add_str icon="pick" %}
<div class="nice-padding">
@ -15,7 +15,7 @@
<p>The "Search term(s)/phrase" field below must contain the full and exact search for which you wish to provide recommended results, <em>including</em> any misspellings/user error. To help, you can choose from search terms that have been popular with users of your site.</p>
{% endblocktrans %}
</div>
<form action="{% url 'wagtailsearch_editorspicks_add' %}" method="POST">
<form action="{% url 'wagtailsearchpromotions:add' %}" method="POST">
{% csrf_token %}
<ul class="fields">
@ -23,7 +23,7 @@
{% include "wagtailsearch/queries/chooser_field.html" with field=query_form.query_string only %}
</li>
<li>
{% include "wagtailsearch/editorspicks/includes/editorspicks_formset.html" with formset=editors_pick_formset only %}
{% include "wagtailsearchpromotions/includes/searchpromotions_formset.html" with formset=searchpicks_formset only %}
</li>
<li><input type="submit" value="{% trans 'Save' %}" /></li>
</ul>
@ -38,7 +38,7 @@
{% include "wagtailadmin/pages/_editor_js.html" %}
<script type="text/javascript">
{% include "wagtailsearch/editorspicks/includes/editorspicks_formset.js" with formset=editors_pick_formset only %}
{% include "wagtailsearchpromotions/includes/searchpromotions_formset.js" with formset=searchpicks_formset only %}
{% include "wagtailsearch/queries/chooser_field.js" only %}
$(function() {

View file

@ -7,7 +7,7 @@
<div class="nice-padding">
<p>{% trans "Are you sure you want to delete all promoted results for this search term?" %}</p>
<form action="{% url 'wagtailsearch_editorspicks_delete' query.id %}" method="POST">
<form action="{% url 'wagtailsearchpromotions:delete' query.id %}" method="POST">
{% csrf_token %}
<input type="submit" value="{% trans 'Yes, delete' %}" class="serious" />
</form>

View file

@ -5,7 +5,7 @@
{% trans "Editing" as editing_str %}
{% include "wagtailadmin/shared/header.html" with title=editing_str subtitle=query.query_string icon="pick" %}
<form action="{% url 'wagtailsearch_editorspicks_edit' query.id %}" method="POST" class="nice-padding">
<form action="{% url 'wagtailsearchpromotions:edit' query.id %}" method="POST" class="nice-padding">
{% csrf_token %}
<ul class="fields">
@ -13,11 +13,11 @@
{% include "wagtailsearch/queries/chooser_field.html" with field=query_form.query_string only %}
</li>
<li>
{% include "wagtailsearch/editorspicks/includes/editorspicks_formset.html" with formset=editors_pick_formset only %}
{% include "wagtailsearchpromotions/includes/searchpromotions_formset.html" with formset=searchpicks_formset only %}
</li>
<li>
<input type="submit" value="{% trans 'Save' %}" />
<a href="{% url 'wagtailsearch_editorspicks_delete' query.id %}" class="button button-secondary no">{% trans "Delete" %}</a>
<a href="{% url 'wagtailsearchpromotions:delete' query.id %}" class="button button-secondary no">{% trans "Delete" %}</a>
</li>
</ul>
</form>
@ -30,11 +30,11 @@
{% include "wagtailadmin/pages/_editor_js.html" %}
<script type="text/javascript">
{% include "wagtailsearch/editorspicks/includes/editorspicks_formset.js" with formset=editors_pick_formset only %}
{% include "wagtailsearchpromotions/includes/searchpromotions_formset.js" with formset=searchpicks_formset only %}
{% include "wagtailsearch/queries/chooser_field.js" only %}
$(function() {
createQueryChooser('{{ query_form.query_string.auto_id }}');
});
</script>
{% endblock %}
{% endblock %}

View file

@ -2,13 +2,13 @@
{{ formset.management_form }}
<ul class="multiple" id="id_{{ formset.prefix }}-FORMS">
{% for form in formset.forms %}
{% include "wagtailsearch/editorspicks/includes/editorspicks_form.html" with form=form only %}
{% include "wagtailsearchpromotions/includes/searchpromotion_form.html" with form=form only %}
{% endfor %}
</ul>
<script type="text/django-form-template" id="id_{{ formset.prefix }}-EMPTY_FORM_TEMPLATE">
{% escapescript %}
{% include "wagtailsearch/editorspicks/includes/editorspicks_form.html" with form=formset.empty_form only %}
{% include "wagtailsearchpromotions/includes/searchpromotion_form.html" with form=formset.empty_form only %}
{% endescapescript %}
</script>

View file

@ -6,7 +6,7 @@
{% block extra_js %}
<script>
window.headerSearch = {
url: "{% url 'wagtailsearch_editorspicks_index' %}",
url: "{% url 'wagtailsearchpromotions:index' %}",
termInput: "#id_q",
targetOutput: "#editorspicks-results"
}
@ -16,11 +16,11 @@
{% block content %}
{% trans "Promoted search results" as sp_title_str %}
{% trans "Add new promoted result" as sp_text_str %}
{% include "wagtailadmin/shared/header.html" with title=sp_title_str add_link="wagtailsearch_editorspicks_add" icon="pick" add_text=sp_text_str search_url="wagtailsearch_editorspicks_index" %}
{% include "wagtailadmin/shared/header.html" with title=sp_title_str add_link="wagtailsearchpromotions:add" icon="pick" add_text=sp_text_str search_url="wagtailsearchpromotions:index" %}
<div class="nice-padding">
<div id="editorspicks-results" class="redirects">
{% include "wagtailsearch/editorspicks/results.html" %}
{% include "wagtailsearchpromotions/results.html" %}
</div>
</div>
{% endblock %}
{% endblock %}

View file

@ -14,11 +14,11 @@
{% for query in queries %}
<tr>
<td class="title">
<h2><a href="{% url 'wagtailsearch_editorspicks_edit' query.id %}" title="{% trans 'Edit this pick' %}">{{ query.query_string }}</a></h2>
<h2><a href="{% url 'wagtailsearchpromotions:edit' query.id %}" title="{% trans 'Edit this pick' %}">{{ query.query_string }}</a></h2>
</td>
<td>
{% for editors_pick in query.editors_picks.all %}
<a href="{% url 'wagtailadmin_pages_edit' editors_pick.page.id %}" class="nolink">{{ editors_pick.page.title }}</a>{% if not forloop.last %}, {% endif %}
{% for searchpick in query.editors_picks.all %}
<a href="{% url 'wagtailadmin_pages:edit' searchpick.page.id %}" class="nolink">{{ searchpick.page.title }}</a>{% if not forloop.last %}, {% endif %}
{% empty %}
{% trans "None" %}
{% endfor %}
@ -27,4 +27,4 @@
</tr>
{% endfor %}
</tbody>
</table>
</table>

View file

@ -10,14 +10,14 @@
</h2>
{% endif %}
{% include "wagtailsearch/editorspicks/list.html" %}
{% include "wagtailsearchpromotions/list.html" %}
{% include "wagtailadmin/shared/pagination_nav.html" with items=queries is_searching=is_searching linkurl="wagtailsearch_editorspicks_index" %}
{% include "wagtailadmin/shared/pagination_nav.html" with items=queries is_searching=is_searching linkurl="wagtailsearchpromotions:index" %}
{% else %}
{% if is_searching %}
<p>{% blocktrans %}Sorry, no promoted results match "<em>{{ query_string }}</em>"{% endblocktrans %}</p>
{% else %}
{% url 'wagtailsearch_editorspicks_add' as wagtailsearch_editorspicks_add_url %}
<p>{% blocktrans %}No promoted results have been created. Why not <a href="{{ wagtailsearch_editorspicks_add_url }}">add one</a>?{% endblocktrans %}</p>
{% url 'wagtailsearchpromotions:add' as wagtailsearchpromotions_add_url %}
<p>{% blocktrans %}No promoted results have been created. Why not <a href="{{ wagtailsearchpromotions_add_url }}">add one</a>?{% endblocktrans %}</p>
{% endif %}
{% endif %}

View file

@ -0,0 +1,11 @@
from django import template
from wagtail.wagtailsearch.models import Query
register = template.Library()
@register.assignment_tag()
def get_search_promotions(search_query):
return Query.get(search_query).editors_picks.all()

View file

@ -0,0 +1,336 @@
from django.test import TestCase
from django.core.urlresolvers import reverse
from wagtail.tests.utils import WagtailTestUtils
from wagtail.wagtailsearch.models import Query
from wagtail.contrib.wagtailsearchpromotions.models import SearchPromotion
from wagtail.contrib.wagtailsearchpromotions.templatetags.wagtailsearchpromotions_tags import get_search_promotions
class TestSearchPromotions(TestCase):
def test_search_pick_create(self):
# Create a search pick to the root page
SearchPromotion.objects.create(
query=Query.get("root page"),
page_id=1,
sort_order=0,
description="First search pick",
)
# Check
self.assertEqual(Query.get("root page").editors_picks.count(), 1)
self.assertEqual(Query.get("root page").editors_picks.first().page_id, 1)
def test_search_pick_ordering(self):
# Add 3 search picks in a different order to their sort_order values
# They should be ordered by their sort order values and not their insertion order
SearchPromotion.objects.create(
query=Query.get("root page"),
page_id=1,
sort_order=0,
description="First search pick",
)
SearchPromotion.objects.create(
query=Query.get("root page"),
page_id=1,
sort_order=2,
description="Last search pick",
)
SearchPromotion.objects.create(
query=Query.get("root page"),
page_id=1,
sort_order=1,
description="Middle search pick",
)
# Check
self.assertEqual(Query.get("root page").editors_picks.count(), 3)
self.assertEqual(Query.get("root page").editors_picks.first().description, "First search pick")
self.assertEqual(Query.get("root page").editors_picks.last().description, "Last search pick")
class TestGetSearchPromotionsTemplateTag(TestCase):
def test_get_search_promotions_template_tag(self):
# Create a search pick to the root page
pick = SearchPromotion.objects.create(
query=Query.get("root page"),
page_id=1,
sort_order=0,
description="First search pick",
)
# Create another search pick against a different query
SearchPromotion.objects.create(
query=Query.get("root page again"),
page_id=1,
sort_order=0,
description="Second search pick",
)
# Check
search_picks = list(get_search_promotions("root page"))
self.assertEqual(search_picks, [pick])
class TestSearchPromotionsIndexView(TestCase, WagtailTestUtils):
def setUp(self):
self.login()
def test_simple(self):
response = self.client.get(reverse('wagtailsearchpromotions:index'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailsearchpromotions/index.html')
def test_search(self):
response = self.client.get(reverse('wagtailsearchpromotions:index'), {'q': "Hello"})
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['query_string'], "Hello")
def make_search_picks(self):
for i in range(50):
SearchPromotion.objects.create(
query=Query.get("query " + str(i)),
page_id=1,
sort_order=0,
description="First search pick",
)
def test_pagination(self):
self.make_search_picks()
response = self.client.get(reverse('wagtailsearchpromotions:index'), {'p': 2})
# Check response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailsearchpromotions/index.html')
# Check that we got the correct page
self.assertEqual(response.context['queries'].number, 2)
def test_pagination_invalid(self):
self.make_search_picks()
response = self.client.get(reverse('wagtailsearchpromotions:index'), {'p': 'Hello World!'})
# Check response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailsearchpromotions/index.html')
# Check that we got page one
self.assertEqual(response.context['queries'].number, 1)
def test_pagination_out_of_range(self):
self.make_search_picks()
response = self.client.get(reverse('wagtailsearchpromotions:index'), {'p': 99999})
# Check response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailsearchpromotions/index.html')
# Check that we got the last page
self.assertEqual(response.context['queries'].number, response.context['queries'].paginator.num_pages)
class TestSearchPromotionsAddView(TestCase, WagtailTestUtils):
def setUp(self):
self.login()
def test_simple(self):
response = self.client.get(reverse('wagtailsearchpromotions:add'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailsearchpromotions/add.html')
def test_post(self):
# Submit
post_data = {
'query_string': "test",
'editors_picks-TOTAL_FORMS': 1,
'editors_picks-INITIAL_FORMS': 0,
'editors_picks-MAX_NUM_FORMS': 1000,
'editors_picks-0-DELETE': '',
'editors_picks-0-ORDER': 0,
'editors_picks-0-page': 1,
'editors_picks-0-description': "Hello",
}
response = self.client.post(reverse('wagtailsearchpromotions:add'), post_data)
# User should be redirected back to the index
self.assertRedirects(response, reverse('wagtailsearchpromotions:index'))
# Check that the search pick was created
self.assertTrue(Query.get('test').editors_picks.filter(page_id=1).exists())
def test_post_without_recommendations(self):
# Submit
post_data = {
'query_string': "test",
'editors_picks-TOTAL_FORMS': 0,
'editors_picks-INITIAL_FORMS': 0,
'editors_picks-MAX_NUM_FORMS': 1000,
}
response = self.client.post(reverse('wagtailsearchpromotions:add'), post_data)
# User should be given an error
self.assertEqual(response.status_code, 200)
self.assertFormsetError(response, 'searchpicks_formset', None, None, "Please specify at least one recommendation for this search term.")
class TestSearchPromotionsEditView(TestCase, WagtailTestUtils):
def setUp(self):
self.login()
# Create an search pick to edit
self.query = Query.get("Hello")
self.search_pick = self.query.editors_picks.create(page_id=1, description="Root page")
self.search_pick_2 = self.query.editors_picks.create(page_id=2, description="Homepage")
def test_simple(self):
response = self.client.get(reverse('wagtailsearchpromotions:edit', args=(self.query.id, )))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailsearchpromotions/edit.html')
def test_post(self):
# Submit
post_data = {
'query_string': "Hello",
'editors_picks-TOTAL_FORMS': 2,
'editors_picks-INITIAL_FORMS': 2,
'editors_picks-MAX_NUM_FORMS': 1000,
'editors_picks-0-id': self.search_pick.id,
'editors_picks-0-DELETE': '',
'editors_picks-0-ORDER': 0,
'editors_picks-0-page': 1,
'editors_picks-0-description': "Description has changed", # Change
'editors_picks-1-id': self.search_pick_2.id,
'editors_picks-1-DELETE': '',
'editors_picks-1-ORDER': 1,
'editors_picks-1-page': 2,
'editors_picks-1-description': "Homepage",
}
response = self.client.post(reverse('wagtailsearchpromotions:edit', args=(self.query.id, )), post_data)
# User should be redirected back to the index
self.assertRedirects(response, reverse('wagtailsearchpromotions:index'))
# Check that the search pick description was edited
self.assertEqual(SearchPromotion.objects.get(id=self.search_pick.id).description, "Description has changed")
def test_post_reorder(self):
# Check order before reordering
self.assertEqual(Query.get("Hello").editors_picks.all()[0], self.search_pick)
self.assertEqual(Query.get("Hello").editors_picks.all()[1], self.search_pick_2)
# Submit
post_data = {
'query_string': "Hello",
'editors_picks-TOTAL_FORMS': 2,
'editors_picks-INITIAL_FORMS': 2,
'editors_picks-MAX_NUM_FORMS': 1000,
'editors_picks-0-id': self.search_pick.id,
'editors_picks-0-DELETE': '',
'editors_picks-0-ORDER': 1, # Change
'editors_picks-0-page': 1,
'editors_picks-0-description': "Root page",
'editors_picks-1-id': self.search_pick_2.id,
'editors_picks-1-DELETE': '',
'editors_picks-1-ORDER': 0, # Change
'editors_picks-1-page': 2,
'editors_picks-1-description': "Homepage",
}
response = self.client.post(reverse('wagtailsearchpromotions:edit', args=(self.query.id, )), post_data)
# User should be redirected back to the index
self.assertRedirects(response, reverse('wagtailsearchpromotions:index'))
# Check that the ordering has been saved correctly
self.assertEqual(SearchPromotion.objects.get(id=self.search_pick.id).sort_order, 1)
self.assertEqual(SearchPromotion.objects.get(id=self.search_pick_2.id).sort_order, 0)
# Check that the recommendations were reordered
self.assertEqual(Query.get("Hello").editors_picks.all()[0], self.search_pick_2)
self.assertEqual(Query.get("Hello").editors_picks.all()[1], self.search_pick)
def test_post_delete_recommendation(self):
# Submit
post_data = {
'query_string': "Hello",
'editors_picks-TOTAL_FORMS': 2,
'editors_picks-INITIAL_FORMS': 2,
'editors_picks-MAX_NUM_FORMS': 1000,
'editors_picks-0-id': self.search_pick.id,
'editors_picks-0-DELETE': '',
'editors_picks-0-ORDER': 0,
'editors_picks-0-page': 1,
'editors_picks-0-description': "Root page",
'editors_picks-1-id': self.search_pick_2.id,
'editors_picks-1-DELETE': 1,
'editors_picks-1-ORDER': 1,
'editors_picks-1-page': 2,
'editors_picks-1-description': "Homepage",
}
response = self.client.post(reverse('wagtailsearchpromotions:edit', args=(self.query.id, )), post_data)
# User should be redirected back to the index
self.assertRedirects(response, reverse('wagtailsearchpromotions:index'))
# Check that the recommendation was deleted
self.assertFalse(SearchPromotion.objects.filter(id=self.search_pick_2.id).exists())
# The other recommendation should still exist
self.assertTrue(SearchPromotion.objects.filter(id=self.search_pick.id).exists())
def test_post_without_recommendations(self):
# Submit
post_data = {
'query_string': "Hello",
'editors_picks-TOTAL_FORMS': 2,
'editors_picks-INITIAL_FORMS': 2,
'editors_picks-MAX_NUM_FORMS': 1000,
'editors_picks-0-id': self.search_pick.id,
'editors_picks-0-DELETE': 1,
'editors_picks-0-ORDER': 0,
'editors_picks-0-page': 1,
'editors_picks-0-description': "Description has changed", # Change
'editors_picks-1-id': self.search_pick_2.id,
'editors_picks-1-DELETE': 1,
'editors_picks-1-ORDER': 1,
'editors_picks-1-page': 2,
'editors_picks-1-description': "Homepage",
}
response = self.client.post(reverse('wagtailsearchpromotions:edit', args=(self.query.id, )), post_data)
# User should be given an error
self.assertEqual(response.status_code, 200)
self.assertFormsetError(response, 'searchpicks_formset', None, None, "Please specify at least one recommendation for this search term.")
class TestSearchPromotionsDeleteView(TestCase, WagtailTestUtils):
def setUp(self):
self.login()
# Create an search pick to delete
self.query = Query.get("Hello")
self.search_pick = self.query.editors_picks.create(page_id=1, description="Root page")
self.search_pick_2 = self.query.editors_picks.create(page_id=2, description="Homepage")
def test_simple(self):
response = self.client.get(reverse('wagtailsearchpromotions:delete', args=(self.query.id, )))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailsearchpromotions/confirm_delete.html')
def test_post(self):
# Submit
post_data = {
'foo': 'bar',
}
response = self.client.post(reverse('wagtailsearchpromotions:delete', args=(self.query.id, )), post_data)
# User should be redirected back to the index
self.assertRedirects(response, reverse('wagtailsearchpromotions:index'))
# Check that both recommendations were deleted
self.assertFalse(SearchPromotion.objects.filter(id=self.search_pick_2.id).exists())
# The other recommendation should still exist
self.assertFalse(SearchPromotion.objects.filter(id=self.search_pick.id).exists())

View file

@ -5,10 +5,13 @@ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.utils.translation import ugettext as _
from django.views.decorators.vary import vary_on_headers
from wagtail.wagtailsearch import models, forms
from wagtail.wagtailsearch import forms as search_forms
from wagtail.wagtailsearch.models import Query
from wagtail.wagtailadmin.forms import SearchForm
from wagtail.wagtailadmin import messages
from wagtail.contrib.wagtailsearchpromotions import forms
@vary_on_headers('X-Requested-With')
def index(request):
@ -16,7 +19,7 @@ def index(request):
page = request.GET.get('p', 1)
query_string = request.GET.get('q', "")
queries = models.Query.objects.filter(editors_picks__isnull=False).distinct()
queries = Query.objects.filter(editors_picks__isnull=False).distinct()
# Search
if query_string:
@ -33,13 +36,13 @@ def index(request):
queries = paginator.page(paginator.num_pages)
if request.is_ajax():
return render(request, "wagtailsearch/editorspicks/results.html", {
return render(request, "wagtailsearchpromotions/results.html", {
'is_searching': is_searching,
'queries': queries,
'query_string': query_string,
})
else:
return render(request, 'wagtailsearch/editorspicks/index.html', {
return render(request, 'wagtailsearchpromotions/index.html', {
'is_searching': is_searching,
'queries': queries,
'query_string': query_string,
@ -47,21 +50,21 @@ def index(request):
})
def save_editorspicks(query, new_query, editors_pick_formset):
def save_searchpicks(query, new_query, searchpicks_formset):
# Save
if editors_pick_formset.is_valid():
if searchpicks_formset.is_valid():
# Set sort_order
for i, form in enumerate(editors_pick_formset.ordered_forms):
for i, form in enumerate(searchpicks_formset.ordered_forms):
form.instance.sort_order = i
# Make sure the form is marked as changed so it gets saved with the new order
form.has_changed = lambda: True
editors_pick_formset.save()
searchpicks_formset.save()
# If query was changed, move all editors picks to the new query
# If query was changed, move all search picks to the new query
if query != new_query:
editors_pick_formset.get_queryset().update(query=new_query)
searchpicks_formset.get_queryset().update(query=new_query)
return True
else:
@ -71,77 +74,77 @@ def save_editorspicks(query, new_query, editors_pick_formset):
def add(request):
if request.POST:
# Get query
query_form = forms.QueryForm(request.POST)
query_form = search_forms.QueryForm(request.POST)
if query_form.is_valid():
query = models.Query.get(query_form['query_string'].value())
query = Query.get(query_form['query_string'].value())
# Save editors picks
editors_pick_formset = forms.EditorsPickFormSet(request.POST, instance=query)
if save_editorspicks(query, query, editors_pick_formset):
# Save search picks
searchpicks_formset = forms.SearchPromotionsFormSet(request.POST, instance=query)
if save_searchpicks(query, query, searchpicks_formset):
messages.success(request, _("Editor's picks for '{0}' created.").format(query), buttons=[
messages.button(reverse('wagtailsearch_editorspicks_edit', args=(query.id,)), _('Edit'))
messages.button(reverse('wagtailsearchpromotions:edit', args=(query.id,)), _('Edit'))
])
return redirect('wagtailsearch_editorspicks_index')
return redirect('wagtailsearchpromotions:index')
else:
if len(editors_pick_formset.non_form_errors()):
messages.error(request, " ".join(error for error in editors_pick_formset.non_form_errors())) # formset level error (e.g. no forms submitted)
if len(searchpicks_formset.non_form_errors()):
messages.error(request, " ".join(error for error in searchpicks_formset.non_form_errors())) # formset level error (e.g. no forms submitted)
else:
messages.error(request, _("Recommendations have not been created due to errors")) # specific errors will be displayed within form fields
else:
editors_pick_formset = forms.EditorsPickFormSet()
searchpicks_formset = forms.SearchPromotionsFormSet()
else:
query_form = forms.QueryForm()
editors_pick_formset = forms.EditorsPickFormSet()
query_form = search_forms.QueryForm()
searchpicks_formset = forms.SearchPromotionsFormSet()
return render(request, 'wagtailsearch/editorspicks/add.html', {
return render(request, 'wagtailsearchpromotions/add.html', {
'query_form': query_form,
'editors_pick_formset': editors_pick_formset,
'searchpicks_formset': searchpicks_formset,
})
def edit(request, query_id):
query = get_object_or_404(models.Query, id=query_id)
query = get_object_or_404(Query, id=query_id)
if request.POST:
# Get query
query_form = forms.QueryForm(request.POST)
query_form = search_forms.QueryForm(request.POST)
# and the recommendations
editors_pick_formset = forms.EditorsPickFormSet(request.POST, instance=query)
searchpicks_formset = forms.SearchPromotionsFormSet(request.POST, instance=query)
if query_form.is_valid():
new_query = models.Query.get(query_form['query_string'].value())
new_query = Query.get(query_form['query_string'].value())
# Save editors picks
if save_editorspicks(query, new_query, editors_pick_formset):
# Save search picks
if save_searchpicks(query, new_query, searchpicks_formset):
messages.success(request, _("Editor's picks for '{0}' updated.").format(new_query), buttons=[
messages.button(reverse('wagtailsearch_editorspicks_edit', args=(query.id,)), _('Edit'))
messages.button(reverse('wagtailsearchpromotions:edit', args=(query.id,)), _('Edit'))
])
return redirect('wagtailsearch_editorspicks_index')
return redirect('wagtailsearchpromotions:index')
else:
if len(editors_pick_formset.non_form_errors()):
messages.error(request, " ".join(error for error in editors_pick_formset.non_form_errors())) # formset level error (e.g. no forms submitted)
if len(searchpicks_formset.non_form_errors()):
messages.error(request, " ".join(error for error in searchpicks_formset.non_form_errors())) # formset level error (e.g. no forms submitted)
else:
messages.error(request, _("Recommendations have not been saved due to errors")) # specific errors will be displayed within form fields
else:
query_form = forms.QueryForm(initial=dict(query_string=query.query_string))
editors_pick_formset = forms.EditorsPickFormSet(instance=query)
query_form = search_forms.QueryForm(initial=dict(query_string=query.query_string))
searchpicks_formset = forms.SearchPromotionsFormSet(instance=query)
return render(request, 'wagtailsearch/editorspicks/edit.html', {
return render(request, 'wagtailsearchpromotions/edit.html', {
'query_form': query_form,
'editors_pick_formset': editors_pick_formset,
'searchpicks_formset': searchpicks_formset,
'query': query,
})
def delete(request, query_id):
query = get_object_or_404(models.Query, id=query_id)
query = get_object_or_404(Query, id=query_id)
if request.POST:
query.editors_picks.all().delete()
messages.success(request, _("Editor's picks deleted."))
return redirect('wagtailsearch_editorspicks_index')
return redirect('wagtailsearchpromotions:index')
return render(request, 'wagtailsearch/editorspicks/confirm_delete.html', {
return render(request, 'wagtailsearchpromotions/confirm_delete.html', {
'query': query,
})

View file

@ -0,0 +1,26 @@
from django.core import urlresolvers
from django.conf.urls import include, url
from django.utils.translation import ugettext_lazy as _
from wagtail.wagtailcore import hooks
from wagtail.contrib.wagtailsearchpromotions import admin_urls
from wagtail.wagtailadmin.menu import MenuItem
@hooks.register('register_admin_urls')
def register_admin_urls():
return [
url(r'^searchpicks/', include(admin_urls, namespace='wagtailsearchpromotions')),
]
class SearchPicksMenuItem(MenuItem):
def is_shown(self, request):
# TEMPORARY: Only show if the user is a superuser
return request.user.is_superuser
@hooks.register('register_settings_menu_item')
def register_search_picks_menu_item():
return SearchPicksMenuItem(_('Promoted search results'), urlresolvers.reverse('wagtailsearchpromotions:index'), classnames='icon icon-pick', order=900)

View file

@ -374,7 +374,7 @@
{% include "wagtailadmin/shared/header.html" with title=title_trans %}
{% include "wagtailadmin/shared/header.html" with title=title_trans add_link="wagtailimages_add_image" icon="image" add_text="button" search_url="wagtailimages_index" %}
{% include "wagtailadmin/shared/header.html" with title=title_trans add_link="wagtailimages_add_image" icon="image" add_text="button" search_url="wagtailimages:index" %}
</section>
<section id="forms">

View file

@ -23,33 +23,33 @@ BASE_DIR = os.path.dirname(PROJECT_DIR)
# Application definition
INSTALLED_APPS = (
INSTALLED_APPS = [
'home',
'search',
'wagtail.wagtailforms',
'wagtail.wagtailredirects',
'wagtail.wagtailembeds',
'wagtail.wagtailsites',
'wagtail.wagtailusers',
'wagtail.wagtailsnippets',
'wagtail.wagtaildocs',
'wagtail.wagtailimages',
'wagtail.wagtailsearch',
'wagtail.wagtailadmin',
'wagtail.wagtailcore',
'modelcluster',
'compressor',
'taggit',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'taggit',
'compressor',
'modelcluster',
'wagtail.wagtailcore',
'wagtail.wagtailadmin',
'wagtail.wagtailsearch',
'wagtail.wagtailimages',
'wagtail.wagtaildocs',
'wagtail.wagtailsnippets',
'wagtail.wagtailusers',
'wagtail.wagtailsites',
'wagtail.wagtailembeds',
'wagtail.wagtailredirects',
'wagtail.wagtailforms',
'search',
'home',
)
]
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',

View file

@ -1,2 +1,2 @@
Django>=1.8,<1.9
wagtail==1.0rc1
wagtail==1.0

View file

@ -78,42 +78,43 @@ MIDDLEWARE_CLASSES = (
)
INSTALLED_APPS = (
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.auth',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.admin',
# 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
'wagtail.wagtailredirects.apps.WagtailRedirectsAppConfig',
'taggit',
'compressor',
'wagtail.wagtailcore',
'wagtail.wagtailadmin',
'wagtail.wagtaildocs',
'wagtail.wagtailsnippets',
'wagtail.wagtailusers',
'wagtail.wagtailsites',
'wagtail.wagtailimages',
'wagtail.wagtailembeds',
'wagtail.wagtailsearch',
'wagtail.wagtailforms',
'wagtail.contrib.wagtailstyleguide',
'wagtail.contrib.wagtailsitemaps',
'wagtail.contrib.wagtailroutablepage',
'wagtail.contrib.wagtailfrontendcache',
'wagtail.contrib.wagtailapi',
'wagtail.tests.testapp',
'wagtail.tests.demosite',
'wagtail.tests.customuser',
'wagtail.tests.snippets',
'wagtail.tests.routablepage',
'wagtail.tests.search',
'wagtail.contrib.wagtailstyleguide',
'wagtail.contrib.wagtailsitemaps',
'wagtail.contrib.wagtailroutablepage',
'wagtail.contrib.wagtailfrontendcache',
'wagtail.contrib.wagtailapi',
'wagtail.contrib.wagtailsearchpromotions',
'wagtail.wagtailforms',
'wagtail.wagtailsearch',
'wagtail.wagtailembeds',
'wagtail.wagtailimages',
'wagtail.wagtailsites',
'wagtail.wagtailusers',
'wagtail.wagtailsnippets',
'wagtail.wagtaildocs',
'wagtail.wagtailadmin',
'wagtail.wagtailcore',
# 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
'wagtail.wagtailredirects.apps.WagtailRedirectsAppConfig',
'taggit',
'compressor',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
)

View file

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('tests', '0005_streampage'),
]
operations = [
migrations.AddField(
model_name='customimagewithadminformfields',
name='file_size',
field=models.PositiveIntegerField(null=True, editable=False),
),
migrations.AddField(
model_name='customimagewithoutadminformfields',
name='file_size',
field=models.PositiveIntegerField(null=True, editable=False),
),
]

View file

@ -335,7 +335,7 @@ function initCollapsibleBlocks() {
$fieldset.hide();
}
$li.find('h2').click(function() {
$li.find('> h2').click(function() {
if (!$li.hasClass('collapsed')) {
$li.addClass('collapsed');
$fieldset.hide('slow');

View file

@ -293,33 +293,18 @@
min-height: 41px;
h2{
cursor: pointer;
}
h2:before{
content: '6';
text-shadow:none;
line-height: 40px;
padding-right: 1px;
opacity: 1;
color: #666;
background-color: transparent;
@include transition(background-color 0.2s ease, color 0.2s ease);
}
h2:hover:before{
&:before, label:before{
content: '6';
cursor: pointer;
}
}
&.collapsed{
background: #fff;
h2{
@include box-shadow(none);
}
h2:before{
content: '5';
color: #fff;
background-color:$color-teal;
}
h2:hover:before{
background-color:$color-teal-darker;
&:before, label:before{
content: '5';
}
}
}
}

View file

@ -11,7 +11,7 @@
<span>{% trans "Dashboard" %}</span>
</a>
<form class="nav-search" action="{% url 'wagtailadmin_pages_search' %}" method="get">
<form class="nav-search" action="{% url 'wagtailadmin_pages:search' %}" method="get">
<div>
<label for="menu-search-q">{% trans "Search" %}</label>
<input type="text" id="menu-search-q" name="q" placeholder="{% trans 'Search' %}" />

View file

@ -21,26 +21,26 @@
<tr>
<td class="title" valign="top">
<h2>
<a href="{% url 'wagtailadmin_pages_edit' revision.page.id %}" title="{% trans 'Edit this page' %}">{{ revision.page.title }}</a>
<a href="{% url 'wagtailadmin_pages:edit' revision.page.id %}" title="{% trans 'Edit this page' %}">{{ revision.page.title }}</a>
{% include "wagtailadmin/pages/listing/_privacy_indicator.html" with page=revision.page %}
{% include "wagtailadmin/pages/listing/_locked_indicator.html" with page=revision.page %}
</h2>
<ul class="actions">
<li>
<form action="{% url 'wagtailadmin_pages_approve_moderation' revision.id %}" method="POST">
<form action="{% url 'wagtailadmin_pages:approve_moderation' revision.id %}" method="POST">
{% csrf_token %}
<input type="submit" class="button button-small button-secondary" value="{% trans 'Approve' %}">
</form>
</li>
<li class="no-border">
<form action="{% url 'wagtailadmin_pages_reject_moderation' revision.id %}" method="POST">
<form action="{% url 'wagtailadmin_pages:reject_moderation' revision.id %}" method="POST">
{% csrf_token %}
<input type="submit" class="button button-small button-secondary no" value="{% trans 'Reject' %}">
</form>
</li>
<li><a href="{% url 'wagtailadmin_pages_edit' revision.page.id %}" class="button button-small button-secondary">{% trans 'Edit' %}</a></li>
<li><a href="{% url 'wagtailadmin_pages_preview_for_moderation' revision.id %}" class="button button-small button-secondary">{% trans 'Preview' %}</a></li>
<li><a href="{% url 'wagtailadmin_pages:edit' revision.page.id %}" class="button button-small button-secondary">{% trans 'Edit' %}</a></li>
<li><a href="{% url 'wagtailadmin_pages:preview_for_moderation' revision.id %}" class="button button-small button-secondary">{% trans 'Preview' %}</a></li>
</ul>
</td>
<td valign="top">

View file

@ -19,15 +19,15 @@
<tr>
<td class="title" valign="top">
<h2>
<a href="{% url 'wagtailadmin_pages_edit' revision.page.id %}" title="{% trans 'Edit this page' %}">{{ revision.page.title }}</a>
<a href="{% url 'wagtailadmin_pages:edit' revision.page.id %}" title="{% trans 'Edit this page' %}">{{ revision.page.title }}</a>
{% include "wagtailadmin/pages/listing/_privacy_indicator.html" with page=revision.page %}
{% include "wagtailadmin/pages/listing/_locked_indicator.html" with page=revision.page %}
</h2>
<ul class="actions">
<li><a href="{% url 'wagtailadmin_pages_edit' revision.page.id %}" class="button button-small button-secondary">{% trans "Edit" %}</a></li>
<li><a href="{% url 'wagtailadmin_pages:edit' revision.page.id %}" class="button button-small button-secondary">{% trans "Edit" %}</a></li>
{% if revision.page.has_unpublished_changes %}
<li><a href="{% url 'wagtailadmin_pages_view_draft' revision.page.id %}" class="button button-small button-secondary">{% trans 'Draft' %}</a></li>
<li><a href="{% url 'wagtailadmin_pages:view_draft' revision.page.id %}" class="button button-small button-secondary">{% trans 'Draft' %}</a></li>
{% endif %}
{% if revision.page.live %}
<li><a href="{{ revision.page.url }}" class="button button-small button-secondary">{% trans 'Live' %}</a></li>

View file

@ -1,4 +1,4 @@
{% extends 'wagtailadmin/notifications/base_notification.html' %}{% block notification %}{% load i18n %}{% blocktrans with title=revision.page.title|safe %}The page "{{ title }}" has been rejected{% endblocktrans %}
{% blocktrans with title=revision.page.title|safe %}The page "{{ title }}" has been rejected.{% endblocktrans %}
{% trans "You can edit the page here:"%} {{ settings.BASE_URL }}{% url 'wagtailadmin_pages_edit' revision.page.id %}{% endblock %}
{% trans "You can edit the page here:"%} {{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' revision.page.id %}{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends 'wagtailadmin/notifications/base_notification.html' %}{% block notification %}{% load i18n %}{% blocktrans with page=revision.page|safe %}The page "{{ page }}" has been submitted for moderation{% endblocktrans %}
{% blocktrans with page=revision.page|safe %}The page "{{ page }}" has been submitted for moderation.{% endblocktrans %}
{% trans "You can preview the page here:" %} {{ settings.BASE_URL }}{% url 'wagtailadmin_pages_preview_for_moderation' revision.id %}
{% trans "You can edit the page here:" %} {{ settings.BASE_URL }}{% url 'wagtailadmin_pages_edit' revision.page.id %}{% endblock %}
{% trans "You can preview the page here:" %} {{ settings.BASE_URL }}{% url 'wagtailadmin_pages:preview_for_moderation' revision.id %}
{% trans "You can edit the page here:" %} {{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' revision.page.id %}{% endblock %}

View file

@ -4,5 +4,5 @@
<div class="nice-padding">
<p>{% trans "This page has been made private by a parent page." %}</p>
<p>{% trans "You can edit the privacy settings on:" %} <a href="{% url 'wagtailadmin_pages_edit' page_with_restriction.id %}">{{ page_with_restriction.title }}</a>
<p>{% trans "You can edit the privacy settings on:" %} <a href="{% url 'wagtailadmin_pages:edit' page_with_restriction.id %}">{{ page_with_restriction.title }}</a>
</div>

View file

@ -4,7 +4,7 @@
<div class="nice-padding">
<p class="help-block help-warning">{% trans "Privacy changes apply to all children of this page too." %}</p>
<form action="{% url 'wagtailadmin_pages_set_privacy' page.id %}" method="POST">
<form action="{% url 'wagtailadmin_pages:set_privacy' page.id %}" method="POST">
{% csrf_token %}
<ul class="fields">
{% include "wagtailadmin/shared/field_as_li.html" with field=form.restriction_type %}

View file

@ -9,9 +9,9 @@
{% if page.locked %}
{% if page_perms.can_lock %}
<form action="{% url 'wagtailadmin_pages_unlock' page.id %}" method="POST" class="status-tag primary">
<form action="{% url 'wagtailadmin_pages:unlock' page.id %}" method="POST" class="status-tag primary">
{% csrf_token %}
<input type="hidden" name="next" value="{% url 'wagtailadmin_pages_edit' page.id %}">
<input type="hidden" name="next" value="{% url 'wagtailadmin_pages:edit' page.id %}">
<input type="submit" class="unbutton" value="{% trans "Locked" %}">
</form>
{% else %}
@ -19,9 +19,9 @@
{% endif %}
{% else %}
{% if page_perms.can_lock %}
<form action="{% url 'wagtailadmin_pages_lock' page.id %}" method="POST" class="status-tag secondary">
<form action="{% url 'wagtailadmin_pages:lock' page.id %}" method="POST" class="status-tag secondary">
{% csrf_token %}
<input type="hidden" name="next" value="{% url 'wagtailadmin_pages_edit' page.id %}">
<input type="hidden" name="next" value="{% url 'wagtailadmin_pages:edit' page.id %}">
<input type="submit" class="unbutton" value="{% trans "Unlocked" %}">
</form>
{% else %}

View file

@ -1,4 +1,4 @@
<button class="action-preview {% if icon %}icon icon-view{% endif %}"
data-action="{% url 'wagtailadmin_pages_preview_on_create' content_type.app_label content_type.model parent_page.id %}{% if mode %}?mode={{ mode|urlencode }}{% endif %}"
data-placeholder="{% url 'wagtailadmin_pages_preview' %}"
data-action="{% url 'wagtailadmin_pages:preview_on_add' content_type.app_label content_type.model parent_page.id %}{% if mode %}?mode={{ mode|urlencode }}{% endif %}"
data-placeholder="{% url 'wagtailadmin_pages:preview' %}"
data-windowname="wagtail_preview_{{ parent_page.id }}_child">{{ label }}</button>

View file

@ -1,4 +1,4 @@
<button class="action-preview {% if icon %}icon icon-view{% endif %}"
data-action="{% url 'wagtailadmin_pages_preview_on_edit' page.id %}{% if mode %}?mode={{ mode|urlencode }}{% endif %}"
data-placeholder="{% url 'wagtailadmin_pages_preview' %}"
data-action="{% url 'wagtailadmin_pages:preview_on_edit' page.id %}{% if mode %}?mode={{ mode|urlencode }}{% endif %}"
data-placeholder="{% url 'wagtailadmin_pages:preview' %}"
data-windowname="wagtail_preview_{{ page.id }}">{{ label }}</button>

View file

@ -8,7 +8,7 @@
<div class="privacy-indicator {% if is_public %}public{% else %}private{% endif %}">
{% trans "Privacy" %}
{% if page_perms.can_set_view_restrictions %}
<a href="{% url 'wagtailadmin_pages_set_privacy' page.id %}" class="status-tag primary action-set-privacy">
<a href="{% url 'wagtailadmin_pages:set_privacy' page.id %}" class="status-tag primary action-set-privacy">
{# labels are shown/hidden in CSS according to the 'private' / 'public' class on view-permission-indicator #}
<span class="label-public icon icon-view">{% trans 'Public' %}</span>
<span class="label-private icon icon-no-view">{% trans 'Private' %}</span>

View file

@ -18,12 +18,12 @@
<li>
<div class="row row-flush">
<div class="col6">
<a href="{% url 'wagtailadmin_pages_create' content_type.app_label content_type.model parent_page.id %}" class="icon icon-plus-inverse icon-larger">{{ content_type.model_class.get_verbose_name }}</a>
<a href="{% url 'wagtailadmin_pages:add' content_type.app_label content_type.model parent_page.id %}" class="icon icon-plus-inverse icon-larger">{{ content_type.model_class.get_verbose_name }}</a>
</div>
<small class="col6" style="text-align:right">
{{ content_type|meta_description }}
<a href="{% url 'wagtailadmin_pages_type_use' content_type.app_label content_type.model %}" class="nolink">{% blocktrans with page_type=content_type.model_class.get_verbose_name %}Pages using {{ page_type }}{% endblocktrans %}</a>
<a href="{% url 'wagtailadmin_pages:type_use' content_type.app_label content_type.model %}" class="nolink">{% blocktrans with page_type=content_type.model_class.get_verbose_name %}Pages using {{ page_type }}{% endblocktrans %}</a>
</small>
</div>

View file

@ -21,9 +21,9 @@
{% if page.live %}
<p>{% trans "Alternatively you can unpublish the page. This removes the page from public view and you can edit or publish it again later." %}</p>
{% endif %}
<form action="{% url 'wagtailadmin_pages_delete' page.id %}" method="POST">
<form action="{% url 'wagtailadmin_pages:delete' page.id %}" method="POST">
{% csrf_token %}
<input type="submit" value="{% trans 'Delete it' %}" class="serious {% if page.live %}button-secondary{% endif %}"> {% if page.live %}<a href="{% url 'wagtailadmin_pages_unpublish' page.id %}" class="button">{% trans 'Unpublish it' %}</a>{% endif %}
<input type="submit" value="{% trans 'Delete it' %}" class="serious {% if page.live %}button-secondary{% endif %}"> {% if page.live %}<a href="{% url 'wagtailadmin_pages:unpublish' page.id %}" class="button">{% trans 'Unpublish it' %}</a>{% endif %}
</form>
</div>
{% endblock %}

View file

@ -13,7 +13,7 @@
<p>{% blocktrans with title=destination.title %}Are you sure you want to move this page and all of its children into '{{ title }}'?{% endblocktrans %}</p>
{% endif %}
<form action="{% url 'wagtailadmin_pages_move_confirm' page_to_move.id destination.id %}" method="POST">
<form action="{% url 'wagtailadmin_pages:move_confirm' page_to_move.id destination.id %}" method="POST">
{% csrf_token %}
<input type="submit" value="{% trans 'Yes, move this page' %}">
</form>

View file

@ -8,7 +8,7 @@
<div class="nice-padding">
<p>{% trans "Are you sure you want to unpublish this page?" %}</p>
<form action="{% url 'wagtailadmin_pages_unpublish' page.id %}" method="POST">
<form action="{% url 'wagtailadmin_pages:unpublish' page.id %}" method="POST">
{% csrf_token %}
<input type="submit" value="{% trans 'Yes, unpublish it' %}">
</form>

View file

@ -7,7 +7,7 @@
{% include "wagtailadmin/shared/header.html" with title=copy_str subtitle=page.title icon="doc-empty-inverse" %}
<div class="nice-padding">
<form action="{% url 'wagtailadmin_pages_copy' page.id %}" method="POST">
<form action="{% url 'wagtailadmin_pages:copy' page.id %}" method="POST">
{% csrf_token %}
<ul class="fields">

View file

@ -17,7 +17,7 @@
</div>
</header>
<form id="page-edit-form" action="{% url 'wagtailadmin_pages_create' content_type.app_label content_type.model parent_page.id %}" method="POST">
<form id="page-edit-form" action="{% url 'wagtailadmin_pages:add' content_type.app_label content_type.model parent_page.id %}" method="POST">
{% csrf_token %}
{{ edit_handler.render_form_content }}

View file

@ -24,7 +24,7 @@
</div>
</header>
<form id="page-edit-form" action="{% url 'wagtailadmin_pages_edit' page.id %}" method="POST">
<form id="page-edit-form" action="{% url 'wagtailadmin_pages:edit' page.id %}" method="POST">
{% csrf_token %}
{{ edit_handler.render_form_content }}
@ -38,10 +38,10 @@
<div class="dropdown-toggle icon icon-arrow-up"></div>
<ul role="menu">
{% if page_perms.can_unpublish %}
<li><a href="{% url 'wagtailadmin_pages_unpublish' page.id %}">{% trans 'Unpublish' %}</a></li>
<li><a href="{% url 'wagtailadmin_pages:unpublish' page.id %}">{% trans 'Unpublish' %}</a></li>
{% endif %}
{% if page_perms.can_delete %}
<li><a href="{% url 'wagtailadmin_pages_delete' page.id %}" class="shortcut">{% trans 'Delete' %}</a></li>
<li><a href="{% url 'wagtailadmin_pages:delete' page.id %}" class="shortcut">{% trans 'Delete' %}</a></li>
{% endif %}
{% if page_perms.can_publish %}
<li><input type="submit" name="action-publish" value="{% trans 'Publish' %}" class="button" /></li>

View file

@ -58,7 +58,7 @@
// Build url
// TODO: Find better way to inject movedPageId
var url = "{% url 'wagtailadmin_pages_set_page_position' '999999' %}".replace('999999', movedPageId);
var url = "{% url 'wagtailadmin_pages:set_page_position' '999999' %}".replace('999999', movedPageId);
if (newPosition != null) {
url += '?position=' + newPosition;
}

View file

@ -21,6 +21,6 @@
{% endblock %}
{% block no_results %}
{% url 'wagtailadmin_pages_add_subpage' parent_page.id as add_page_url%}
{% url 'wagtailadmin_pages:add_subpage' parent_page.id as add_page_url%}
<tr><td colspan="3" class="no-results-message"><p>{% trans "No pages have been created at this location." %}{% if parent_page and parent_page_perms.can_add_subpage %} {% blocktrans %}Why not <a href="{{ add_page_url }}">create one</a>?{% endblocktrans %}{% endif %}</td></tr>
{% endblock %}

View file

@ -9,7 +9,7 @@ Navigation controls for the page listing in 'explore' mode
{% if page.is_navigable %}
<a href="{% url 'wagtailadmin_explore' page.id %}" class="icon text-replace icon-arrow-right" title="{% blocktrans with title=page.title %}Explore child pages of '{{ title }}'{% endblocktrans %}">{% trans "Explore" %}</a>
{% elif page_perms.can_add_subpage %}
<a href="{% url 'wagtailadmin_pages_add_subpage' page.id %}" class="icon text-replace icon-plus-inverse" title="{% blocktrans with title=page.title %}Add a child page to '{{ title }}'{% endblocktrans %}">{% trans 'Add subpage' %}</a>
<a href="{% url 'wagtailadmin_pages:add_subpage' page.id %}" class="icon text-replace icon-plus-inverse" title="{% blocktrans with title=page.title %}Add a child page to '{{ title }}'{% endblocktrans %}">{% trans 'Add subpage' %}</a>
{% endif %}
{% endif %}
</td>

View file

@ -6,6 +6,6 @@ Navigation controls for the page listing in 'move' mode
<td class="{% if page.can_descend %}children{% endif %}">
{% if page.can_descend %}
<a href="{% url 'wagtailadmin_pages_move_choose_destination' page_to_move.id page.id %}" class="icon text-replace icon-arrow-right navigate-pages" title="{% blocktrans with title=page.title %}Explore subpages of '{{ title }}'{% endblocktrans %}">{% trans 'Explore' %}</a>
<a href="{% url 'wagtailadmin_pages:move_choose_destination' page_to_move.id page.id %}" class="icon text-replace icon-arrow-right navigate-pages" title="{% blocktrans with title=page.title %}Explore subpages of '{{ title }}'{% endblocktrans %}">{% trans 'Explore' %}</a>
{% endif %}
</td>

View file

@ -6,7 +6,7 @@ Expects a variable 'page', the page instance.
<h2>
{% if page.can_choose %}
<a class="choose-page" href="#{{ page.id }}" data-id="{{ page.id }}" data-title="{{ page.title }}" data-url="{{ page.url }}" data-edit-url="{% url 'wagtailadmin_pages_edit' page.id %}">{{ page.title }}</a>
<a class="choose-page" href="#{{ page.id }}" data-id="{{ page.id }}" data-title="{{ page.title }}" data-url="{{ page.url }}" data-edit-url="{% url 'wagtailadmin_pages:edit' page.id %}">{{ page.title }}</a>
{% else %}
{{ page.title }}
{% endif %}

View file

@ -4,7 +4,7 @@
<h2>
{% if page_perms.can_edit and 'edit' not in hide_actions|default:'' %}
<a href="{% url 'wagtailadmin_pages_edit' page.id %}" title="{% trans 'Edit this page' %}">{{ page.title }}</a>
<a href="{% url 'wagtailadmin_pages:edit' page.id %}" title="{% trans 'Edit this page' %}">{{ page.title }}</a>
{% else %}
{{ page.title }}
{% endif %}
@ -15,27 +15,27 @@
<ul class="actions">
{% if page_perms.can_edit and 'edit' not in hide_actions|default:'' %}
<li><a href="{% url 'wagtailadmin_pages_edit' page.id %}" class="button button-small button-secondary" title="{% trans 'Edit this page' %}">{% trans 'Edit' %}</a></li>
<li><a href="{% url 'wagtailadmin_pages:edit' page.id %}" class="button button-small button-secondary" title="{% trans 'Edit this page' %}">{% trans 'Edit' %}</a></li>
{% endif %}
{% if page.has_unpublished_changes and 'view_draft' not in hide_actions|default:'' %}
<li><a href="{% url 'wagtailadmin_pages_view_draft' page.id %}" class="button button-small button-secondary" target="_blank">{% trans 'Draft' %}</a></li>
<li><a href="{% url 'wagtailadmin_pages:view_draft' page.id %}" class="button button-small button-secondary" target="_blank">{% trans 'Draft' %}</a></li>
{% endif %}
{% if page.live and 'view_live' not in hide_actions|default:'' %}
<li><a {% if page.url %}href="{{ page.url }}"{% endif %} class="button button-small button-secondary {% if not page.url %}disabled{% endif %}" target="_blank" {% if not page.url %}title="{% trans 'This page is published but does not exist within a configured Site, so cannot be viewed.' %}"{% endif %}>{% trans 'Live' %}</a></li>
{% endif %}
{% if page_perms.can_move and 'move' not in hide_actions|default:'' %}
<li><a href="{% url 'wagtailadmin_pages_move' page.id %}" class="button button-small button-secondary">{% trans 'Move' %}</a></li>
<li><a href="{% url 'wagtailadmin_pages:move' page.id %}" class="button button-small button-secondary">{% trans 'Move' %}</a></li>
{% endif %}
{% if parent_page_perms.can_add_subpage and 'copy' not in hide_actions|default:'' %}
<li><a href="{% url 'wagtailadmin_pages_copy' page.id %}" class="button button-small button-secondary">{% trans 'Copy' %}</a></li>
<li><a href="{% url 'wagtailadmin_pages:copy' page.id %}" class="button button-small button-secondary">{% trans 'Copy' %}</a></li>
{% endif %}
{% if page_perms.can_delete and 'delete' not in hide_actions|default:'' %}
<li><a href="{% url 'wagtailadmin_pages_delete' page.id %}" class="button button-small button-secondary">{% trans 'Delete' %}</a></li>
<li><a href="{% url 'wagtailadmin_pages:delete' page.id %}" class="button button-small button-secondary">{% trans 'Delete' %}</a></li>
{% endif %}
{% if page_perms.can_unpublish and 'unpublish' not in hide_actions|default:'' %}
<li><a href="{% url 'wagtailadmin_pages_unpublish' page.id %}" class="button button-small button-secondary">{% trans 'Unpublish' %}</a></li>
<li><a href="{% url 'wagtailadmin_pages:unpublish' page.id %}" class="button button-small button-secondary">{% trans 'Unpublish' %}</a></li>
{% endif %}
{% if page_perms.can_add_subpage and 'add_subpage' not in hide_actions|default:'' %}
<li><a href="{% url 'wagtailadmin_pages_add_subpage' page.id %}" class="button button-small button-secondary">{% trans 'Add child page' %}</a></li>
<li><a href="{% url 'wagtailadmin_pages:add_subpage' page.id %}" class="button button-small button-secondary">{% trans 'Add child page' %}</a></li>
{% endif %}
</ul>

View file

@ -6,7 +6,7 @@ Expects a variable 'page', the page instance.
<h2>
{% if page.can_choose %}
<a href="{% url 'wagtailadmin_pages_move_confirm' page_to_move.id page.id %}">{{ page.title }}</a>
<a href="{% url 'wagtailadmin_pages:move_confirm' page_to_move.id page.id %}">{{ page.title }}</a>
{% else %}
{{ page.title }}
{% endif %}

View file

@ -5,7 +5,7 @@
<h2>
{% if parent_page_perms.can_edit and 'edit' not in hide_actions|default:'' %}
<a href="{% url 'wagtailadmin_pages_edit' parent_page.id %}">{{ parent_page.title }}</a>
<a href="{% url 'wagtailadmin_pages:edit' parent_page.id %}">{{ parent_page.title }}</a>
{% else %}
{{ parent_page.title }}
{% endif %}
@ -17,25 +17,25 @@
<ul class="actions">
{% if parent_page_perms.can_edit and 'edit' not in hide_actions|default:'' %}
<li><a href="{% url 'wagtailadmin_pages_edit' parent_page.id %}" class="button button-small button-secondary">{% trans 'Edit' %}</a></li>
<li><a href="{% url 'wagtailadmin_pages:edit' parent_page.id %}" class="button button-small button-secondary">{% trans 'Edit' %}</a></li>
{% endif %}
{% if parent_page.has_unpublished_changes and 'view_draft' not in hide_actions|default:'' %}
<li><a href="{% url 'wagtailadmin_pages_view_draft' parent_page.id %}" class="button button-small button-secondary" target="_blank">{% trans 'Draft' %}</a></li>
<li><a href="{% url 'wagtailadmin_pages:view_draft' parent_page.id %}" class="button button-small button-secondary" target="_blank">{% trans 'Draft' %}</a></li>
{% endif %}
{% if parent_page.live and not parent_page.is_root and 'view_live' not in hide_actions|default:'' %}
<li><a {% if parent_page.url %}href="{{ parent_page.url }}"{% endif %} class="button button-small button-secondary {% if not parent_page.url %}disabled{% endif %}" target="_blank" {% if not parent_page.url %}title="{% trans 'This page is published but does not exist within a configured Site, so cannot be viewed.' %}"{% endif %}>{% trans 'Live' %}</a></li>
{% endif %}
{% if parent_page_perms.can_move and 'move' not in hide_actions|default:'' %}
<li><a href="{% url 'wagtailadmin_pages_move' parent_page.id %}" class="button button-small button-secondary">{% trans 'Move' %}</a></li>
<li><a href="{% url 'wagtailadmin_pages:move' parent_page.id %}" class="button button-small button-secondary">{% trans 'Move' %}</a></li>
{% endif %}
{% if parent_page_perms.can_delete and 'delete' not in hide_actions|default:'' %}
<li><a href="{% url 'wagtailadmin_pages_delete' parent_page.id %}" class="button button-small button-secondary">{% trans 'Delete' %}</a></li>
<li><a href="{% url 'wagtailadmin_pages:delete' parent_page.id %}" class="button button-small button-secondary">{% trans 'Delete' %}</a></li>
{% endif %}
{% if parent_page_perms.can_unpublish and 'unpublish' not in hide_actions|default:'' %}
<li><a href="{% url 'wagtailadmin_pages_unpublish' parent_page.id %}" class="button button-small button-secondary">{% trans 'Unpublish' %}</a></li>
<li><a href="{% url 'wagtailadmin_pages:unpublish' parent_page.id %}" class="button button-small button-secondary">{% trans 'Unpublish' %}</a></li>
{% endif %}
{% if parent_page_perms.can_add_subpage and 'add_subpage' not in hide_actions|default:'' %}
<li><a href="{% url 'wagtailadmin_pages_add_subpage' parent_page.id %}" class="button button-small bicolor icon white icon-plus">{% trans 'Add child page' %}</a></li>
<li><a href="{% url 'wagtailadmin_pages:add_subpage' parent_page.id %}" class="button button-small bicolor icon white icon-plus">{% trans 'Add child page' %}</a></li>
{% endif %}
</ul>

View file

@ -11,6 +11,6 @@
<div id="loading-spinner-wrapper">
<div id="loading-spinner"></div>
</div>
<iframe id="preview-frame" src="{% url 'wagtailadmin_pages_preview_loading' %}"></iframe>
<iframe id="preview-frame" src="{% url 'wagtailadmin_pages:preview_loading' %}"></iframe>
</body>
</html>

View file

@ -5,7 +5,7 @@
{% block extra_js %}
<script>
window.headerSearch = {
url: "{% url 'wagtailadmin_pages_search' %}",
url: "{% url 'wagtailadmin_pages:search' %}",
termInput: "#id_q",
targetOutput: "#page-results"
}
@ -14,7 +14,7 @@
{% block content %}
{% trans "Search" as search_str %}
{% include "wagtailadmin/shared/header.html" with title=search_str search_url="wagtailadmin_pages_search" icon="search" %}
{% include "wagtailadmin/shared/header.html" with title=search_str search_url="wagtailadmin_pages:search" icon="search" %}
<div id="page-results">
{% include "wagtailadmin/pages/search_results.html" %}

View file

@ -12,15 +12,15 @@
<nav class="listing-filter">
<h3 class="filter-title">{% trans "Other searches" %}</h3>
<ul class="filter-options">
<li><a href="{% url 'wagtailimages_index' %}?q={{ query_string|urlencode }}" class="icon icon-image">{% trans "Images" %}</a></li>
<li><a href="{% url 'wagtaildocs_index' %}?q={{ query_string|urlencode }}" class="icon icon-doc-full-inverse">{% trans "Documents" %}</a></li>
<li><a href="{% url 'wagtailusers_users_index' %}?q={{ query_string|urlencode }}" class="icon icon-user">{% trans "Users" %}</a></li>
<li><a href="{% url 'wagtailimages:index' %}?q={{ query_string|urlencode }}" class="icon icon-image">{% trans "Images" %}</a></li>
<li><a href="{% url 'wagtaildocs:index' %}?q={{ query_string|urlencode }}" class="icon icon-doc-full-inverse">{% trans "Documents" %}</a></li>
<li><a href="{% url 'wagtailusers_users:index' %}?q={{ query_string|urlencode }}" class="icon icon-user">{% trans "Users" %}</a></li>
</ul>
</nav>
{% include "wagtailadmin/pages/listing/_list_explore.html" with show_parent=1 allow_navigation=0 %}
{% url 'wagtailadmin_pages_search' as pagination_base_url %}
{% url 'wagtailadmin_pages:search' as pagination_base_url %}
{% include "wagtailadmin/pages/listing/_pagination.html" with page=pages base_url=pagination_base_url query_params=pagination_query_params only %}
{% else %}
{% if query_string %}

View file

@ -3,7 +3,7 @@
{% if pages %}
{% include "wagtailadmin/pages/listing/_list_explore.html" with show_parent=1 allow_navigation=0 %}
{% url 'wagtailadmin_pages_type_use' app_name content_type.model as pagination_base_url %}
{% url 'wagtailadmin_pages:type_use' app_name content_type.model as pagination_base_url %}
{% include "wagtailadmin/pages/listing/_pagination.html" with page=pages base_url=pagination_base_url only %}
{% else %}
<p>{% trans 'No pages use' %}<em>"{{ page_class.get_verbose_name }}"</em>.</p>

View file

@ -2,5 +2,5 @@
{% load i18n %}
{% block item_content %}
<a href="{% url 'wagtailadmin_pages_add_subpage' self.page.id %}" target="_parent" class="action icon icon-plus" title="{% trans 'Add a child page' %}">{% trans 'Add' %}</a>
<a href="{% url 'wagtailadmin_pages:add_subpage' self.page.id %}" target="_parent" class="action icon icon-plus" title="{% trans 'Add a child page' %}">{% trans 'Add' %}</a>
{% endblock %}

View file

@ -2,7 +2,7 @@
{% load i18n %}
{% block item_content %}
<form action="{% url 'wagtailadmin_pages_approve_moderation' self.revision.id %}" target="_parent" method="post">
<form action="{% url 'wagtailadmin_pages:approve_moderation' self.revision.id %}" target="_parent" method="post">
{% csrf_token %}
<div class="action icon icon-tick">
<input type="submit" value="{% trans 'Approve' %}" />

View file

@ -2,5 +2,5 @@
{% load i18n %}
{% block item_content %}
<a href="{% url 'wagtailadmin_pages_edit' self.page.id %}" target="_parent" class="action icon icon-edit">{% trans 'Edit' %}</a>
<a href="{% url 'wagtailadmin_pages:edit' self.page.id %}" target="_parent" class="action icon icon-edit">{% trans 'Edit' %}</a>
{% endblock %}

View file

@ -2,7 +2,7 @@
{% load i18n %}
{% block item_content %}
<form action="{% url 'wagtailadmin_pages_reject_moderation' self.revision.id %}" target="_parent" method="post">
<form action="{% url 'wagtailadmin_pages:reject_moderation' self.revision.id %}" target="_parent" method="post">
{% csrf_token %}
<div class="action icon icon-cross">
<input type="submit" value="{% trans 'Reject' %}" />

View file

@ -4,4 +4,4 @@
<span class="title">{{ page.title }}</span>
{% endblock %}
{% block edit_chosen_item_url %}{% if page %}{% url 'wagtailadmin_pages_edit' page.id %}{% endif %}{% endblock %}
{% block edit_chosen_item_url %}{% if page %}{% url 'wagtailadmin_pages:edit' page.id %}{% endif %}{% endblock %}

View file

@ -135,7 +135,7 @@ class TestPageCreation(TestCase, WagtailTestUtils):
self.user = self.login()
def test_add_subpage(self):
response = self.client.get(reverse('wagtailadmin_pages_add_subpage', args=(self.root_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:add_subpage', args=(self.root_page.id, )))
self.assertEqual(response.status_code, 200)
def test_add_subpage_bad_permissions(self):
@ -147,17 +147,17 @@ class TestPageCreation(TestCase, WagtailTestUtils):
self.user.save()
# Get add subpage page
response = self.client.get(reverse('wagtailadmin_pages_add_subpage', args=(self.root_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:add_subpage', args=(self.root_page.id, )))
# Check that the user recieved a 403 response
self.assertEqual(response.status_code, 403)
def test_add_subpage_nonexistantparent(self):
response = self.client.get(reverse('wagtailadmin_pages_add_subpage', args=(100000, )))
response = self.client.get(reverse('wagtailadmin_pages:add_subpage', args=(100000, )))
self.assertEqual(response.status_code, 404)
def test_create_simplepage(self):
response = self.client.get(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id)))
response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)))
self.assertEqual(response.status_code, 200)
self.assertContains(response, '<a href="#content" class="active">Content</a>')
self.assertContains(response, '<a href="#promote" class="">Promote</a>')
@ -166,7 +166,7 @@ class TestPageCreation(TestCase, WagtailTestUtils):
"""
Test that the Promote tab is not rendered for page classes that define it as empty
"""
response = self.client.get(reverse('wagtailadmin_pages_create', args=('tests', 'standardindex', self.root_page.id)))
response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'standardindex', self.root_page.id)))
self.assertEqual(response.status_code, 200)
self.assertContains(response, '<a href="#content" class="active">Content</a>')
self.assertNotContains(response, '<a href="#promote" class="">Promote</a>')
@ -175,7 +175,7 @@ class TestPageCreation(TestCase, WagtailTestUtils):
"""
Test that custom edit handlers are rendered
"""
response = self.client.get(reverse('wagtailadmin_pages_create', args=('tests', 'standardchild', self.root_page.id)))
response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'standardchild', self.root_page.id)))
self.assertEqual(response.status_code, 200)
self.assertContains(response, '<a href="#content" class="active">Content</a>')
self.assertContains(response, '<a href="#promote" class="">Promote</a>')
@ -190,7 +190,7 @@ class TestPageCreation(TestCase, WagtailTestUtils):
self.user.save()
# Get page
response = self.client.get(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id, )))
# Check that the user recieved a 403 response
self.assertEqual(response.status_code, 403)
@ -201,13 +201,13 @@ class TestPageCreation(TestCase, WagtailTestUtils):
'content': "Some content",
'slug': 'hello-world',
}
response = self.client.post(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id)), post_data)
response = self.client.post(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data)
# Find the page and check it
page = Page.objects.get(path__startswith=self.root_page.path, slug='hello-world').specific
# Should be redirected to edit page
self.assertRedirects(response, reverse('wagtailadmin_pages_edit', args=(page.id, )))
self.assertRedirects(response, reverse('wagtailadmin_pages:edit', args=(page.id, )))
self.assertEqual(page.title, post_data['title'])
self.assertIsInstance(page, SimplePage)
@ -227,7 +227,7 @@ class TestPageCreation(TestCase, WagtailTestUtils):
'go_live_at': submittable_timestamp(go_live_at),
'expire_at': submittable_timestamp(expire_at),
}
response = self.client.post(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id)), post_data)
response = self.client.post(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data)
# Should be redirected to explorer page
self.assertEqual(response.status_code, 302)
@ -250,7 +250,7 @@ class TestPageCreation(TestCase, WagtailTestUtils):
'go_live_at': submittable_timestamp(timezone.now() + timedelta(days=2)),
'expire_at': submittable_timestamp(timezone.now() + timedelta(days=1)),
}
response = self.client.post(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id)), post_data)
response = self.client.post(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data)
self.assertEqual(response.status_code, 200)
@ -265,7 +265,7 @@ class TestPageCreation(TestCase, WagtailTestUtils):
'slug': 'hello-world',
'expire_at': submittable_timestamp(timezone.now() + timedelta(days=-1)),
}
response = self.client.post(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id)), post_data)
response = self.client.post(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data)
self.assertEqual(response.status_code, 200)
@ -284,7 +284,7 @@ class TestPageCreation(TestCase, WagtailTestUtils):
'slug': 'hello-world',
'action-publish': "Publish",
}
response = self.client.post(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id)), post_data)
response = self.client.post(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data)
# Find the page and check it
page = Page.objects.get(path__startswith=self.root_page.path, slug='hello-world').specific
@ -319,7 +319,7 @@ class TestPageCreation(TestCase, WagtailTestUtils):
'go_live_at': submittable_timestamp(go_live_at),
'expire_at': submittable_timestamp(expire_at),
}
response = self.client.post(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id)), post_data)
response = self.client.post(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data)
# Should be redirected to explorer page
self.assertEqual(response.status_code, 302)
@ -348,7 +348,7 @@ class TestPageCreation(TestCase, WagtailTestUtils):
'slug': 'hello-world',
'action-submit': "Submit",
}
response = self.client.post(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id)), post_data)
response = self.client.post(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data)
# Find the page and check it
page = Page.objects.get(path__startswith=self.root_page.path, slug='hello-world').specific
@ -385,7 +385,7 @@ class TestPageCreation(TestCase, WagtailTestUtils):
'slug': 'hello-world',
'action-publish': "Publish",
}
response = self.client.post(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id)), post_data)
response = self.client.post(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data)
# Should not be redirected (as the save should fail)
self.assertEqual(response.status_code, 200)
@ -394,11 +394,11 @@ class TestPageCreation(TestCase, WagtailTestUtils):
self.assertFormError(response, 'form', 'slug', "This slug is already in use")
def test_create_nonexistantparent(self):
response = self.client.get(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', 100000)))
response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', 100000)))
self.assertEqual(response.status_code, 404)
def test_create_nonpagetype(self):
response = self.client.get(reverse('wagtailadmin_pages_create', args=('wagtailimages', 'image', self.root_page.id)))
response = self.client.get(reverse('wagtailadmin_pages:add', args=('wagtailimages', 'image', self.root_page.id)))
self.assertEqual(response.status_code, 404)
def test_preview_on_create(self):
@ -408,7 +408,7 @@ class TestPageCreation(TestCase, WagtailTestUtils):
'slug': 'hello-world',
'action-submit': "Submit",
}
response = self.client.post(reverse('wagtailadmin_pages_preview_on_create', args=('tests', 'simplepage', self.root_page.id)), post_data)
response = self.client.post(reverse('wagtailadmin_pages:preview_on_add', args=('tests', 'simplepage', self.root_page.id)), post_data)
# Check the response
self.assertEqual(response.status_code, 200)
@ -428,7 +428,7 @@ class TestPageCreation(TestCase, WagtailTestUtils):
'action-submit': "Submit",
'seo_title': '\t',
}
response = self.client.post(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id)), post_data)
response = self.client.post(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data)
# Check that a form error was raised
self.assertFormError(response, 'form', 'title', "Value cannot be entirely whitespace characters")
@ -444,7 +444,7 @@ class TestPageCreation(TestCase, WagtailTestUtils):
'hello-world-hello-world-hello-world-hello-world-hello-world-hello-world',
'action-submit': "Submit",
}
response = self.client.post(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id)), post_data)
response = self.client.post(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)), post_data)
# Check that a form error was raised
self.assertEqual(response.status_code, 200)
@ -476,7 +476,7 @@ class TestPageEdit(TestCase, WagtailTestUtils):
def test_page_edit(self):
# Tests that the edit page loads
response = self.client.get(reverse('wagtailadmin_pages_edit', args=(self.event_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:edit', args=(self.event_page.id, )))
self.assertEqual(response.status_code, 200)
def test_page_edit_bad_permissions(self):
@ -488,7 +488,7 @@ class TestPageEdit(TestCase, WagtailTestUtils):
self.user.save()
# Get edit page
response = self.client.get(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:edit', args=(self.child_page.id, )))
# Check that the user recieved a 403 response
self.assertEqual(response.status_code, 403)
@ -500,10 +500,10 @@ class TestPageEdit(TestCase, WagtailTestUtils):
'content': "Some content",
'slug': 'hello-world',
}
response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:edit', args=(self.child_page.id, )), post_data)
# Should be redirected to edit page
self.assertRedirects(response, reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )))
self.assertRedirects(response, reverse('wagtailadmin_pages:edit', args=(self.child_page.id, )))
# The page should have "has_unpublished_changes" flag set
child_page_new = SimplePage.objects.get(id=self.child_page.id)
@ -522,7 +522,7 @@ class TestPageEdit(TestCase, WagtailTestUtils):
'content': "Some content",
'slug': 'hello-world',
}
response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:edit', args=(self.child_page.id, )), post_data)
# Shouldn't be redirected
self.assertContains(response, "The page could not be saved as it is locked")
@ -543,7 +543,7 @@ class TestPageEdit(TestCase, WagtailTestUtils):
'go_live_at': submittable_timestamp(go_live_at),
'expire_at': submittable_timestamp(expire_at),
}
response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:edit', args=(self.child_page.id, )), post_data)
# Should be redirected to explorer page
self.assertEqual(response.status_code, 302)
@ -568,7 +568,7 @@ class TestPageEdit(TestCase, WagtailTestUtils):
'go_live_at': submittable_timestamp(timezone.now() + timedelta(days=2)),
'expire_at': submittable_timestamp(timezone.now() + timedelta(days=1)),
}
response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:edit', args=(self.child_page.id, )), post_data)
self.assertEqual(response.status_code, 200)
@ -583,7 +583,7 @@ class TestPageEdit(TestCase, WagtailTestUtils):
'slug': 'hello-world',
'expire_at': submittable_timestamp(timezone.now() + timedelta(days=-1)),
}
response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:edit', args=(self.child_page.id, )), post_data)
self.assertEqual(response.status_code, 200)
@ -610,7 +610,7 @@ class TestPageEdit(TestCase, WagtailTestUtils):
'slug': 'hello-world',
'action-publish': "Publish",
}
response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:edit', args=(self.child_page.id, )), post_data)
# Should be redirected to explorer
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
@ -644,7 +644,7 @@ class TestPageEdit(TestCase, WagtailTestUtils):
'go_live_at': submittable_timestamp(go_live_at),
'expire_at': submittable_timestamp(expire_at),
}
response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:edit', args=(self.child_page.id, )), post_data)
# Should be redirected to explorer page
self.assertEqual(response.status_code, 302)
@ -672,7 +672,7 @@ class TestPageEdit(TestCase, WagtailTestUtils):
'go_live_at': submittable_timestamp(go_live_at),
'expire_at': submittable_timestamp(expire_at),
}
response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:edit', args=(self.child_page.id, )), post_data)
# Should be redirected to edit page
self.assertEqual(response.status_code, 302)
@ -694,7 +694,7 @@ class TestPageEdit(TestCase, WagtailTestUtils):
'action-publish': "Publish",
'go_live_at': "",
}
response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:edit', args=(self.child_page.id, )), post_data)
# Should be redirected to edit page
self.assertEqual(response.status_code, 302)
@ -718,7 +718,7 @@ class TestPageEdit(TestCase, WagtailTestUtils):
'slug': 'hello-world',
'action-submit': "Submit",
}
response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:edit', args=(self.child_page.id, )), post_data)
# Should be redirected to explorer
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
@ -750,7 +750,7 @@ class TestPageEdit(TestCase, WagtailTestUtils):
'slug': 'hello-world',
'action-submit': "Submit",
}
response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:edit', args=(self.child_page.id, )), post_data)
# Should not be redirected (as the save should fail)
self.assertEqual(response.status_code, 200)
@ -765,7 +765,7 @@ class TestPageEdit(TestCase, WagtailTestUtils):
'slug': 'hello-world',
'action-submit': "Submit",
}
response = self.client.post(reverse('wagtailadmin_pages_preview_on_edit', args=(self.child_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:preview_on_edit', args=(self.child_page.id, )), post_data)
# Check the response
self.assertEqual(response.status_code, 200)
@ -798,7 +798,7 @@ class TestPageEditReordering(TestCase, WagtailTestUtils):
self.assertEqual(order, expected_order)
def test_order(self):
response = self.client.get(reverse('wagtailadmin_pages_edit', args=(self.event_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:edit', args=(self.event_page.id, )))
self.assertEqual(response.status_code, 200)
self.check_order(response, ['1234567', '7654321', 'abcdefg'])
@ -834,13 +834,13 @@ class TestPageEditReordering(TestCase, WagtailTestUtils):
'carousel_items-2-caption': self.event_page.carousel_items.all()[2].caption,
'carousel_items-2-ORDER': 1,
}
response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.event_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:edit', args=(self.event_page.id, )), post_data)
# Should be redirected back to same page
self.assertRedirects(response, reverse('wagtailadmin_pages_edit', args=(self.event_page.id, )))
self.assertRedirects(response, reverse('wagtailadmin_pages:edit', args=(self.event_page.id, )))
# Check order
response = self.client.get(reverse('wagtailadmin_pages_edit', args=(self.event_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:edit', args=(self.event_page.id, )))
self.assertEqual(response.status_code, 200)
self.check_order(response, ['abcdefg', '1234567', '7654321'])
@ -876,7 +876,7 @@ class TestPageEditReordering(TestCase, WagtailTestUtils):
'carousel_items-2-caption': self.event_page.carousel_items.all()[2].caption,
'carousel_items-2-ORDER': 1,
}
response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.event_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:edit', args=(self.event_page.id, )), post_data)
self.assertEqual(response.status_code, 200)
self.check_order(response, ['abcdefg', '1234567', '7654321'])
@ -903,7 +903,7 @@ class TestPageDelete(TestCase, WagtailTestUtils):
self.user = self.login()
def test_page_delete(self):
response = self.client.get(reverse('wagtailadmin_pages_delete', args=(self.child_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:delete', args=(self.child_page.id, )))
self.assertEqual(response.status_code, 200)
# deletion should not actually happen on GET
self.assertTrue(SimplePage.objects.filter(id=self.child_page.id).exists())
@ -917,7 +917,7 @@ class TestPageDelete(TestCase, WagtailTestUtils):
self.user.save()
# Get delete page
response = self.client.get(reverse('wagtailadmin_pages_delete', args=(self.child_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:delete', args=(self.child_page.id, )))
# Check that the user recieved a 403 response
self.assertEqual(response.status_code, 403)
@ -931,7 +931,7 @@ class TestPageDelete(TestCase, WagtailTestUtils):
page_unpublished.connect(mock_handler)
# Post
response = self.client.post(reverse('wagtailadmin_pages_delete', args=(self.child_page.id, )))
response = self.client.post(reverse('wagtailadmin_pages:delete', args=(self.child_page.id, )))
# Should be redirected to explorer page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
@ -963,7 +963,7 @@ class TestPageDelete(TestCase, WagtailTestUtils):
page_unpublished.connect(mock_handler)
# Post
response = self.client.post(reverse('wagtailadmin_pages_delete', args=(self.child_page.id, )))
response = self.client.post(reverse('wagtailadmin_pages:delete', args=(self.child_page.id, )))
# Should be redirected to explorer page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
@ -997,7 +997,7 @@ class TestPageDelete(TestCase, WagtailTestUtils):
post_delete.connect(post_delete_handler)
# Post
response = self.client.post(reverse('wagtailadmin_pages_delete', args=(self.child_index.id, )))
response = self.client.post(reverse('wagtailadmin_pages:delete', args=(self.child_index.id, )))
# Should be redirected to explorer page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
@ -1030,7 +1030,7 @@ class TestPageSearch(TestCase, WagtailTestUtils):
self.login()
def get(self, params=None, **extra):
return self.client.get(reverse('wagtailadmin_pages_search'), params or {}, **extra)
return self.client.get(reverse('wagtailadmin_pages:search'), params or {}, **extra)
def test_view(self):
response = self.get()
@ -1091,7 +1091,7 @@ class TestPageMove(TestCase, WagtailTestUtils):
self.user = self.login()
def test_page_move(self):
response = self.client.get(reverse('wagtailadmin_pages_move', args=(self.test_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:move', args=(self.test_page.id, )))
self.assertEqual(response.status_code, 200)
def test_page_move_bad_permissions(self):
@ -1103,17 +1103,17 @@ class TestPageMove(TestCase, WagtailTestUtils):
self.user.save()
# Get move page
response = self.client.get(reverse('wagtailadmin_pages_move', args=(self.test_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:move', args=(self.test_page.id, )))
# Check that the user recieved a 403 response
self.assertEqual(response.status_code, 403)
def test_page_move_confirm(self):
response = self.client.get(reverse('wagtailadmin_pages_move_confirm', args=(self.test_page.id, self.section_b.id)))
response = self.client.get(reverse('wagtailadmin_pages:move_confirm', args=(self.test_page.id, self.section_b.id)))
self.assertEqual(response.status_code, 200)
def test_page_set_page_position(self):
response = self.client.get(reverse('wagtailadmin_pages_set_page_position', args=(self.test_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:set_page_position', args=(self.test_page.id, )))
self.assertEqual(response.status_code, 200)
@ -1149,7 +1149,7 @@ class TestPageCopy(TestCase, WagtailTestUtils):
self.user = self.login()
def test_page_copy(self):
response = self.client.get(reverse('wagtailadmin_pages_copy', args=(self.test_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )))
# Check response
self.assertEqual(response.status_code, 200)
@ -1177,7 +1177,7 @@ class TestPageCopy(TestCase, WagtailTestUtils):
'new_parent_page': str(self.test_page.id),
'copy_subpages': False,
}
response = self.client.post(reverse('wagtailadmin_pages_copy', args=(self.test_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )), post_data)
# Check that the user received a 403 response
self.assertEqual(response.status_code, 403)
@ -1190,7 +1190,7 @@ class TestPageCopy(TestCase, WagtailTestUtils):
'copy_subpages': False,
'publish_copies': False,
}
response = self.client.post(reverse('wagtailadmin_pages_copy', args=(self.test_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )), post_data)
# Check that the user was redirected to the parents explore page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
@ -1222,7 +1222,7 @@ class TestPageCopy(TestCase, WagtailTestUtils):
'copy_subpages': True,
'publish_copies': False,
}
response = self.client.post(reverse('wagtailadmin_pages_copy', args=(self.test_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )), post_data)
# Check that the user was redirected to the parents explore page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
@ -1266,7 +1266,7 @@ class TestPageCopy(TestCase, WagtailTestUtils):
'copy_subpages': True,
'publish_copies': True,
}
response = self.client.post(reverse('wagtailadmin_pages_copy', args=(self.test_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )), post_data)
# Check that the user was redirected to the parents explore page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
@ -1310,7 +1310,7 @@ class TestPageCopy(TestCase, WagtailTestUtils):
'copy_subpages': False,
'publish_copies': False,
}
response = self.client.post(reverse('wagtailadmin_pages_copy', args=(self.test_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )), post_data)
# Check that the user was redirected to the new parents explore page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.test_child_page.id, )))
@ -1331,7 +1331,7 @@ class TestPageCopy(TestCase, WagtailTestUtils):
'new_parent_page': str(self.root_page.id),
'copy_subpages': False,
}
response = self.client.post(reverse('wagtailadmin_pages_copy', args=(self.test_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )), post_data)
# Should not be redirected (as the save should fail)
self.assertEqual(response.status_code, 200)
@ -1349,7 +1349,7 @@ class TestPageCopy(TestCase, WagtailTestUtils):
'new_parent_page': str(self.test_child_page.id),
'copy_subpages': False,
}
response = self.client.post(reverse('wagtailadmin_pages_copy', args=(self.test_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )), post_data)
# Check that the user was redirected to the parents explore page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.test_child_page.id, )))
@ -1362,7 +1362,7 @@ class TestPageCopy(TestCase, WagtailTestUtils):
'new_parent_page': str(self.root_page.id),
'copy_subpages': False,
}
response = self.client.post(reverse('wagtailadmin_pages_copy', args=(self.test_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )), post_data)
# Should not be redirected (as the save should fail)
self.assertEqual(response.status_code, 200)
@ -1379,7 +1379,7 @@ class TestPageCopy(TestCase, WagtailTestUtils):
self.user.save()
# Get copy page
response = self.client.get(reverse('wagtailadmin_pages_copy', args=(self.test_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )))
# The user should have access to the copy page
self.assertEqual(response.status_code, 200)
@ -1406,7 +1406,7 @@ class TestPageCopy(TestCase, WagtailTestUtils):
'copy_subpages': True,
'publish_copies': True,
}
response = self.client.post(reverse('wagtailadmin_pages_copy', args=(self.test_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:copy', args=(self.test_page.id, )), post_data)
# Check that the user was redirected to the parents explore page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
@ -1458,7 +1458,7 @@ class TestPageUnpublish(TestCase, WagtailTestUtils):
This tests that the unpublish view responds with an unpublish confirm page
"""
# Get unpublish page
response = self.client.get(reverse('wagtailadmin_pages_unpublish', args=(self.page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:unpublish', args=(self.page.id, )))
# Check that the user recieved an unpublish confirm page
self.assertEqual(response.status_code, 200)
@ -1469,7 +1469,7 @@ class TestPageUnpublish(TestCase, WagtailTestUtils):
This tests that the unpublish view returns an error if the page id is invalid
"""
# Get unpublish page
response = self.client.get(reverse('wagtailadmin_pages_unpublish', args=(12345, )))
response = self.client.get(reverse('wagtailadmin_pages:unpublish', args=(12345, )))
# Check that the user recieved a 404 response
self.assertEqual(response.status_code, 404)
@ -1486,7 +1486,7 @@ class TestPageUnpublish(TestCase, WagtailTestUtils):
self.user.save()
# Get unpublish page
response = self.client.get(reverse('wagtailadmin_pages_unpublish', args=(self.page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:unpublish', args=(self.page.id, )))
# Check that the user recieved a 403 response
self.assertEqual(response.status_code, 403)
@ -1500,7 +1500,7 @@ class TestPageUnpublish(TestCase, WagtailTestUtils):
page_unpublished.connect(mock_handler)
# Post to the unpublish page
response = self.client.post(reverse('wagtailadmin_pages_unpublish', args=(self.page.id, )))
response = self.client.post(reverse('wagtailadmin_pages:unpublish', args=(self.page.id, )))
# Should be redirected to explorer page
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
@ -1549,7 +1549,7 @@ class TestApproveRejectModeration(TestCase, WagtailTestUtils):
page_published.connect(mock_handler)
# Post
response = self.client.post(reverse('wagtailadmin_pages_approve_moderation', args=(self.revision.id, )))
response = self.client.post(reverse('wagtailadmin_pages:approve_moderation', args=(self.revision.id, )))
# Check that the user was redirected to the dashboard
self.assertRedirects(response, reverse('wagtailadmin_home'))
@ -1572,7 +1572,7 @@ class TestApproveRejectModeration(TestCase, WagtailTestUtils):
self.page.title = "Goodbye world!"
self.page.save_revision(user=self.submitter, submitted_for_moderation=False)
response = self.client.post(reverse('wagtailadmin_pages_approve_moderation', args=(self.revision.id, )))
response = self.client.post(reverse('wagtailadmin_pages:approve_moderation', args=(self.revision.id, )))
# Check that the user was redirected to the dashboard
self.assertRedirects(response, reverse('wagtailadmin_home'))
@ -1590,7 +1590,7 @@ class TestApproveRejectModeration(TestCase, WagtailTestUtils):
This tests that the approve moderation view handles invalid revision ids correctly
"""
# Post
response = self.client.post(reverse('wagtailadmin_pages_approve_moderation', args=(12345, )))
response = self.client.post(reverse('wagtailadmin_pages:approve_moderation', args=(12345, )))
# Check that the user recieved a 404 response
self.assertEqual(response.status_code, 404)
@ -1607,7 +1607,7 @@ class TestApproveRejectModeration(TestCase, WagtailTestUtils):
self.user.save()
# Post
response = self.client.post(reverse('wagtailadmin_pages_approve_moderation', args=(self.revision.id, )))
response = self.client.post(reverse('wagtailadmin_pages:approve_moderation', args=(self.revision.id, )))
# Check that the user recieved a 403 response
self.assertEqual(response.status_code, 403)
@ -1617,7 +1617,7 @@ class TestApproveRejectModeration(TestCase, WagtailTestUtils):
This posts to the reject moderation view and checks that the page was rejected
"""
# Post
response = self.client.post(reverse('wagtailadmin_pages_reject_moderation', args=(self.revision.id, )))
response = self.client.post(reverse('wagtailadmin_pages:reject_moderation', args=(self.revision.id, )))
# Check that the user was redirected to the dashboard
self.assertRedirects(response, reverse('wagtailadmin_home'))
@ -1633,7 +1633,7 @@ class TestApproveRejectModeration(TestCase, WagtailTestUtils):
This tests that the reject moderation view handles invalid revision ids correctly
"""
# Post
response = self.client.post(reverse('wagtailadmin_pages_reject_moderation', args=(12345, )))
response = self.client.post(reverse('wagtailadmin_pages:reject_moderation', args=(12345, )))
# Check that the user recieved a 404 response
self.assertEqual(response.status_code, 404)
@ -1650,13 +1650,13 @@ class TestApproveRejectModeration(TestCase, WagtailTestUtils):
self.user.save()
# Post
response = self.client.post(reverse('wagtailadmin_pages_reject_moderation', args=(self.revision.id, )))
response = self.client.post(reverse('wagtailadmin_pages:reject_moderation', args=(self.revision.id, )))
# Check that the user recieved a 403 response
self.assertEqual(response.status_code, 403)
def test_preview_for_moderation(self):
response = self.client.get(reverse('wagtailadmin_pages_preview_for_moderation', args=(self.revision.id, )))
response = self.client.get(reverse('wagtailadmin_pages:preview_for_moderation', args=(self.revision.id, )))
# Check response
self.assertEqual(response.status_code, 200)
@ -1672,7 +1672,7 @@ class TestContentTypeUse(TestCase, WagtailTestUtils):
def test_content_type_use(self):
# Get use of event page
response = self.client.get(reverse('wagtailadmin_pages_type_use', args=('tests', 'eventpage')))
response = self.client.get(reverse('wagtailadmin_pages:type_use', args=('tests', 'eventpage')))
# Check response
self.assertEqual(response.status_code, 200)
@ -1713,7 +1713,7 @@ class TestSubpageBusinessRules(TestCase, WagtailTestUtils):
self.login()
def test_standard_subpage(self):
add_subpage_url = reverse('wagtailadmin_pages_add_subpage', args=(self.standard_index.id, ))
add_subpage_url = reverse('wagtailadmin_pages:add_subpage', args=(self.standard_index.id, ))
# explorer should contain a link to 'add child page'
response = self.client.get(reverse('wagtailadmin_explore', args=(self.standard_index.id, )))
@ -1730,7 +1730,7 @@ class TestSubpageBusinessRules(TestCase, WagtailTestUtils):
self.assertNotContains(response, BusinessChild.get_verbose_name())
def test_business_subpage(self):
add_subpage_url = reverse('wagtailadmin_pages_add_subpage', args=(self.business_index.id, ))
add_subpage_url = reverse('wagtailadmin_pages:add_subpage', args=(self.business_index.id, ))
# explorer should contain a link to 'add child page'
response = self.client.get(reverse('wagtailadmin_explore', args=(self.business_index.id, )))
@ -1746,7 +1746,7 @@ class TestSubpageBusinessRules(TestCase, WagtailTestUtils):
self.assertContains(response, BusinessChild.get_verbose_name())
def test_business_child_subpage(self):
add_subpage_url = reverse('wagtailadmin_pages_add_subpage', args=(self.business_child.id, ))
add_subpage_url = reverse('wagtailadmin_pages:add_subpage', args=(self.business_child.id, ))
# explorer should not contain a link to 'add child page', as this page doesn't accept subpages
response = self.client.get(reverse('wagtailadmin_explore', args=(self.business_child.id, )))
@ -1754,30 +1754,30 @@ class TestSubpageBusinessRules(TestCase, WagtailTestUtils):
self.assertNotContains(response, add_subpage_url)
# this also means that fetching add_subpage is blocked at the permission-check level
response = self.client.get(reverse('wagtailadmin_pages_add_subpage', args=(self.business_child.id, )))
response = self.client.get(reverse('wagtailadmin_pages:add_subpage', args=(self.business_child.id, )))
self.assertEqual(response.status_code, 403)
def test_cannot_add_invalid_subpage_type(self):
# cannot add StandardChild as a child of BusinessIndex, as StandardChild is not present in subpage_types
response = self.client.get(reverse('wagtailadmin_pages_create', args=('tests', 'standardchild', self.business_index.id)))
response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'standardchild', self.business_index.id)))
self.assertEqual(response.status_code, 403)
# likewise for BusinessChild which has an empty subpage_types list
response = self.client.get(reverse('wagtailadmin_pages_create', args=('tests', 'standardchild', self.business_child.id)))
response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'standardchild', self.business_child.id)))
self.assertEqual(response.status_code, 403)
# cannot add BusinessChild to StandardIndex, as BusinessChild restricts is parent page types
response = self.client.get(reverse('wagtailadmin_pages_create', args=('tests', 'businesschild', self.standard_index.id)))
response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'businesschild', self.standard_index.id)))
self.assertEqual(response.status_code, 403)
# but we can add a BusinessChild to BusinessIndex
response = self.client.get(reverse('wagtailadmin_pages_create', args=('tests', 'businesschild', self.business_index.id)))
response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'businesschild', self.business_index.id)))
self.assertEqual(response.status_code, 200)
def test_not_prompted_for_page_type_when_only_one_choice(self):
response = self.client.get(reverse('wagtailadmin_pages_add_subpage', args=(self.business_subindex.id, )))
response = self.client.get(reverse('wagtailadmin_pages:add_subpage', args=(self.business_subindex.id, )))
# BusinessChild is the only valid subpage type of BusinessSubIndex, so redirect straight there
self.assertRedirects(response, reverse('wagtailadmin_pages_create', args=('tests', 'businesschild', self.business_subindex.id)))
self.assertRedirects(response, reverse('wagtailadmin_pages:add', args=('tests', 'businesschild', self.business_subindex.id)))
class TestNotificationPreferences(TestCase, WagtailTestUtils):
@ -1817,7 +1817,7 @@ class TestNotificationPreferences(TestCase, WagtailTestUtils):
}
def submit(self):
return self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), self.post_data)
return self.client.post(reverse('wagtailadmin_pages:edit', args=(self.child_page.id, )), self.post_data)
def silent_submit(self):
"""
@ -1827,10 +1827,10 @@ class TestNotificationPreferences(TestCase, WagtailTestUtils):
self.revision = self.child_page.get_latest_revision()
def approve(self):
return self.client.post(reverse('wagtailadmin_pages_approve_moderation', args=(self.revision.id, )))
return self.client.post(reverse('wagtailadmin_pages:approve_moderation', args=(self.revision.id, )))
def reject(self):
return self.client.post(reverse('wagtailadmin_pages_reject_moderation', args=(self.revision.id, )))
return self.client.post(reverse('wagtailadmin_pages:reject_moderation', args=(self.revision.id, )))
def test_vanilla_profile(self):
# Check that the vanilla profile has rejected notifications on
@ -1927,7 +1927,7 @@ class TestLocking(TestCase, WagtailTestUtils):
self.root_page.add_child(instance=self.child_page)
def test_lock_post(self):
response = self.client.post(reverse('wagtailadmin_pages_lock', args=(self.child_page.id, )))
response = self.client.post(reverse('wagtailadmin_pages:lock', args=(self.child_page.id, )))
# Check response
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
@ -1936,7 +1936,7 @@ class TestLocking(TestCase, WagtailTestUtils):
self.assertTrue(Page.objects.get(id=self.child_page.id).locked)
def test_lock_get(self):
response = self.client.get(reverse('wagtailadmin_pages_lock', args=(self.child_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:lock', args=(self.child_page.id, )))
# Check response
self.assertEqual(response.status_code, 405)
@ -1949,7 +1949,7 @@ class TestLocking(TestCase, WagtailTestUtils):
self.child_page.locked = True
self.child_page.save()
response = self.client.post(reverse('wagtailadmin_pages_lock', args=(self.child_page.id, )))
response = self.client.post(reverse('wagtailadmin_pages:lock', args=(self.child_page.id, )))
# Check response
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
@ -1958,18 +1958,18 @@ class TestLocking(TestCase, WagtailTestUtils):
self.assertTrue(Page.objects.get(id=self.child_page.id).locked)
def test_lock_post_with_good_redirect(self):
response = self.client.post(reverse('wagtailadmin_pages_lock', args=(self.child_page.id, )), {
'next': reverse('wagtailadmin_pages_edit', args=(self.child_page.id, ))
response = self.client.post(reverse('wagtailadmin_pages:lock', args=(self.child_page.id, )), {
'next': reverse('wagtailadmin_pages:edit', args=(self.child_page.id, ))
})
# Check response
self.assertRedirects(response, reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )))
self.assertRedirects(response, reverse('wagtailadmin_pages:edit', args=(self.child_page.id, )))
# Check that the page is locked
self.assertTrue(Page.objects.get(id=self.child_page.id).locked)
def test_lock_post_with_bad_redirect(self):
response = self.client.post(reverse('wagtailadmin_pages_lock', args=(self.child_page.id, )), {
response = self.client.post(reverse('wagtailadmin_pages:lock', args=(self.child_page.id, )), {
'next': 'http://www.google.co.uk'
})
@ -1980,7 +1980,7 @@ class TestLocking(TestCase, WagtailTestUtils):
self.assertTrue(Page.objects.get(id=self.child_page.id).locked)
def test_lock_post_bad_page(self):
response = self.client.post(reverse('wagtailadmin_pages_lock', args=(9999, )))
response = self.client.post(reverse('wagtailadmin_pages:lock', args=(9999, )))
# Check response
self.assertEqual(response.status_code, 404)
@ -1996,7 +1996,7 @@ class TestLocking(TestCase, WagtailTestUtils):
)
self.user.save()
response = self.client.post(reverse('wagtailadmin_pages_lock', args=(self.child_page.id, )))
response = self.client.post(reverse('wagtailadmin_pages:lock', args=(self.child_page.id, )))
# Check response
self.assertEqual(response.status_code, 403)
@ -2009,7 +2009,7 @@ class TestLocking(TestCase, WagtailTestUtils):
self.child_page.locked = True
self.child_page.save()
response = self.client.post(reverse('wagtailadmin_pages_unlock', args=(self.child_page.id, )))
response = self.client.post(reverse('wagtailadmin_pages:unlock', args=(self.child_page.id, )))
# Check response
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
@ -2022,7 +2022,7 @@ class TestLocking(TestCase, WagtailTestUtils):
self.child_page.locked = True
self.child_page.save()
response = self.client.get(reverse('wagtailadmin_pages_unlock', args=(self.child_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:unlock', args=(self.child_page.id, )))
# Check response
self.assertEqual(response.status_code, 405)
@ -2031,7 +2031,7 @@ class TestLocking(TestCase, WagtailTestUtils):
self.assertTrue(Page.objects.get(id=self.child_page.id).locked)
def test_unlock_post_already_unlocked(self):
response = self.client.post(reverse('wagtailadmin_pages_unlock', args=(self.child_page.id, )))
response = self.client.post(reverse('wagtailadmin_pages:unlock', args=(self.child_page.id, )))
# Check response
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
@ -2044,12 +2044,12 @@ class TestLocking(TestCase, WagtailTestUtils):
self.child_page.locked = True
self.child_page.save()
response = self.client.post(reverse('wagtailadmin_pages_unlock', args=(self.child_page.id, )), {
'next': reverse('wagtailadmin_pages_edit', args=(self.child_page.id, ))
response = self.client.post(reverse('wagtailadmin_pages:unlock', args=(self.child_page.id, )), {
'next': reverse('wagtailadmin_pages:edit', args=(self.child_page.id, ))
})
# Check response
self.assertRedirects(response, reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )))
self.assertRedirects(response, reverse('wagtailadmin_pages:edit', args=(self.child_page.id, )))
# Check that the page is unlocked
self.assertFalse(Page.objects.get(id=self.child_page.id).locked)
@ -2059,7 +2059,7 @@ class TestLocking(TestCase, WagtailTestUtils):
self.child_page.locked = True
self.child_page.save()
response = self.client.post(reverse('wagtailadmin_pages_unlock', args=(self.child_page.id, )), {
response = self.client.post(reverse('wagtailadmin_pages:unlock', args=(self.child_page.id, )), {
'next': 'http://www.google.co.uk'
})
@ -2074,7 +2074,7 @@ class TestLocking(TestCase, WagtailTestUtils):
self.child_page.locked = True
self.child_page.save()
response = self.client.post(reverse('wagtailadmin_pages_unlock', args=(9999, )))
response = self.client.post(reverse('wagtailadmin_pages:unlock', args=(9999, )))
# Check response
self.assertEqual(response.status_code, 404)
@ -2094,7 +2094,7 @@ class TestLocking(TestCase, WagtailTestUtils):
self.child_page.locked = True
self.child_page.save()
response = self.client.post(reverse('wagtailadmin_pages_unlock', args=(self.child_page.id, )))
response = self.client.post(reverse('wagtailadmin_pages:unlock', args=(self.child_page.id, )))
# Check response
self.assertEqual(response.status_code, 403)
@ -2125,7 +2125,7 @@ class TestIssue197(TestCase, WagtailTestUtils):
'tags': "hello, world",
'action-publish': "Publish",
}
response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.tagged_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:edit', args=(self.tagged_page.id, )), post_data)
# Should be redirected to explorer
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
@ -2159,7 +2159,7 @@ class TestChildRelationsOnSuperclass(TestCase, WagtailTestUtils):
self.login()
def test_get_create_form(self):
response = self.client.get(reverse('wagtailadmin_pages_create', args=('tests', 'standardindex', self.root_page.id)))
response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'standardindex', self.root_page.id)))
self.assertEqual(response.status_code, 200)
# Response should include an advert_placements formset labelled Adverts
self.assertContains(response, "Adverts")
@ -2176,19 +2176,19 @@ class TestChildRelationsOnSuperclass(TestCase, WagtailTestUtils):
'advert_placements-0-colour': 'yellow',
'advert_placements-0-id': '',
}
response = self.client.post(reverse('wagtailadmin_pages_create', args=('tests', 'standardindex', self.root_page.id)), post_data)
response = self.client.post(reverse('wagtailadmin_pages:add', args=('tests', 'standardindex', self.root_page.id)), post_data)
# Find the page and check it
page = Page.objects.get(path__startswith=self.root_page.path, slug='new-index').specific
# Should be redirected to edit page
self.assertRedirects(response, reverse('wagtailadmin_pages_edit', args=(page.id, )))
self.assertRedirects(response, reverse('wagtailadmin_pages:edit', args=(page.id, )))
self.assertEqual(page.advert_placements.count(), 1)
self.assertEqual(page.advert_placements.first().advert.text, 'test_advert')
def test_get_edit_form(self):
response = self.client.get(reverse('wagtailadmin_pages_edit', args=(self.index_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:edit', args=(self.index_page.id, )))
self.assertEqual(response.status_code, 200)
# Response should include an advert_placements formset labelled Adverts
@ -2213,7 +2213,7 @@ class TestChildRelationsOnSuperclass(TestCase, WagtailTestUtils):
'advert_placements-1-id': '',
'action-publish': "Publish",
}
response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.index_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:edit', args=(self.index_page.id, )), post_data)
# Should be redirected to explorer
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))

View file

@ -36,7 +36,7 @@ class TestSetPrivacyView(TestCase, WagtailTestUtils):
"""
This tests that a blank form is returned when a user opens the set_privacy view on a public page
"""
response = self.client.get(reverse('wagtailadmin_pages_set_privacy', args=(self.public_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:set_privacy', args=(self.public_page.id, )))
# Check response
self.assertEqual(response.status_code, 200)
@ -50,7 +50,7 @@ class TestSetPrivacyView(TestCase, WagtailTestUtils):
"""
This tests that the restriction type and password fields as set correctly when a user opens the set_privacy view on a public page
"""
response = self.client.get(reverse('wagtailadmin_pages_set_privacy', args=(self.private_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:set_privacy', args=(self.private_page.id, )))
# Check response
self.assertEqual(response.status_code, 200)
@ -65,7 +65,7 @@ class TestSetPrivacyView(TestCase, WagtailTestUtils):
"""
This tests that the set_privacy view tells the user that the password restriction has been applied to an ancestor
"""
response = self.client.get(reverse('wagtailadmin_pages_set_privacy', args=(self.private_child_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:set_privacy', args=(self.private_child_page.id, )))
# Check response
self.assertEqual(response.status_code, 200)
@ -80,7 +80,7 @@ class TestSetPrivacyView(TestCase, WagtailTestUtils):
'restriction_type': 'password',
'password': 'helloworld',
}
response = self.client.post(reverse('wagtailadmin_pages_set_privacy', args=(self.public_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:set_privacy', args=(self.public_page.id, )), post_data)
# Check response
self.assertEqual(response.status_code, 200)
@ -100,7 +100,7 @@ class TestSetPrivacyView(TestCase, WagtailTestUtils):
'restriction_type': 'password',
'password': '',
}
response = self.client.post(reverse('wagtailadmin_pages_set_privacy', args=(self.public_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:set_privacy', args=(self.public_page.id, )), post_data)
# Check response
self.assertEqual(response.status_code, 200)
@ -116,7 +116,7 @@ class TestSetPrivacyView(TestCase, WagtailTestUtils):
'restriction_type': 'none',
'password': '',
}
response = self.client.post(reverse('wagtailadmin_pages_set_privacy', args=(self.private_page.id, )), post_data)
response = self.client.post(reverse('wagtailadmin_pages:set_privacy', args=(self.private_page.id, )), post_data)
# Check response
self.assertEqual(response.status_code, 200)
@ -222,7 +222,7 @@ class TestPrivacyIndicators(TestCase, WagtailTestUtils):
"""
This tests that the privacy indicator on the public pages edit view is set to "PUBLIC"
"""
response = self.client.get(reverse('wagtailadmin_pages_edit', args=(self.public_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:edit', args=(self.public_page.id, )))
# Check the response
self.assertEqual(response.status_code, 200)
@ -236,7 +236,7 @@ class TestPrivacyIndicators(TestCase, WagtailTestUtils):
"""
This tests that the privacy indicator on the private pages edit view is set to "PRIVATE"
"""
response = self.client.get(reverse('wagtailadmin_pages_edit', args=(self.private_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:edit', args=(self.private_page.id, )))
# Check the response
self.assertEqual(response.status_code, 200)
@ -250,7 +250,7 @@ class TestPrivacyIndicators(TestCase, WagtailTestUtils):
"""
This tests that the privacy indicator on the private child pages edit view is set to "PRIVATE"
"""
response = self.client.get(reverse('wagtailadmin_pages_edit', args=(self.private_child_page.id, )))
response = self.client.get(reverse('wagtailadmin_pages:edit', args=(self.private_child_page.id, )))
# Check the response
self.assertEqual(response.status_code, 200)

View file

@ -79,7 +79,7 @@ class TestUserbarAddLink(TestCase, WagtailTestUtils):
response = self.client.get(reverse('wagtailadmin_userbar_frontend', args=(self.event_index.id, )))
# page allows subpages, so the 'add page' button should show
expected_url = reverse('wagtailadmin_pages_add_subpage', args=(self.event_index.id, ))
expected_url = reverse('wagtailadmin_pages:add_subpage', args=(self.event_index.id, ))
expected_link = '<a href="%s" target="_parent" class="action icon icon-plus" title="Add a child page">Add</a>' % expected_url
self.assertContains(response, expected_link)
@ -87,7 +87,7 @@ class TestUserbarAddLink(TestCase, WagtailTestUtils):
response = self.client.get(reverse('wagtailadmin_userbar_frontend', args=(self.business_child.id, )))
# page disallows subpages, so the 'add page' button shouldn't show
expected_url = reverse('wagtailadmin_pages_add_subpage', args=(self.business_index.id, ))
expected_url = reverse('wagtailadmin_pages:add_subpage', args=(self.business_index.id, ))
expected_link = '<a href="%s" target="_parent" class="action icon icon-plus" title="Add a child page">Add</a>' % expected_url
self.assertNotContains(response, expected_link)

View file

@ -44,13 +44,13 @@ class TestEditorHooks(TestCase, WagtailTestUtils):
self.login()
def test_editor_css_and_js_hooks_on_add(self):
response = self.client.get(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.homepage.id)))
response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.homepage.id)))
self.assertEqual(response.status_code, 200)
self.assertContains(response, '<link rel="stylesheet" href="/path/to/my/custom.css">')
self.assertContains(response, '<script src="/path/to/my/custom.js"></script>')
def test_editor_css_and_js_hooks_on_edit(self):
response = self.client.get(reverse('wagtailadmin_pages_edit', args=(self.homepage.id, )))
response = self.client.get(reverse('wagtailadmin_pages:edit', args=(self.homepage.id, )))
self.assertEqual(response.status_code, 200)
self.assertContains(response, '<link rel="stylesheet" href="/path/to/my/custom.css">')
self.assertContains(response, '<script src="/path/to/my/custom.js"></script>')

View file

@ -1,127 +0,0 @@
from django.conf.urls import url
from django.contrib.auth.decorators import permission_required
from django.contrib.auth import views as django_auth_views
from django.views.decorators.cache import cache_control
from wagtail.wagtailadmin.forms import PasswordResetForm
from wagtail.wagtailadmin.views import account, chooser, home, pages, tags, userbar, page_privacy
from wagtail.wagtailcore import hooks
from wagtail.utils.urlpatterns import decorate_urlpatterns
urlpatterns = [
url(r'^$', home.home, name='wagtailadmin_home'),
url(r'^failwhale/$', home.error_test, name='wagtailadmin_error_test'),
url(r'^explorer-nav/$', pages.explorer_nav, name='wagtailadmin_explorer_nav'),
url(r'^pages/$', pages.index, name='wagtailadmin_explore_root'),
url(r'^pages/(\d+)/$', pages.index, name='wagtailadmin_explore'),
url(r'^pages/new/(\w+)/(\w+)/(\d+)/$', pages.create, name='wagtailadmin_pages_create'),
url(r'^pages/new/(\w+)/(\w+)/(\d+)/preview/$', pages.preview_on_create, name='wagtailadmin_pages_preview_on_create'),
url(r'^pages/usage/(\w+)/(\w+)/$', pages.content_type_use, name='wagtailadmin_pages_type_use'),
url(r'^pages/(\d+)/edit/$', pages.edit, name='wagtailadmin_pages_edit'),
url(r'^pages/(\d+)/edit/preview/$', pages.preview_on_edit, name='wagtailadmin_pages_preview_on_edit'),
url(r'^pages/preview/$', pages.preview, name='wagtailadmin_pages_preview'),
url(r'^pages/preview_loading/$', pages.preview_loading, name='wagtailadmin_pages_preview_loading'),
url(r'^pages/(\d+)/view_draft/$', pages.view_draft, name='wagtailadmin_pages_view_draft'),
url(r'^pages/(\d+)/add_subpage/$', pages.add_subpage, name='wagtailadmin_pages_add_subpage'),
url(r'^pages/(\d+)/delete/$', pages.delete, name='wagtailadmin_pages_delete'),
url(r'^pages/(\d+)/unpublish/$', pages.unpublish, name='wagtailadmin_pages_unpublish'),
url(r'^pages/search/$', pages.search, name='wagtailadmin_pages_search'),
url(r'^pages/(\d+)/move/$', pages.move_choose_destination, name='wagtailadmin_pages_move'),
url(r'^pages/(\d+)/move/(\d+)/$', pages.move_choose_destination, name='wagtailadmin_pages_move_choose_destination'),
url(r'^pages/(\d+)/move/(\d+)/confirm/$', pages.move_confirm, name='wagtailadmin_pages_move_confirm'),
url(r'^pages/(\d+)/set_position/$', pages.set_page_position, name='wagtailadmin_pages_set_page_position'),
url(r'^pages/(\d+)/copy/$', pages.copy, name='wagtailadmin_pages_copy'),
url(r'^pages/moderation/(\d+)/approve/$', pages.approve_moderation, name='wagtailadmin_pages_approve_moderation'),
url(r'^pages/moderation/(\d+)/reject/$', pages.reject_moderation, name='wagtailadmin_pages_reject_moderation'),
url(r'^pages/moderation/(\d+)/preview/$', pages.preview_for_moderation, name='wagtailadmin_pages_preview_for_moderation'),
url(r'^pages/(\d+)/privacy/$', page_privacy.set_privacy, name='wagtailadmin_pages_set_privacy'),
url(r'^pages/(\d+)/lock/$', pages.lock, name='wagtailadmin_pages_lock'),
url(r'^pages/(\d+)/unlock/$', pages.unlock, name='wagtailadmin_pages_unlock'),
url(r'^choose-page/$', chooser.browse, name='wagtailadmin_choose_page'),
url(r'^choose-page/(\d+)/$', chooser.browse, name='wagtailadmin_choose_page_child'),
url(r'^choose-page/search/$', chooser.search, name='wagtailadmin_choose_page_search'),
url(r'^choose-external-link/$', chooser.external_link, name='wagtailadmin_choose_page_external_link'),
url(r'^choose-email-link/$', chooser.email_link, name='wagtailadmin_choose_page_email_link'),
url(r'^tag-autocomplete/$', tags.autocomplete, name='wagtailadmin_tag_autocomplete'),
url(r'^account/$', account.account, name='wagtailadmin_account'),
url(r'^account/change_password/$', account.change_password, name='wagtailadmin_account_change_password'),
url(r'^account/notification_preferences/$', account.notification_preferences, name='wagtailadmin_account_notification_preferences'),
url(r'^logout/$', account.logout, name='wagtailadmin_logout'),
]
# Import additional urlpatterns from any apps that define a register_admin_urls hook
for fn in hooks.get_hooks('register_admin_urls'):
urls = fn()
if urls:
urlpatterns += urls
# Add "wagtailadmin.access_admin" permission check
urlpatterns = decorate_urlpatterns(urlpatterns,
permission_required(
'wagtailadmin.access_admin',
login_url='wagtailadmin_login'
)
)
# These url patterns do not require an authenticated admin user
urlpatterns += [
url(r'^login/$', account.login, name='wagtailadmin_login'),
# These two URLs have the "permission_required" decorator applied directly
# as they need to fail with a 403 error rather than redirect to the login page
url(r'^userbar/(\d+)/$', userbar.for_frontend, name='wagtailadmin_userbar_frontend'),
url(r'^userbar/moderation/(\d+)/$', userbar.for_moderation, name='wagtailadmin_userbar_moderation'),
# Password reset
url(
r'^password_reset/$', django_auth_views.password_reset, {
'template_name': 'wagtailadmin/account/password_reset/form.html',
'email_template_name': 'wagtailadmin/account/password_reset/email.txt',
'subject_template_name': 'wagtailadmin/account/password_reset/email_subject.txt',
'password_reset_form': PasswordResetForm,
'post_reset_redirect': 'wagtailadmin_password_reset_done',
}, name='wagtailadmin_password_reset'
),
url(
r'^password_reset/done/$', django_auth_views.password_reset_done, {
'template_name': 'wagtailadmin/account/password_reset/done.html'
}, name='wagtailadmin_password_reset_done'
),
url(
r'^password_reset/confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
django_auth_views.password_reset_confirm, {
'template_name': 'wagtailadmin/account/password_reset/confirm.html',
'post_reset_redirect': 'wagtailadmin_password_reset_complete',
}, name='wagtailadmin_password_reset_confirm',
),
url(
r'^password_reset/complete/$', django_auth_views.password_reset_complete, {
'template_name': 'wagtailadmin/account/password_reset/complete.html'
}, name='wagtailadmin_password_reset_complete'
),
]
# Decorate all views with cache settings to prevent caching
urlpatterns = decorate_urlpatterns(urlpatterns,
cache_control(private=True, no_cache=True, no_store=True, max_age=0)
)

View file

@ -0,0 +1,73 @@
from django.conf.urls import url, include
from django.contrib.auth.decorators import permission_required
from django.views.decorators.cache import cache_control
from wagtail.wagtailadmin.urls import pages as wagtailadmin_pages_urls
from wagtail.wagtailadmin.urls import password_reset as wagtailadmin_password_reset_urls
from wagtail.wagtailadmin.views import account, chooser, home, pages, tags, userbar
from wagtail.wagtailcore import hooks
from wagtail.utils.urlpatterns import decorate_urlpatterns
urlpatterns = [
url(r'^$', home.home, name='wagtailadmin_home'),
url(r'^failwhale/$', home.error_test, name='wagtailadmin_error_test'),
url(r'^explorer-nav/$', pages.explorer_nav, name='wagtailadmin_explorer_nav'),
# TODO: Move into wagtailadmin_pages namespace
url(r'^pages/$', pages.index, name='wagtailadmin_explore_root'),
url(r'^pages/(\d+)/$', pages.index, name='wagtailadmin_explore'),
url(r'^pages/', include(wagtailadmin_pages_urls, namespace='wagtailadmin_pages')),
# TODO: Move into wagtailadmin_pages namespace
url(r'^choose-page/$', chooser.browse, name='wagtailadmin_choose_page'),
url(r'^choose-page/(\d+)/$', chooser.browse, name='wagtailadmin_choose_page_child'),
url(r'^choose-page/search/$', chooser.search, name='wagtailadmin_choose_page_search'),
url(r'^choose-external-link/$', chooser.external_link, name='wagtailadmin_choose_page_external_link'),
url(r'^choose-email-link/$', chooser.email_link, name='wagtailadmin_choose_page_email_link'),
url(r'^tag-autocomplete/$', tags.autocomplete, name='wagtailadmin_tag_autocomplete'),
url(r'^account/$', account.account, name='wagtailadmin_account'),
url(r'^account/change_password/$', account.change_password, name='wagtailadmin_account_change_password'),
url(r'^account/notification_preferences/$', account.notification_preferences, name='wagtailadmin_account_notification_preferences'),
url(r'^logout/$', account.logout, name='wagtailadmin_logout'),
]
# Import additional urlpatterns from any apps that define a register_admin_urls hook
for fn in hooks.get_hooks('register_admin_urls'):
urls = fn()
if urls:
urlpatterns += urls
# Add "wagtailadmin.access_admin" permission check
urlpatterns = decorate_urlpatterns(urlpatterns,
permission_required(
'wagtailadmin.access_admin',
login_url='wagtailadmin_login'
)
)
# These url patterns do not require an authenticated admin user
urlpatterns += [
url(r'^login/$', account.login, name='wagtailadmin_login'),
# These two URLs have the "permission_required" decorator applied directly
# as they need to fail with a 403 error rather than redirect to the login page
url(r'^userbar/(\d+)/$', userbar.for_frontend, name='wagtailadmin_userbar_frontend'),
url(r'^userbar/moderation/(\d+)/$', userbar.for_moderation, name='wagtailadmin_userbar_moderation'),
# Password reset
url(r'^password_reset/', include(wagtailadmin_password_reset_urls)),
]
# Decorate all views with cache settings to prevent caching
urlpatterns = decorate_urlpatterns(urlpatterns,
cache_control(private=True, no_cache=True, no_store=True, max_age=0)
)

View file

@ -0,0 +1,39 @@
from django.conf.urls import url
from wagtail.wagtailadmin.views import pages, page_privacy
urlpatterns = [
url(r'^add/(\w+)/(\w+)/(\d+)/$', pages.create, name='add'),
url(r'^add/(\w+)/(\w+)/(\d+)/preview/$', pages.preview_on_create, name='preview_on_add'),
url(r'^usage/(\w+)/(\w+)/$', pages.content_type_use, name='type_use'),
url(r'^(\d+)/edit/$', pages.edit, name='edit'),
url(r'^(\d+)/edit/preview/$', pages.preview_on_edit, name='preview_on_edit'),
url(r'^preview/$', pages.preview, name='preview'),
url(r'^preview_loading/$', pages.preview_loading, name='preview_loading'),
url(r'^(\d+)/view_draft/$', pages.view_draft, name='view_draft'),
url(r'^(\d+)/add_subpage/$', pages.add_subpage, name='add_subpage'),
url(r'^(\d+)/delete/$', pages.delete, name='delete'),
url(r'^(\d+)/unpublish/$', pages.unpublish, name='unpublish'),
url(r'^search/$', pages.search, name='search'),
url(r'^(\d+)/move/$', pages.move_choose_destination, name='move'),
url(r'^(\d+)/move/(\d+)/$', pages.move_choose_destination, name='move_choose_destination'),
url(r'^(\d+)/move/(\d+)/confirm/$', pages.move_confirm, name='move_confirm'),
url(r'^(\d+)/set_position/$', pages.set_page_position, name='set_page_position'),
url(r'^(\d+)/copy/$', pages.copy, name='copy'),
url(r'^moderation/(\d+)/approve/$', pages.approve_moderation, name='approve_moderation'),
url(r'^moderation/(\d+)/reject/$', pages.reject_moderation, name='reject_moderation'),
url(r'^moderation/(\d+)/preview/$', pages.preview_for_moderation, name='preview_for_moderation'),
url(r'^(\d+)/privacy/$', page_privacy.set_privacy, name='set_privacy'),
url(r'^(\d+)/lock/$', pages.lock, name='lock'),
url(r'^(\d+)/unlock/$', pages.unlock, name='unlock'),
]

View file

@ -0,0 +1,34 @@
from django.conf.urls import url
from django.contrib.auth import views as django_auth_views
from wagtail.wagtailadmin.forms import PasswordResetForm
urlpatterns = [
url(
r'^$', django_auth_views.password_reset, {
'template_name': 'wagtailadmin/account/password_reset/form.html',
'email_template_name': 'wagtailadmin/account/password_reset/email.txt',
'subject_template_name': 'wagtailadmin/account/password_reset/email_subject.txt',
'password_reset_form': PasswordResetForm,
'post_reset_redirect': 'wagtailadmin_password_reset_done',
}, name='wagtailadmin_password_reset'
),
url(
r'^done/$', django_auth_views.password_reset_done, {
'template_name': 'wagtailadmin/account/password_reset/done.html'
}, name='wagtailadmin_password_reset_done'
),
url(
r'^confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
django_auth_views.password_reset_confirm, {
'template_name': 'wagtailadmin/account/password_reset/confirm.html',
'post_reset_redirect': 'wagtailadmin_password_reset_complete',
}, name='wagtailadmin_password_reset_confirm',
),
url(
r'^complete/$', django_auth_views.password_reset_complete, {
'template_name': 'wagtailadmin/account/password_reset/complete.html'
}, name='wagtailadmin_password_reset_complete'
),
]

View file

@ -20,6 +20,16 @@ def get_querystring(request):
})
def shared_context(request, extra_context={}):
context = {
'allow_external_link': request.GET.get('allow_external_link'),
'allow_email_link': request.GET.get('allow_email_link'),
'querystring': get_querystring(request),
}
context.update(extra_context)
return context
def browse(request, parent_page_id=None):
ITEMS_PER_PAGE = 25
@ -82,17 +92,18 @@ def browse(request, parent_page_id=None):
except EmptyPage:
pages = paginator.page(paginator.num_pages)
return render_modal_workflow(request, 'wagtailadmin/chooser/browse.html', 'wagtailadmin/chooser/browse.js', {
'allow_external_link': request.GET.get('allow_external_link'),
'allow_email_link': request.GET.get('allow_email_link'),
'querystring': get_querystring(request),
'parent_page': parent_page,
'pages': pages,
'search_form': search_form,
'page_type_string': ','.join(page_types),
'page_type_names': [desired_class.get_verbose_name() for desired_class in desired_classes],
'page_types_restricted': (page_type != 'wagtailcore.page')
})
return render_modal_workflow(
request,
'wagtailadmin/chooser/browse.html', 'wagtailadmin/chooser/browse.js',
shared_context(request, {
'parent_page': parent_page,
'pages': pages,
'search_form': search_form,
'page_type_string': ','.join(page_types),
'page_type_names': [desired_class.get_verbose_name() for desired_class in desired_classes],
'page_types_restricted': (page_type != 'wagtailcore.page')
})
)
def search(request, parent_page_id=None):
@ -130,11 +141,13 @@ def search(request, parent_page_id=None):
page.can_choose = True
shown_pages.append(page)
return render(request, 'wagtailadmin/chooser/_search_results.html', {
'querystring': get_querystring(request),
'searchform': search_form,
'pages': shown_pages,
})
return render(
request, 'wagtailadmin/chooser/_search_results.html',
shared_context(request, {
'searchform': search_form,
'pages': shown_pages,
})
)
def external_link(request):
@ -162,11 +175,9 @@ def external_link(request):
return render_modal_workflow(
request,
'wagtailadmin/chooser/external_link.html', 'wagtailadmin/chooser/external_link.js',
{
'querystring': get_querystring(request),
'allow_email_link': request.GET.get('allow_email_link'),
shared_context(request, {
'form': form,
}
})
)
@ -195,9 +206,7 @@ def email_link(request):
return render_modal_workflow(
request,
'wagtailadmin/chooser/email_link.html', 'wagtailadmin/chooser/email_link.js',
{
'querystring': get_querystring(request),
'allow_external_link': request.GET.get('allow_external_link'),
shared_context(request, {
'form': form,
}
})
)

View file

@ -79,7 +79,7 @@ def add_subpage(request, parent_page_id):
# Only one page type is available - redirect straight to the create form rather than
# making the user choose
content_type = page_types[0]
return redirect('wagtailadmin_pages_create', content_type.app_label, content_type.model, parent_page.id)
return redirect('wagtailadmin_pages:add', content_type.app_label, content_type.model, parent_page.id)
return render(request, 'wagtailadmin/pages/add_subpage.html', {
'parent_page': parent_page,
@ -176,12 +176,12 @@ def create(request, content_type_app_name, content_type_model_name, parent_page_
if is_publishing:
messages.success(request, _("Page '{0}' created and published.").format(page.title), buttons=[
messages.button(page.url, _('View live')),
messages.button(reverse('wagtailadmin_pages_edit', args=(page.id,)), _('Edit'))
messages.button(reverse('wagtailadmin_pages:edit', args=(page.id,)), _('Edit'))
])
elif is_submitting:
messages.success(request, _("Page '{0}' created and submitted for moderation.").format(page.title), buttons=[
messages.button(reverse('wagtailadmin_pages_view_draft', args=(page.id,)), _('View draft')),
messages.button(reverse('wagtailadmin_pages_edit', args=(page.id,)), _('Edit'))
messages.button(reverse('wagtailadmin_pages:view_draft', args=(page.id,)), _('View draft')),
messages.button(reverse('wagtailadmin_pages:edit', args=(page.id,)), _('Edit'))
])
send_notification(page.get_latest_revision().id, 'submitted', request.user.id)
else:
@ -197,7 +197,7 @@ def create(request, content_type_app_name, content_type_model_name, parent_page_
return redirect('wagtailadmin_explore', page.get_parent().id)
else:
# Just saving - remain on edit page for further edits
return redirect('wagtailadmin_pages_edit', page.id)
return redirect('wagtailadmin_pages:edit', page.id)
else:
messages.error(request, _("The page could not be created due to validation errors"))
edit_handler = edit_handler_class(instance=page, form=form)
@ -256,12 +256,12 @@ def edit(request, page_id):
if is_publishing:
messages.success(request, _("Page '{0}' published.").format(page.title), buttons=[
messages.button(page.url, _('View live')),
messages.button(reverse('wagtailadmin_pages_edit', args=(page_id,)), _('Edit'))
messages.button(reverse('wagtailadmin_pages:edit', args=(page_id,)), _('Edit'))
])
elif is_submitting:
messages.success(request, _("Page '{0}' submitted for moderation.").format(page.title), buttons=[
messages.button(reverse('wagtailadmin_pages_view_draft', args=(page_id,)), _('View draft')),
messages.button(reverse('wagtailadmin_pages_edit', args=(page_id,)), _('Edit'))
messages.button(reverse('wagtailadmin_pages:view_draft', args=(page_id,)), _('View draft')),
messages.button(reverse('wagtailadmin_pages:edit', args=(page_id,)), _('Edit'))
])
send_notification(page.get_latest_revision().id, 'submitted', request.user.id)
else:
@ -277,7 +277,7 @@ def edit(request, page_id):
return redirect('wagtailadmin_explore', page.get_parent().id)
else:
# Just saving - remain on edit page for further edits
return redirect('wagtailadmin_pages_edit', page.id)
return redirect('wagtailadmin_pages:edit', page.id)
else:
if page.locked:
messages.error(request, _("The page could not be saved as it is locked"))
@ -494,7 +494,7 @@ def unpublish(request, page_id):
page.unpublish()
messages.success(request, _("Page '{0}' unpublished.").format(page.title), buttons=[
messages.button(reverse('wagtailadmin_pages_edit', args=(page.id,)), _('Edit'))
messages.button(reverse('wagtailadmin_pages:edit', args=(page.id,)), _('Edit'))
])
return redirect('wagtailadmin_explore', page.get_parent().id)
@ -546,7 +546,7 @@ def move_confirm(request, page_to_move_id, destination_id):
page_to_move.move(destination, pos='last-child')
messages.success(request, _("Page '{0}' moved.").format(page_to_move.title), buttons=[
messages.button(reverse('wagtailadmin_pages_edit', args=(page_to_move.id,)), _('Edit'))
messages.button(reverse('wagtailadmin_pages:edit', args=(page_to_move.id,)), _('Edit'))
])
return redirect('wagtailadmin_explore', destination.id)
@ -733,7 +733,7 @@ def approve_moderation(request, revision_id):
revision.approve_moderation()
messages.success(request, _("Page '{0}' published.").format(revision.page.title), buttons=[
messages.button(revision.page.url, _('View live')),
messages.button(reverse('wagtailadmin_pages_edit', args=(revision.page.id,)), _('Edit'))
messages.button(reverse('wagtailadmin_pages:edit', args=(revision.page.id,)), _('Edit'))
])
send_notification(revision.id, 'approved', request.user.id)
@ -752,7 +752,7 @@ def reject_moderation(request, revision_id):
if request.method == 'POST':
revision.reject_moderation()
messages.success(request, _("Page '{0}' rejected for publication.").format(revision.page.title), buttons=[
messages.button(reverse('wagtailadmin_pages_edit', args=(revision.page.id,)), _('Edit'))
messages.button(reverse('wagtailadmin_pages:edit', args=(revision.page.id,)), _('Edit'))
])
send_notification(revision.id, 'rejected', request.user.id)

View file

@ -3,13 +3,13 @@ from wagtail.wagtaildocs.views import documents, chooser
urlpatterns = [
url(r'^$', documents.index, name='wagtaildocs_index'),
url(r'^add/$', documents.add, name='wagtaildocs_add_document'),
url(r'^edit/(\d+)/$', documents.edit, name='wagtaildocs_edit_document'),
url(r'^delete/(\d+)/$', documents.delete, name='wagtaildocs_delete_document'),
url(r'^$', documents.index, name='index'),
url(r'^add/$', documents.add, name='add'),
url(r'^edit/(\d+)/$', documents.edit, name='edit'),
url(r'^delete/(\d+)/$', documents.delete, name='delete'),
url(r'^chooser/$', chooser.chooser, name='wagtaildocs_chooser'),
url(r'^chooser/(\d+)/$', chooser.document_chosen, name='wagtaildocs_document_chosen'),
url(r'^chooser/upload/$', chooser.chooser_upload, name='wagtaildocs_chooser_upload'),
url(r'^usage/(\d+)/$', documents.usage, name='wagtaildocs_document_usage'),
url(r'^chooser/$', chooser.chooser, name='chooser'),
url(r'^chooser/(\d+)/$', chooser.document_chosen, name='document_chosen'),
url(r'^chooser/upload/$', chooser.chooser_upload, name='chooser_upload'),
url(r'^usage/(\d+)/$', documents.usage, name='document_usage'),
]

View file

@ -55,7 +55,7 @@ class Document(models.Model, TagSearchable):
@property
def usage_url(self):
return reverse('wagtaildocs_document_usage',
return reverse('wagtaildocs:document_usage',
args=(self.id,))
def is_editable_by_user(self, user):

View file

@ -12,7 +12,7 @@
<div class="tab-content">
<section id="search" class="{% if not uploadform.errors %}active {% endif %}nice-padding">
<form class="document-search search-bar" action="{% url 'wagtaildocs_chooser' %}" method="GET">
<form class="document-search search-bar" action="{% url 'wagtaildocs:chooser' %}" method="GET">
<ul class="fields">
{% for field in searchform %}
{% include "wagtailadmin/shared/field_as_li.html" with field=field %}
@ -26,7 +26,7 @@
</section>
{% if uploadform %}
<section id="upload" class="{% if uploadform.errors %}active {% endif %}nice-padding">
<form class="document-upload" action="{% url 'wagtaildocs_chooser_upload' %}" method="POST" enctype="multipart/form-data">
<form class="document-upload" action="{% url 'wagtaildocs:chooser_upload' %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<ul class="fields">
{% for field in uploadform %}

View file

@ -16,7 +16,7 @@
{% include "wagtailadmin/shared/header.html" with title=add_str icon="doc-full-inverse" %}
<div class="nice-padding">
<form action="{% url 'wagtaildocs_add_document' %}" method="POST" enctype="multipart/form-data">
<form action="{% url 'wagtaildocs:add' %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<ul class="fields">
{% for field in form %}

View file

@ -8,7 +8,7 @@
<div class="nice-padding">
<p>{% trans "Are you sure you want to delete this document?" %}</p>
<form action="{% url 'wagtaildocs_delete_document' document.id %}" method="POST">
<form action="{% url 'wagtaildocs:delete' document.id %}" method="POST">
{% csrf_token %}
<input type="submit" value='{% trans "Yes, delete" %}' class="serious" />
</form>

View file

@ -18,7 +18,7 @@
<div class="row row-flush nice-padding">
<div class="col10 divider-after">
<form action="{% url 'wagtaildocs_edit_document' document.id %}" method="POST" enctype="multipart/form-data">
<form action="{% url 'wagtaildocs:edit' document.id %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<ul class="fields">
{% for field in form %}
@ -28,7 +28,7 @@
{% include "wagtailadmin/shared/field_as_li.html" %}
{% endif %}
{% endfor %}
<li><input type="submit" value="{% trans 'Save' %}" /> <a href="{% url 'wagtaildocs_delete_document' document.id %}" class="button button-secondary no">{% trans "Delete document" %}</a></li>
<li><input type="submit" value="{% trans 'Save' %}" /> <a href="{% url 'wagtaildocs:delete' document.id %}" class="button button-secondary no">{% trans "Delete document" %}</a></li>
</ul>
</form>
</div>

View file

@ -5,7 +5,7 @@
{% block extra_js %}
<script>
window.headerSearch = {
url: "{% url 'wagtaildocs_index' %}",
url: "{% url 'wagtaildocs:index' %}",
termInput: "#id_q",
targetOutput: "#document-results"
}
@ -15,7 +15,7 @@
{% block content %}
{% trans "Documents" as doc_str %}
{% trans "Add a document" as add_doc_str %}
{% include "wagtailadmin/shared/header.html" with title=doc_str add_link="wagtaildocs_add_document" icon="doc-full-inverse" add_text=add_doc_str search_url="wagtaildocs_index" %}
{% include "wagtailadmin/shared/header.html" with title=doc_str add_link="wagtaildocs:add" icon="doc-full-inverse" add_text=add_doc_str search_url="wagtaildocs:index" %}
<div class="nice-padding">
<div id="document-results" class="documents">

View file

@ -7,7 +7,7 @@
<tr class="table-headers">
<th>
{% if not is_searching %}
<a href="{% url 'wagtaildocs_index' %}{% if not ordering == "title" %}?ordering=title{% endif %}" class="icon icon-arrow-down-after {% if ordering == "title" %}teal{% endif %}">
<a href="{% url 'wagtaildocs:index' %}{% if not ordering == "title" %}?ordering=title{% endif %}" class="icon icon-arrow-down-after {% if ordering == "title" %}teal{% endif %}">
{% trans "Title" %}
</a>
{% else %}
@ -17,7 +17,7 @@
<th>{% trans "File" %}</th>
<th>
{% if not is_searching %}
<a href="{% url 'wagtaildocs_index' %}{% if not ordering == "-created_at" %}?ordering=-created_at{% endif %}" class="icon icon-arrow-down-after {% if ordering == "-created_at" %}teal{% endif %}">
<a href="{% url 'wagtaildocs:index' %}{% if not ordering == "-created_at" %}?ordering=-created_at{% endif %}" class="icon icon-arrow-down-after {% if ordering == "-created_at" %}teal{% endif %}">
{% trans "Uploaded" %}
</a>
{% else %}
@ -31,9 +31,9 @@
<tr>
<td class="title">
{% if choosing %}
<h2><a href="{% url 'wagtaildocs_document_chosen' doc.id %}" class="document-choice">{{ doc.title }}</a></h2>
<h2><a href="{% url 'wagtaildocs:document_chosen' doc.id %}" class="document-choice">{{ doc.title }}</a></h2>
{% else %}
<h2><a href="{% url 'wagtaildocs_edit_document' doc.id %}">{{ doc.title }}</a></h2>
<h2><a href="{% url 'wagtaildocs:edit' doc.id %}">{{ doc.title }}</a></h2>
{% endif %}
</td>
<td><a href="{{ doc.url }}" class="nolink">{{ doc.filename }}</a></td>

Some files were not shown because too many files have changed in this diff Show more