Add before_{create,edit,delete}_snippet hooks

pull/6275/head
Karl Hobley 2020-07-30 08:08:40 +01:00 zatwierdzone przez GitHub
rodzic 873160dd9a
commit f3f932d2e5
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
5 zmienionych plików z 177 dodań i 1 usunięć

Wyświetl plik

@ -4,6 +4,7 @@ Changelog
2.11 (xx.xx.xxxx) - IN DEVELOPMENT
~~~~~~~~~~~~~~~~~
* Add `before_edit_snippet`, `before_create_snippet` and `before_delete_snippet` hooks and documentation (Karl Hobley. Sponsored by the Mozilla Foundation)
2.10 (xx.xx.xxxx) - IN DEVELOPMENT
~~~~~~~~~~~~~~~~~

Wyświetl plik

@ -1016,6 +1016,24 @@ Hooks for working with registered Snippets.
def after_snippet_update(request, instance):
return HttpResponse(f"Congrats on editing a snippet with id {instance.pk}", content_type="text/plain")
.. _before_edit_snippet:
``before_edit_snippet``
~~~~~~~~~~~~~~~~~~~~~~
Called at the beginning of the edit snippet view. The callable passed into the hook will receive the model instance, the request object. If the callable returns an ``HttpResponse``, that response will be returned immediately to the user, and Wagtail will not proceed to call ``redirect()`` to the listing view.
.. code-block:: python
from django.http import HttpResponse
from wagtail.core import hooks
@hooks.register('before_edit_snippet')
def block_snippet_edit(request, instance):
if isinstance(instance, RestrictedSnippet) and instance.prevent_edit:
return HttpResponse("Sorry, you can't edit this snippet", content_type="text/plain")
.. _after_create_snippet:
``after_create_snippet``
@ -1025,6 +1043,13 @@ Hooks for working with registered Snippets.
``after_edit_snippet`` work in identical ways. The only difference is where
the hook is called.
.. _before_create_snippet:
``before_create_snippet``
~~~~~~~~~~~~~~~~~~~~~~~~
Called at the beginning of the create snippet view. Works in a similar way to `before_edit_snippet` except the model is passed as an argument instead of an instance.
.. _after_delete_snippet:
``after_delete_snippet``
@ -1044,6 +1069,29 @@ Hooks for working with registered Snippets.
total = len(instances)
return HttpResponse(f"{total} snippets have been deleted", content_type="text/plain")
.. _before_delete_snippet:
``before_delete_snippet``
~~~~~~~~~~~~~~~~~~~~~~~~
Called at the beginning of the delete snippet view. The callable passed into the hook will receive the model instance(s) as a queryset along with the request object. If the callable returns an ``HttpResponse``, that response will be returned immediately to the user, and Wagtail will not proceed to call ``redirect()`` to the listing view.
.. code-block:: python
from django.http import HttpResponse
from wagtail.core import hooks
@hooks.register('before_delete_snippet')
def before_snippet_delete(request, instances):
# "instances" is a QuerySet
total = len(instances)
if request.method == 'POST':
# Override the deletion behaviour
instances.delete()
return HttpResponse(f"{total} snippets have been deleted", content_type="text/plain")
Audit log
---------

Wyświetl plik

@ -13,7 +13,7 @@ What's new
Other features
~~~~~~~~~~~~~~
* ...
* Add ``before_edit_snippet``, ``before_create_snippet`` and ``before_delete_snippet`` hooks and documentation (Karl Hobley. Sponsored by the Mozilla Foundation)
Bug fixes

Wyświetl plik

@ -228,6 +228,37 @@ class TestSnippetCreateView(TestCase, WagtailTestUtils):
snippet = FileUploadSnippet.objects.get()
self.assertEqual(snippet.file.read(), b"Uploaded file")
def test_before_create_snippet_hook_get(self):
def hook_func(request, model):
self.assertIsInstance(request, HttpRequest)
self.assertEqual(model, Advert)
return HttpResponse("Overridden!")
with self.register_hook('before_create_snippet', hook_func):
response = self.get()
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
def test_before_create_snippet_hook_post(self):
def hook_func(request, model):
self.assertIsInstance(request, HttpRequest)
self.assertEqual(model, Advert)
return HttpResponse("Overridden!")
with self.register_hook('before_create_snippet', hook_func):
post_data = {
'text': 'Hook test',
'url': 'http://www.example.com/'
}
response = self.post(post_data=post_data)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# Request intercepted before advert was created
self.assertFalse(Advert.objects.exists())
def test_after_create_snippet_hook(self):
def hook_func(request, instance):
self.assertIsInstance(request, HttpRequest)
@ -245,6 +276,9 @@ class TestSnippetCreateView(TestCase, WagtailTestUtils):
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# Request intercepted after advert was created
self.assertTrue(Advert.objects.exists())
class BaseTestSnippetEditView(TestCase, WagtailTestUtils):
@ -318,6 +352,38 @@ class TestSnippetEditView(BaseTestSnippetEditView):
list(snippet.tags.order_by('name')),
expected_tags)
def test_before_edit_snippet_hook_get(self):
def hook_func(request, instance):
self.assertIsInstance(request, HttpRequest)
self.assertEqual(instance.text, 'test_advert')
self.assertEqual(instance.url, 'http://www.example.com')
return HttpResponse("Overridden!")
with self.register_hook('before_edit_snippet', hook_func):
response = self.get()
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
def test_before_edit_snippet_hook_post(self):
def hook_func(request, instance):
self.assertIsInstance(request, HttpRequest)
self.assertEqual(instance.text, 'test_advert')
self.assertEqual(instance.url, 'http://www.example.com')
return HttpResponse("Overridden!")
with self.register_hook('before_edit_snippet', hook_func):
response = self.post(post_data={'text': 'Edited and runs hook',
'url': 'http://www.example.com/hook-enabled-edited'})
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# Request intercepted before advert was updated
self.assertEqual(Advert.objects.get().text, "test_advert")
def test_after_edit_snippet_hook(self):
def hook_func(request, instance):
@ -333,6 +399,9 @@ class TestSnippetEditView(BaseTestSnippetEditView):
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# Request intercepted after advert was updated
self.assertEqual(Advert.objects.get().text, "Edited and runs hook")
class TestEditTabbedSnippet(BaseTestSnippetEditView):
@ -402,6 +471,45 @@ class TestSnippetDelete(TestCase, WagtailTestUtils):
self.assertContains(response, 'Used 2 times')
self.assertContains(response, self.test_snippet.usage_url())
def test_before_delete_snippet_hook_get(self):
advert = Advert.objects.create(
url='http://www.example.com/',
text='Test hook',
)
def hook_func(request, instances):
self.assertIsInstance(request, HttpRequest)
self.assertQuerysetEqual(instances, ["<Advert: Test hook>"])
return HttpResponse("Overridden!")
with self.register_hook('before_delete_snippet', hook_func):
response = self.client.get(reverse('wagtailsnippets:delete', args=['tests', 'advert', quote(advert.pk)]))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
def test_before_delete_snippet_hook_post(self):
advert = Advert.objects.create(
url='http://www.example.com/',
text='Test hook',
)
def hook_func(request, instances):
self.assertIsInstance(request, HttpRequest)
self.assertQuerysetEqual(instances, ["<Advert: Test hook>"])
return HttpResponse("Overridden!")
with self.register_hook('before_delete_snippet', hook_func):
response = self.client.post(
reverse('wagtailsnippets:delete', args=('tests', 'advert', quote(advert.pk), ))
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# Request intercepted before advert was deleted
self.assertTrue(Advert.objects.filter(pk=advert.pk).exists())
def test_after_delete_snippet_hook(self):
advert = Advert.objects.create(
url='http://www.example.com/',
@ -421,6 +529,9 @@ class TestSnippetDelete(TestCase, WagtailTestUtils):
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# Request intercepted after advert was deleted
self.assertFalse(Advert.objects.filter(pk=advert.pk).exists())
class TestSnippetDeleteMultipleWithOne(TestCase, WagtailTestUtils):
# test deletion of one snippet using the delete-multiple URL

Wyświetl plik

@ -134,6 +134,11 @@ def create(request, app_label, model_name):
if not request.user.has_perm(permission):
return permission_denied(request)
for fn in hooks.get_hooks('before_create_snippet'):
result = fn(request, model)
if hasattr(result, 'status_code'):
return result
instance = model()
edit_handler = get_snippet_edit_handler(model)
edit_handler = edit_handler.bind_to(request=request)
@ -188,6 +193,12 @@ def edit(request, app_label, model_name, pk):
return permission_denied(request)
instance = get_object_or_404(model, pk=unquote(pk))
for fn in hooks.get_hooks('before_edit_snippet'):
result = fn(request, instance)
if hasattr(result, 'status_code'):
return result
edit_handler = get_snippet_edit_handler(model)
edit_handler = edit_handler.bind_to(instance=instance, request=request)
form_class = edit_handler.get_form_class()
@ -247,6 +258,11 @@ def delete(request, app_label, model_name, pk=None):
ids = request.GET.getlist('id')
instances = model.objects.filter(pk__in=ids)
for fn in hooks.get_hooks('before_delete_snippet'):
result = fn(request, instances)
if hasattr(result, 'status_code'):
return result
count = len(instances)
if request.method == 'POST':