feat: Added construct_image_chooser_queryset, construct_document_chooser_queryset and construct_page_chooser_queryset hooks

This commit is contained in:
Gagaro 2017-02-15 18:46:28 +01:00 committed by Matt Westcott
parent b00bf70229
commit 46c9cdda15
9 changed files with 215 additions and 25 deletions

View file

@ -7,6 +7,7 @@ Changelog
* Use minified versions of jQuery and jQuery UI in the admin. Total savings without compression 371 KB (Tom Dyson)
* Hooks can now specify the order in which they are run (Gagaro)
* Added a `submit_buttons` block to login template (Gagaro)
* Added `construct_image_chooser_queryset`, `construct_document_chooser_queryset` and `construct_page_chooser_queryset` hooks (Gagaro)
* The homepage created in the project template is now titled "Home" rather than "Homepage" (Karl Hobley)
* Signal receivers for custom `Image` and `Rendition` models are connected automatically (Mike Dingjan)
* Fix: Marked 'Date from' / 'Date to' strings in wagtailforms for translation (Vorlif)

View file

@ -511,6 +511,66 @@ Hooks for customising the way users are directed through the process of creating
return items.append( UserbarPuppyLinkItem() )
Choosers
--------
.. _construct_page_chooser_queryset:
``construct_page_chooser_queryset``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Called when rendering the page chooser view, to allow the page listing queryset to be customised. The callable passed into the hook will receive the current page queryset and the request object, and must return a Page queryset (either the original one, or a new one).
.. code-block:: python
from wagtail.wagtailcore import hooks
@hooks.register('construct_page_chooser_queryset')
def show_my_pages_only(pages, request):
# Only show own pages
pages = pages.filter(owner=request.user)
return pages
.. _construct_document_chooser_queryset:
``construct_document_chooser_queryset``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Called when rendering the document chooser view, to allow the document listing queryset to be customised. The callable passed into the hook will receive the current document queryset and the request object, and must return a Document queryset (either the original one, or a new one).
.. code-block:: python
from wagtail.wagtailcore import hooks
@hooks.register('construct_document_chooser_queryset')
def show_my_uploaded_documents_only(documents, request):
# Only show uploaded documents
documents = documents.filter(uploaded_by=request.user)
return documents
.. _construct_image_chooser_queryset:
``construct_image_chooser_queryset``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Called when rendering the image chooser view, to allow the image listing queryset to be customised. The callable passed into the hook will receive the current image queryset and the request object, and must return a Document queryset (either the original one, or a new one).
.. code-block:: python
from wagtail.wagtailcore import hooks
@hooks.register('construct_image_chooser_queryset')
def show_my_uploaded_images_only(images, request):
# Only show uploaded images
images = images.filter(uploaded_by=request.user)
return images
Page explorer
-------------

View file

@ -17,6 +17,7 @@ Other features
* Use minified versions of jQuery and jQuery UI in the admin. Total savings without compression 371 KB (Tom Dyson)
* Hooks can now specify the order in which they are run (Gagaro)
* Added a ``submit_buttons`` block to login template (Gagaro)
* Added ``construct_image_chooser_queryset``, ``construct_document_chooser_queryset`` and ``construct_page_chooser_queryset`` hooks (Gagaro)
* The homepage created in the project template is now titled "Home" rather than "Homepage" (Karl Hobley)
* Signal receivers for custom ``Image`` and ``Rendition`` models are connected automatically (Mike Dingjan)

View file

@ -27,6 +27,21 @@ class TestChooserBrowse(TestCase, WagtailTestUtils):
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailadmin/chooser/browse.html')
def test_construct_queryset_hook(self):
page = SimplePage(title="Test shown", content="hello")
Page.get_first_root_node().add_child(instance=page)
page_not_shown = SimplePage(title="Test not shown", content="hello")
Page.get_first_root_node().add_child(instance=page_not_shown)
def filter_pages(pages, request):
return pages.filter(id=page.id)
with self.register_hook('construct_page_chooser_queryset', filter_pages):
response = self.get()
self.assertEqual(len(response.context['pages']), 1)
self.assertEqual(response.context['pages'][0].specific, page)
class TestCanChooseRootFlag(TestCase, WagtailTestUtils):
def setUp(self):
@ -297,6 +312,21 @@ class TestChooserSearch(TestCase, WagtailTestUtils):
response = self.get({'page_type': 'foo'})
self.assertEqual(response.status_code, 404)
def test_construct_queryset_hook(self):
page = SimplePage(title="Test shown", content="hello")
self.root_page.add_child(instance=page)
page_not_shown = SimplePage(title="Test not shown", content="hello")
self.root_page.add_child(instance=page_not_shown)
def filter_pages(pages, request):
return pages.filter(id=page.id)
with self.register_hook('construct_page_chooser_queryset', filter_pages):
response = self.get({'q': 'Test'})
self.assertEqual(len(response.context['pages']), 1)
self.assertEqual(response.context['pages'][0].specific, page)
class TestAutomaticRootPageDetection(TestCase, WagtailTestUtils):
def setUp(self):

View file

@ -8,6 +8,7 @@ from django.shortcuts import get_object_or_404, render
from wagtail.utils.pagination import paginate
from wagtail.wagtailadmin.forms import EmailLinkChooserForm, ExternalLinkChooserForm, SearchForm
from wagtail.wagtailadmin.modal_workflow import render_modal_workflow
from wagtail.wagtailcore import hooks
from wagtail.wagtailcore.models import Page
from wagtail.wagtailcore.utils import resolve_model_string
@ -75,6 +76,10 @@ def browse(request, parent_page_id=None):
# Get children of parent page
pages = parent_page.get_children()
# allow hooks to modify the queryset
for hook in hooks.get_hooks('construct_page_chooser_queryset'):
pages = hook(pages, request)
# Filter them by page type
if desired_classes != (Page,):
# restrict the page listing to just those pages that:
@ -131,15 +136,20 @@ def search(request, parent_page_id=None):
except (ValueError, LookupError):
raise Http404
pages = Page.objects.all()
# allow hooks to modify the queryset
for hook in hooks.get_hooks('construct_page_chooser_queryset'):
pages = hook(pages, request)
search_form = SearchForm(request.GET)
if search_form.is_valid() and search_form.cleaned_data['q']:
pages = Page.objects.exclude(
pages = pages.exclude(
depth=1 # never include root
)
pages = filter_page_type(pages, desired_classes)
pages = pages.search(search_form.cleaned_data['q'], fields=['title'])
else:
pages = Page.objects.none()
pages = pages.none()
paginator, pages = paginate(request, pages, per_page=25)

View file

@ -634,7 +634,7 @@ class TestMultipleDocumentUploader(TestCase, WagtailTestUtils):
class TestDocumentChooserView(TestCase, WagtailTestUtils):
def setUp(self):
self.login()
self.user = self.login()
def test_simple(self):
response = self.client.get(reverse('wagtaildocs:chooser'))
@ -688,6 +688,44 @@ class TestDocumentChooserView(TestCase, WagtailTestUtils):
# Check that we got the last page
self.assertEqual(response.context['documents'].number, response.context['documents'].paginator.num_pages)
def test_construct_queryset_hook_browse(self):
document = Document.objects.create(
title="Test document shown",
uploaded_by_user=self.user,
)
Document.objects.create(
title="Test document not shown",
)
def filter_documents(documents, request):
# Filter on `uploaded_by_user` because it is
# the only default FilterField in search_fields
return documents.filter(uploaded_by_user=self.user)
with self.register_hook('construct_document_chooser_queryset', filter_documents):
response = self.client.get(reverse('wagtaildocs:chooser'))
self.assertEqual(len(response.context['documents']), 1)
self.assertEqual(response.context['documents'][0], document)
def test_construct_queryset_hook_search(self):
document = Document.objects.create(
title="Test document shown",
uploaded_by_user=self.user,
)
Document.objects.create(
title="Test document not shown",
)
def filter_documents(documents, request):
# Filter on `uploaded_by_user` because it is
# the only default FilterField in search_fields
return documents.filter(uploaded_by_user=self.user)
with self.register_hook('construct_document_chooser_queryset', filter_documents):
response = self.client.get(reverse('wagtaildocs:chooser'), {'q': 'Test'})
self.assertEqual(len(response.context['documents']), 1)
self.assertEqual(response.context['documents'][0], document)
class TestDocumentChooserChosenView(TestCase, WagtailTestUtils):
def setUp(self):

View file

@ -9,6 +9,7 @@ from wagtail.utils.pagination import paginate
from wagtail.wagtailadmin.forms import SearchForm
from wagtail.wagtailadmin.modal_workflow import render_modal_workflow
from wagtail.wagtailadmin.utils import PermissionPolicyChecker
from wagtail.wagtailcore import hooks
from wagtail.wagtailcore.models import Collection
from wagtail.wagtaildocs.forms import get_document_form
from wagtail.wagtaildocs.models import get_document_model
@ -41,12 +42,14 @@ def chooser(request):
else:
uploadform = None
documents = []
documents = Document.objects.all()
# allow hooks to modify the queryset
for hook in hooks.get_hooks('construct_document_chooser_queryset'):
documents = hook(documents, request)
q = None
is_searching = False
if 'q' in request.GET or 'p' in request.GET or 'collection_id' in request.GET:
documents = Document.objects.all()
collection_id = request.GET.get('collection_id')
if collection_id:
@ -77,16 +80,16 @@ def chooser(request):
if len(collections) < 2:
collections = None
documents = Document.objects.order_by('-created_at')
documents = documents.order_by('-created_at')
paginator, documents = paginate(request, documents, per_page=10)
return render_modal_workflow(request, 'wagtaildocs/chooser/chooser.html', 'wagtaildocs/chooser/chooser.js', {
'documents': documents,
'uploadform': uploadform,
'searchform': searchform,
'collections': collections,
'is_searching': False,
})
return render_modal_workflow(request, 'wagtaildocs/chooser/chooser.html', 'wagtaildocs/chooser/chooser.js', {
'documents': documents,
'uploadform': uploadform,
'searchform': searchform,
'collections': collections,
'is_searching': False,
})
def document_chosen(request, document_id):

View file

@ -412,7 +412,7 @@ class TestImageDeleteView(TestCase, WagtailTestUtils):
class TestImageChooserView(TestCase, WagtailTestUtils):
def setUp(self):
self.login()
self.user = self.login()
def get(self, params={}):
return self.client.get(reverse('wagtailimages:chooser'), params)
@ -452,6 +452,48 @@ class TestImageChooserView(TestCase, WagtailTestUtils):
# Results should not include images that just have 'even' in the title
self.assertNotContains(response, "Test image 3 is even better")
def test_construct_queryset_hook_browse(self):
image = Image.objects.create(
title="Test image shown",
file=get_test_image_file(),
uploaded_by_user=self.user,
)
Image.objects.create(
title="Test image not shown",
file=get_test_image_file(),
)
def filter_images(images, request):
# Filter on `uploaded_by_user` because it is
# the only default FilterField in search_fields
return images.filter(uploaded_by_user=self.user)
with self.register_hook('construct_image_chooser_queryset', filter_images):
response = self.get()
self.assertEqual(len(response.context['images']), 1)
self.assertEqual(response.context['images'][0], image)
def test_construct_queryset_hook_search(self):
image = Image.objects.create(
title="Test image shown",
file=get_test_image_file(),
uploaded_by_user=self.user,
)
Image.objects.create(
title="Test image not shown",
file=get_test_image_file(),
)
def filter_images(images, request):
# Filter on `uploaded_by_user` because it is
# the only default FilterField in search_fields
return images.filter(uploaded_by_user=self.user)
with self.register_hook('construct_image_chooser_queryset', filter_images):
response = self.get({'q': 'Test'})
self.assertEqual(len(response.context['images']), 1)
self.assertEqual(response.context['images'][0], image)
class TestImageChooserChosenView(TestCase, WagtailTestUtils):
def setUp(self):

View file

@ -9,6 +9,7 @@ from wagtail.utils.pagination import paginate
from wagtail.wagtailadmin.forms import SearchForm
from wagtail.wagtailadmin.modal_workflow import render_modal_workflow
from wagtail.wagtailadmin.utils import PermissionPolicyChecker, popular_tags_for_model
from wagtail.wagtailcore import hooks
from wagtail.wagtailcore.models import Collection
from wagtail.wagtailimages import get_image_model
from wagtail.wagtailimages.formats import get_image_format
@ -49,6 +50,10 @@ def chooser(request):
images = Image.objects.order_by('-created_at')
# allow hooks to modify the queryset
for hook in hooks.get_hooks('construct_image_chooser_queryset'):
images = hook(images, request)
q = None
if (
'q' in request.GET or 'p' in request.GET or 'tag' in request.GET or
@ -91,16 +96,16 @@ def chooser(request):
paginator, images = paginate(request, images, per_page=12)
return render_modal_workflow(request, 'wagtailimages/chooser/chooser.html', 'wagtailimages/chooser/chooser.js', {
'images': images,
'uploadform': uploadform,
'searchform': searchform,
'is_searching': False,
'query_string': q,
'will_select_format': request.GET.get('select_format'),
'popular_tags': popular_tags_for_model(Image),
'collections': collections,
})
return render_modal_workflow(request, 'wagtailimages/chooser/chooser.html', 'wagtailimages/chooser/chooser.js', {
'images': images,
'uploadform': uploadform,
'searchform': searchform,
'is_searching': False,
'query_string': q,
'will_select_format': request.GET.get('select_format'),
'popular_tags': popular_tags_for_model(Image),
'collections': collections,
})
def image_chosen(request, image_id):