kopia lustrzana https://github.com/wagtail/wagtail
Merge branch 'master' into copy-page-slug-available
commit
389f3262de
|
@ -18,6 +18,7 @@ Changelog
|
|||
* Add Learn Wagtail to third-party tutorials in documentation (Matt Westcott)
|
||||
* Add a Django setting `TAG_LIMIT` to limit number of tags that can be added to any taggit model (Mani)
|
||||
* Added instructions on how to generate urls for `ModelAdmin` to documentation (LB (Ben Johnston), Andy Babic)
|
||||
* Added option to specify a fallback URL on `{% pageurl %}` (Arthur Holzner)
|
||||
* Fix: Set `SERVER_PORT` to 443 in `Page.dummy_request()` for HTTPS sites (Sergey Fedoseev)
|
||||
* Fix: Include port number in `Host` header of `Page.dummy_request()` (Sergey Fedoseev)
|
||||
* Fix: Validation error messages in `InlinePanel` no longer count towards `max_num` when disabling the 'add' button (Todd Dembrey, Thibaud Colas)
|
||||
|
@ -31,6 +32,8 @@ Changelog
|
|||
* Fix: Unclear error message when saving image after focal point edit (Hugo van den Berg)
|
||||
* Fix: `send_mail` now correctly uses the `html_message` kwarg for HTML messages (Tiago Requeijo)
|
||||
* Fix: Page copying no longer allowed if page model has reached its `max_count` (Andy Babic)
|
||||
* Fix: Don't show page type on page chooser button when multiple types are allowed (Thijs Kramer)
|
||||
* Fix: Make sure page chooser search results correspond to the latest search by canceling previous requests (Esper Kuijs)
|
||||
* Fix: Inform user when moving a page from one parent to another where there is an already existing page with the same slug (Casper Timmers)
|
||||
|
||||
|
||||
|
|
|
@ -353,6 +353,7 @@ Contributors
|
|||
* Gassan Gousseinov
|
||||
* Thomas Kremmel
|
||||
* patta42
|
||||
* Esper Kuijs
|
||||
|
||||
Translators
|
||||
===========
|
||||
|
|
|
@ -48,6 +48,7 @@ Want to know more about customising ``ModelAdmin``?
|
|||
create_edit_delete_views
|
||||
inspectview
|
||||
chooseparentview
|
||||
tips_and_tricks/index
|
||||
|
||||
.. _modeladmin_usage:
|
||||
|
||||
|
@ -222,10 +223,3 @@ the same ``wagtail_hooks.py`` file if you want. The example below will create
|
|||
modeladmin_register(BookAdmin)
|
||||
modeladmin_register(MovieAdmin)
|
||||
modeladmin_register(MusicAdminGroup)
|
||||
|
||||
|
||||
-------------------------
|
||||
Additional Tips & Tricks
|
||||
-------------------------
|
||||
|
||||
* To programatically generate URLs for any of your ``ModelAdmin`` views, see :ref:`modeladmin_reversing_urls`.
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
==========================
|
||||
Additional tips and tricks
|
||||
==========================
|
||||
|
||||
This section explores some of modeladmin's lesser-known features, and provides examples to help with modeladmin customisation. More pages will be added in future.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
reversing_urls
|
|
@ -28,6 +28,7 @@ Other features
|
|||
* Add Learn Wagtail to third-party tutorials in documentation (Matt Westcott)
|
||||
* Add a Django setting ``TAG_LIMIT`` to limit number of tags that can be added to any taggit model (Mani)
|
||||
* Added instructions on how to generate urls for ``ModelAdmin`` to documentation (LB (Ben Johnston), Andy Babic)
|
||||
* Added option to specify a fallback URL on ``{% pageurl %}`` (Arthur Holzner)
|
||||
|
||||
|
||||
Bug fixes
|
||||
|
@ -47,6 +48,8 @@ Bug fixes
|
|||
* Increase max length on ``Embed.thumbnail_url`` to 255 characters (Kevin Howbrook)
|
||||
* ``send_mail`` now correctly uses the ``html_message`` kwarg for HTML messages (Tiago Requeijo)
|
||||
* Page copying no longer allowed if page model has reached its ``max_count`` (Andy Babic)
|
||||
* Don't show page type on page chooser button when multiple types are allowed (Thijs Kramer)
|
||||
* Make sure page chooser search results correspond to the latest search by canceling previous requests (Esper Kuijs)
|
||||
|
||||
|
||||
Upgrade considerations
|
||||
|
|
|
@ -160,7 +160,23 @@ Takes a Page object and returns a relative URL (``/foo/bar/``) if within the sam
|
|||
|
||||
{% load wagtailcore_tags %}
|
||||
...
|
||||
<a href="{% pageurl page.blog_page %}">
|
||||
<a href="{% pageurl page.get_parent %}">Back to index</a>
|
||||
|
||||
|
||||
A ``fallback`` keyword argument can be provided - this should be a URL route name that takes no parameters, and will be used as a substitute URL when the passed page is ``None``.
|
||||
|
||||
.. code-block:: html+django
|
||||
|
||||
{% load wagtailcore_tags %}
|
||||
|
||||
{% for publication in page.related_publications.all %}
|
||||
<li>
|
||||
<a href="{% pageurl publication.detail_page fallback='coming_soon' %}">
|
||||
{{ publication.title }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
.. _slugurl_tag:
|
||||
|
||||
|
|
|
@ -25,16 +25,19 @@ PAGE_CHOOSER_MODAL_ONLOAD_HANDLERS = {
|
|||
/* save initial page browser HTML, so that we can restore it if the search box gets cleared */
|
||||
var initialPageResultsHtml = $('.page-results', modal.body).html();
|
||||
|
||||
var request;
|
||||
|
||||
function search() {
|
||||
var query = $('#id_q', modal.body).val();
|
||||
if (query != '') {
|
||||
$.ajax({
|
||||
request = $.ajax({
|
||||
url: searchUrl,
|
||||
data: {
|
||||
q: query,
|
||||
results_only: true
|
||||
},
|
||||
success: function(data, status) {
|
||||
request = null;
|
||||
$('.page-results', modal.body).html(data);
|
||||
ajaxifySearchResults();
|
||||
}
|
||||
|
@ -48,6 +51,9 @@ PAGE_CHOOSER_MODAL_ONLOAD_HANDLERS = {
|
|||
}
|
||||
|
||||
$('#id_q', modal.body).on('input', function() {
|
||||
if(request) {
|
||||
request.abort();
|
||||
}
|
||||
clearTimeout($.data(this, 'timer'));
|
||||
var wait = setTimeout(search, 200);
|
||||
$(this).data('timer', wait);
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
{% csrf_token %}
|
||||
|
||||
{% page_permissions parent_page as parent_page_perms %}
|
||||
{% include "wagtailadmin/pages/listing/_list_explore.html" with sortable=1 full_width=1 show_ordering_column=1 parent_page=parent_page orderable=parent_page_perms.can_reorder_children %}
|
||||
{% include "wagtailadmin/pages/listing/_list_explore.html" with sortable=1 sortable_by_type=1 full_width=1 show_ordering_column=1 parent_page=parent_page orderable=parent_page_perms.can_reorder_children %}
|
||||
|
||||
{% if do_paginate %}
|
||||
{% url 'wagtailadmin_explore' parent_page.id as pagination_base_url %}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
Table headers for the page listing, when in 'explore' mode. Expects the following variables:
|
||||
|
||||
sortable: if true, headings are links to wagtailadmin_explore with sort parameters applied.
|
||||
sortable_by_type: must be true to allow sorting on the 'type' column
|
||||
show_ordering_column: if true, an 'ordering' column is added.
|
||||
orderable: if true, the 'ordering' column is populated (again with links to wagtailadmin_explore).
|
||||
|
||||
|
@ -28,43 +29,31 @@ ordering: the current sort parameter
|
|||
</th>
|
||||
{% endif %}
|
||||
<th class="title">
|
||||
{% if sortable %}
|
||||
<a href="{% if ordering == 'title' %}{% querystring ordering='-title' %}{% else %}{% querystring ordering='title' %}{% endif %}" class="icon icon-arrow-{% if ordering == 'title' %}down-after{% elif ordering == '-title' %}up-after{% else %}down-after{% endif %} {% if ordering == 'title' or ordering == '-title' %}teal{% endif %}">
|
||||
{% trans 'Title' %}
|
||||
</a>
|
||||
{% else %}
|
||||
{% trans 'Title' %}
|
||||
{% endif %}
|
||||
{% trans 'Title' as title_label %}
|
||||
{% table_header_label label=title_label sortable=sortable sort_field='title' %}
|
||||
</th>
|
||||
{% if show_parent %}
|
||||
<th class="parent">{% trans 'Parent' %}</th>
|
||||
<th class="parent">
|
||||
{% trans 'Parent' as parent_label %}
|
||||
{% table_header_label label=parent_label sortable=0 %}
|
||||
</th>
|
||||
{% endif %}
|
||||
<th class="updated">
|
||||
{% if sortable %}
|
||||
<a href="{% if ordering == 'latest_revision_created_at' %}{% querystring ordering='-latest_revision_created_at' %}{% else %}{% querystring ordering='latest_revision_created_at' %}{% endif %}" class="icon icon-arrow-{% if ordering == '-latest_revision_created_at' %}up-after{% else %}down-after{% endif %} {% if ordering == 'latest_revision_created_at' or ordering == '-latest_revision_created_at' %}teal {% endif %}">
|
||||
{% trans 'Updated' %}
|
||||
</a>
|
||||
{% else %}
|
||||
{% trans 'Updated' %}
|
||||
{% endif %}
|
||||
{% trans 'Updated' as updated_label %}
|
||||
{% table_header_label label=updated_label sortable=sortable sort_field='latest_revision_created_at' %}
|
||||
</th>
|
||||
<th class="type">
|
||||
{% if sortable and not not_sortable_by_type %}
|
||||
<a href="{% if ordering == 'content_type' %}{% querystring ordering='-content_type' %}{% else %}{% querystring ordering='content_type' %}{% endif %}" class="icon icon-arrow-{% if ordering == '-content_type' %}up-after{% else %}down-after{% endif %} {% if ordering == 'content_type' or ordering == '-content_type' %}teal {% endif %}">
|
||||
{% trans 'Type' %}
|
||||
</a>
|
||||
{% trans 'Type' as type_label %}
|
||||
|
||||
{% if sortable and sortable_by_type %}
|
||||
{% table_header_label label=type_label sortable=1 sort_field='content_type' %}
|
||||
{% else %}
|
||||
{% trans 'Type' %}
|
||||
{% table_header_label label=type_label sortable=0 %}
|
||||
{% endif %}
|
||||
</th>
|
||||
<th class="status">
|
||||
{% if sortable %}
|
||||
<a href="{% if ordering == 'live' %}{% querystring ordering='-live' %}{% else %}{% querystring ordering='live' %}{% endif %}" class="icon icon-arrow-{% if ordering == '-live' %}up-after{% else %}down-after{% endif %} {% if ordering == 'live' or ordering == '-live' %}teal {% endif %}">
|
||||
{% trans 'Status' %}
|
||||
</a>
|
||||
{% else %}
|
||||
{% trans 'Status' %}
|
||||
{% endif %}
|
||||
{% trans 'Status' as status_label %}
|
||||
{% table_header_label label=status_label sortable=sortable sort_field='live' %}
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
</nav>
|
||||
{% endif %}
|
||||
|
||||
{% include "wagtailadmin/pages/listing/_list_explore.html" with show_parent=1 sortable=1 not_sortable_by_type=1 %}
|
||||
{% include "wagtailadmin/pages/listing/_list_explore.html" with show_parent=1 sortable=1 sortable_by_type=0 %}
|
||||
|
||||
{% url 'wagtailadmin_pages:search' as pagination_base_url %}
|
||||
{% paginate pages base_url=pagination_base_url %}
|
||||
|
|
|
@ -8,7 +8,7 @@ from django.contrib.messages.constants import DEFAULT_TAGS as MESSAGE_TAGS
|
|||
from django.template.defaultfilters import stringfilter
|
||||
from django.template.loader import render_to_string
|
||||
from django.templatetags.static import static
|
||||
from django.utils.html import conditional_escape
|
||||
from django.utils.html import conditional_escape, format_html
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from wagtail.admin.menu import admin_menu
|
||||
|
@ -289,6 +289,52 @@ def querystring(context, **kwargs):
|
|||
return '?' + querydict.urlencode()
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def table_header_label(context, label=None, sortable=True, ordering=None, sort_context_var='ordering', sort_param='ordering', sort_field=None):
|
||||
"""
|
||||
A label to go in a table header cell, optionally with a 'sort' link that alternates between
|
||||
forward and reverse sorting
|
||||
|
||||
label = label text
|
||||
ordering = current active ordering. If not specified, we will fetch it from the template context variable
|
||||
given by sort_context_var. (We don't fetch it from the URL because that wouldn't give the view method
|
||||
the opportunity to set a default)
|
||||
sort_param = URL parameter that indicates the current active ordering
|
||||
sort_field = the value for sort_param that indicates that sorting is currently on this column.
|
||||
For example, if sort_param='ordering' and sort_field='title', then a URL parameter of
|
||||
ordering=title indicates that the listing is ordered forwards on this column, and a URL parameter
|
||||
of ordering=-title indicated that the listing is ordered in reverse on this column
|
||||
To disable sorting on this column, set sortable=False or leave sort_field unspecified.
|
||||
"""
|
||||
if not sortable or not sort_field:
|
||||
# render label without a sort link
|
||||
return label
|
||||
|
||||
if ordering is None:
|
||||
ordering = context.get(sort_context_var)
|
||||
reverse_sort_field = "-%s" % sort_field
|
||||
|
||||
if ordering == sort_field:
|
||||
# currently ordering forwards on this column; link should change to reverse ordering
|
||||
url = querystring(context, **{sort_param: reverse_sort_field})
|
||||
classname = "icon icon-arrow-down-after teal"
|
||||
|
||||
elif ordering == reverse_sort_field:
|
||||
# currently ordering backwards on this column; link should change to forward ordering
|
||||
url = querystring(context, **{sort_param: sort_field})
|
||||
classname = "icon icon-arrow-up-after teal"
|
||||
|
||||
else:
|
||||
# not currently ordering on this column; link should change to forward ordering
|
||||
url = querystring(context, **{sort_param: sort_field})
|
||||
classname = "icon icon-arrow-down-after"
|
||||
|
||||
return format_html(
|
||||
'<a href="{url}" class="{classname}">{label}</a>',
|
||||
url=url, classname=classname, label=label
|
||||
)
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def pagination_querystring(context, page_number, page_key=DEFAULT_PAGE_KEY):
|
||||
"""
|
||||
|
|
|
@ -76,7 +76,7 @@ class TestAdminPageChooserWidget(TestCase):
|
|||
)
|
||||
|
||||
html = widget.render_html('test', self.child_page, {})
|
||||
self.assertIn(">Choose a page (Simple Page, Event Page)<", html)
|
||||
self.assertIn(">Choose a page<", html)
|
||||
|
||||
def test_render_js_init_with_can_choose_root(self):
|
||||
widget = widgets.AdminPageChooser(can_choose_root=True)
|
||||
|
|
|
@ -171,9 +171,9 @@ class AdminPageChooser(AdminChooser):
|
|||
super().__init__(**kwargs)
|
||||
|
||||
if target_models:
|
||||
models = ', '.join([model._meta.verbose_name.title() for model in target_models if model is not Page])
|
||||
if models:
|
||||
self.choose_one_text += ' (' + models + ')'
|
||||
model_names = [model._meta.verbose_name.title() for model in target_models if model is not Page]
|
||||
if len(model_names) == 1:
|
||||
self.choose_one_text += ' (' + model_names[0] + ')'
|
||||
|
||||
self.user_perms = user_perms
|
||||
self.target_models = list(target_models or [Page])
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from django import template
|
||||
from django.shortcuts import reverse
|
||||
from django.template.defaulttags import token_kwargs
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.safestring import mark_safe
|
||||
|
@ -12,11 +13,15 @@ register = template.Library()
|
|||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def pageurl(context, page):
|
||||
def pageurl(context, page, fallback=None):
|
||||
"""
|
||||
Outputs a page's URL as relative (/foo/bar/) if it's within the same site as the
|
||||
current page, or absolute (http://example.com/foo/bar/) if not.
|
||||
If kwargs contains a fallback view name and page is None, the fallback view url will be returned.
|
||||
"""
|
||||
if page is None and fallback:
|
||||
return reverse(fallback)
|
||||
|
||||
if not hasattr(page, 'relative_url'):
|
||||
raise ValueError("pageurl tag expected a Page object, got %r" % page)
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ from django import template
|
|||
from django.core.cache import cache
|
||||
from django.http import HttpRequest
|
||||
from django.test import TestCase
|
||||
from django.urls.exceptions import NoReverseMatch
|
||||
from django.utils.safestring import SafeText
|
||||
|
||||
from wagtail.core.models import Page, Site
|
||||
|
@ -19,6 +20,16 @@ class TestPageUrlTags(TestCase):
|
|||
self.assertContains(response,
|
||||
'<a href="/events/christmas/">Christmas</a>')
|
||||
|
||||
def test_pageurl_fallback(self):
|
||||
tpl = template.Template('''{% load wagtailcore_tags %}<a href="{% pageurl page fallback='fallback' %}">Fallback</a>''')
|
||||
result = tpl.render(template.Context({'page': None}))
|
||||
self.assertIn('<a href="/fallback/">Fallback</a>', result)
|
||||
|
||||
def test_pageurl_fallback_without_valid_fallback(self):
|
||||
tpl = template.Template('''{% load wagtailcore_tags %}<a href="{% pageurl page fallback='not-existing-endpoint' %}">Fallback</a>''')
|
||||
with self.assertRaises(NoReverseMatch):
|
||||
tpl.render(template.Context({'page': None}))
|
||||
|
||||
def test_slugurl_tag(self):
|
||||
response = self.client.get('/events/christmas/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from django.conf.urls import include, url
|
||||
from django.http import HttpResponse
|
||||
|
||||
from wagtail.admin import urls as wagtailadmin_urls
|
||||
from wagtail.api.v2.endpoints import PagesAPIEndpoint
|
||||
|
@ -37,6 +38,8 @@ urlpatterns = [
|
|||
|
||||
url(r'^testapp/', include(testapp_urls)),
|
||||
|
||||
url(r'^fallback/', lambda: HttpResponse('ok'), name='fallback'),
|
||||
|
||||
# For anything not caught by a more specific rule above, hand over to
|
||||
# Wagtail's serving mechanism
|
||||
url(r'', include(wagtail_urls)),
|
||||
|
|
Ładowanie…
Reference in New Issue