Bulk actions - Refactor action registration (#7426)

* [feat] Bulk action class now accepts a list of models

* [feat] Add list of models to each bulk action

* [refactor] Remove individual listing dispatcher views

* [refactor] Introduce new hook for registering action classes

* [refactor] Add single dispatcher view for all bulk actions, and modify tag used to fetch bulk actions for ui

* [refactor] Transfer bulk action files to a directory

* [feat] Add utility functions to retrieve bulk actions easily

* [refactor] Register actions in wagtail_hooks.py files

* [refactor] Fix linting errors

* [fix] Fix tests due to change in url of bulk actions

* [refactor] Change action registration for users listing

* [fix] Fix code for which tests were failing

* [feat] Raise exception if bulk action class is not derived from BulkAction

* [feat] Make single model in cls.models as default model

* [refactor] Change name of function to get bulk actions for a model

* [feat] Add classmethod to get default model in case of single element models list

* [feat] Add bulk action registry to keep track of bulk actions instead of looping over all of them each time

* [fix] Fix linting issue

* [feat] Initialise bulk action registry once and reuse that instance wherever required

* [refactor] get_bulk_actions_for_model now returns an iterator
pull/7618/head
Shohan 2021-08-31 14:02:01 +05:30 zatwierdzone przez Matt Westcott
rodzic 52ce949ea0
commit abd6c18125
57 zmienionych plików z 195 dodań i 242 usunięć

Wyświetl plik

@ -2,7 +2,7 @@
<footer class="footer bulk-actions-choices hidden" data-bulk-action-footer>
<div class="footer__container">
<input data-bulk-action-select-all-checkbox type="checkbox" aria-label='{% trans "Select all" %}' />
<ul class="bulk-actions-buttons">{% bulk_action_choices bulk_action_register_hook %}</ul>
<ul>{% bulk_action_choices app_label model_name %}</ul>
<span data-bulk-action-num-objects class="num-objects"></span>
{% if select_all_obj_text and objects.has_other_pages %}
<button data-bulk-action-num-objects-in-listing class="num-objects-in-listing u-hidden">{{ select_all_obj_text }}</button>

Wyświetl plik

@ -21,7 +21,7 @@
{% paginate pages base_url=pagination_base_url %}
{% endif %}
{% trans "Select all pages in listing" as select_all_text %}
{% include 'wagtailadmin/bulk_actions/footer.html' with select_all_obj_text=select_all_text bulk_action_register_hook='register_page_bulk_action' objects=pages %}
{% include 'wagtailadmin/bulk_actions/footer.html' with select_all_obj_text=select_all_text app_label='wagtailcore' model_name='page' objects=pages %}
</form>
{% endblock %}

Wyświetl plik

@ -24,5 +24,5 @@
<div id="page-results">
{% include "wagtailadmin/pages/search_results.html" %}
</div>
{% include 'wagtailadmin/bulk_actions/footer.html' with bulk_action_register_hook='register_page_bulk_action' %}
{% include 'wagtailadmin/bulk_actions/footer.html' with app_label='wagtailcore' model_name='page' %}
{% endblock %}

Wyświetl plik

@ -31,6 +31,8 @@ from wagtail.admin.navigation import get_explorable_root_page
from wagtail.admin.search import admin_search_areas
from wagtail.admin.staticfiles import versioned_static as versioned_static_func
from wagtail.admin.ui import sidebar
from wagtail.admin.views.bulk_action.registry import bulk_action_registry
from wagtail.admin.views.pages.utils import get_valid_next_url_from_request
from wagtail.admin.widgets import ButtonWithDropdown, PageListingButton
from wagtail.core import hooks
from wagtail.core.models import (
@ -504,24 +506,9 @@ def bulk_action_filters(context):
@register.inclusion_tag("wagtailadmin/pages/listing/_buttons.html",
takes_context=True)
def bulk_action_choices(context, hook_name):
corresponding_urls = {
'register_page_bulk_action': 'wagtailadmin_page_bulk_action',
'register_document_bulk_action': 'wagtaildocs:document_bulk_action',
'register_image_bulk_action': 'wagtailimages:image_bulk_action',
'register_user_bulk_action': 'wagtailusers_users:user_bulk_action',
}
if hook_name not in corresponding_urls:
return {'buttons': []}
corresponding_url = corresponding_urls[hook_name]
bulk_actions_list = []
bulk_actions = hooks.get_hooks(hook_name)
for action_func in bulk_actions:
action = action_func(context.request)
bulk_actions_list.append(action)
def bulk_action_choices(context, app_label, model_name):
bulk_actions_list = list(bulk_action_registry.get_bulk_actions_for_model(app_label, model_name))
bulk_actions_list.sort(key=lambda x: x.action_priority)
bulk_action_more_list = []
@ -529,10 +516,14 @@ def bulk_action_choices(context, hook_name):
bulk_action_more_list = bulk_actions_list[4:]
bulk_actions_list = bulk_actions_list[:4]
next_url = get_valid_next_url_from_request(context['request'])
if not next_url:
next_url = context['request'].path
bulk_action_buttons = [
PageListingButton(
action.display_name,
reverse(corresponding_url, args=[action.action_type]) + '?' + urlencode({'next': action.next_url}),
reverse('wagtail_bulk_action', args=[app_label, model_name, action.action_type]) + '?' + urlencode({'next': next_url}),
attrs={'aria-label': action.aria_label},
priority=action.action_priority,
classes=action.classes | {'bulk-action-btn'},
@ -549,7 +540,7 @@ def bulk_action_choices(context, hook_name):
button_classes={'button', 'button-small'},
buttons_data=[{
'label': action.display_name,
'url': reverse(corresponding_url, args=[action.action_type]) + '?' + urlencode({'next': action.next_url}),
'url': reverse('wagtail_bulk_action', args=[app_label, model_name, action.action_type]) + '?' + urlencode({'next': next_url}),
'attrs': {'aria-label': action.aria_label},
'priority': action.action_priority,
'classes': {'bulk-action-btn'},

Wyświetl plik

@ -43,7 +43,7 @@ class TestBulkDelete(TestCase, WagtailTestUtils):
for grandchild_page in grandchild_pages:
child_page.add_child(instance=grandchild_page)
self.url = reverse('wagtailadmin_page_bulk_action', args=('delete', )) + '?'
self.url = reverse('wagtail_bulk_action', args=('wagtailcore', 'page', 'delete', )) + '?'
for child_page in self.pages_to_be_deleted:
self.url += f'&id={child_page.id}'

Wyświetl plik

@ -58,7 +58,7 @@ class TestBulkMove(TestCase, WagtailTestUtils):
self.pages_to_be_moved = [self.test_page_b, self.test_page_b_1]
self.url = reverse('wagtailadmin_page_bulk_action', args=('move', )) + f'?id={self.test_page_b.id}&id={self.test_page_b_1.id}'
self.url = reverse('wagtail_bulk_action', args=('wagtailcore', 'page', 'move', )) + f'?id={self.test_page_b.id}&id={self.test_page_b_1.id}'
# Login
self.user = self.login()
@ -111,7 +111,7 @@ class TestBulkMove(TestCase, WagtailTestUtils):
self.assertFalse(can_bulk_delete)
response = self.client.get(
reverse('wagtailadmin_page_bulk_action', args=('move', )) + f'?id={self.unpublished_page.id}'
reverse('wagtail_bulk_action', args=('wagtailcore', 'page', 'move', )) + f'?id={self.unpublished_page.id}'
)
self.assertEqual(response.status_code, 200)

Wyświetl plik

@ -27,7 +27,7 @@ class TestBulkPublish(TestCase, WagtailTestUtils):
for child_page in self.child_pages:
self.root_page.add_child(instance=child_page)
self.url = reverse('wagtailadmin_page_bulk_action', args=('publish', )) + '?'
self.url = reverse('wagtail_bulk_action', args=('wagtailcore', 'page', 'publish', )) + '?'
for child_page in self.pages_to_be_published:
self.url += f'id={child_page.id}&'
self.redirect_url = reverse('wagtailadmin_explore', args=(self.root_page.id, ))
@ -50,7 +50,7 @@ class TestBulkPublish(TestCase, WagtailTestUtils):
This tests that the publish view returns an error if the page id is invalid
"""
# Request confirm publish page but with illegal page id
response = self.client.get(reverse('wagtailadmin_page_bulk_action', args=('publish', )))
response = self.client.get(reverse('wagtail_bulk_action', args=('wagtailcore', 'page', 'publish', )))
# Check that the user received a 404 response
self.assertEqual(response.status_code, 404)
@ -189,7 +189,7 @@ class TestBulkPublishIncludingDescendants(TestCase, WagtailTestUtils):
for grandchild_page in grandchild_pages:
child_page.add_child(instance=grandchild_page)
self.url = reverse('wagtailadmin_page_bulk_action', args=('publish', )) + '?'
self.url = reverse('wagtail_bulk_action', args=('wagtailcore', 'page', 'publish', )) + '?'
for child_page in self.pages_to_be_published:
self.url += f'&id={child_page.id}'

Wyświetl plik

@ -27,7 +27,7 @@ class TestBulkUnpublish(TestCase, WagtailTestUtils):
for child_page in self.child_pages:
self.root_page.add_child(instance=child_page)
self.url = reverse('wagtailadmin_page_bulk_action', args=('unpublish', )) + '?'
self.url = reverse('wagtail_bulk_action', args=('wagtailcore', 'page', 'unpublish', )) + '?'
for child_page in self.pages_to_be_unpublished:
self.url += f'&id={child_page.id}'
self.redirect_url = reverse('wagtailadmin_explore', args=(self.root_page.id, ))
@ -51,7 +51,7 @@ class TestBulkUnpublish(TestCase, WagtailTestUtils):
This tests that the unpublish view returns an error if the page id is invalid
"""
# Request confirm unpublish page but with illegal page id
response = self.client.get(reverse('wagtailadmin_page_bulk_action', args=('unpublish', )))
response = self.client.get(reverse('wagtail_bulk_action', args=('wagtailcore', 'page', 'unpublish', )))
# Check that the user received a 404 response
self.assertEqual(response.status_code, 404)
@ -191,7 +191,7 @@ class TestBulkUnpublishIncludingDescendants(TestCase, WagtailTestUtils):
for grandchild_page in grandchild_pages:
child_page.add_child(instance=grandchild_page)
self.url = reverse('wagtailadmin_page_bulk_action', args=('unpublish', )) + '?'
self.url = reverse('wagtail_bulk_action', args=('wagtailcore', 'page', 'unpublish', )) + '?'
for child_page in self.pages_to_be_unpublished:
self.url += f'&id={child_page.id}'
self.redirect_url = reverse('wagtailadmin_explore', args=(self.root_page.id, ))

Wyświetl plik

@ -16,8 +16,8 @@ from wagtail.admin.urls import password_reset as wagtailadmin_password_reset_url
from wagtail.admin.urls import reports as wagtailadmin_reports_urls
from wagtail.admin.urls import workflows as wagtailadmin_workflows_urls
from wagtail.admin.views import account, chooser, home, tags, userbar
from wagtail.admin.views.bulk_action import index as bulk_actions
from wagtail.admin.views.pages import listing
from wagtail.admin.views.pages.bulk_actions import index as bulk_actions
from wagtail.core import hooks
from wagtail.utils.urlpatterns import decorate_urlpatterns
@ -36,7 +36,7 @@ urlpatterns = [
path('pages/<int:parent_page_id>/', listing.index, name='wagtailadmin_explore'),
# bulk actions
path('pages/multiple/<str:action>/', bulk_actions, name='wagtailadmin_page_bulk_action'),
path('bulk/<str:app_label>/<str:model_name>/<str:action>/', bulk_actions, name='wagtail_bulk_action'),
path('pages/', include(wagtailadmin_pages_urls, namespace='wagtailadmin_pages')),

Wyświetl plik

@ -0,0 +1,5 @@
from .base_bulk_action import BulkAction
from .dispatcher import index
__all__ = ['BulkAction', 'index']

Wyświetl plik

@ -28,24 +28,28 @@ class BulkAction(ABC, FormView):
extras = dict()
action_priority = 100
model = None
models = []
object_key = 'object'
classes = set()
form_class = forms.Form
cleaned_form = None
def __init__(self, request):
def __init__(self, request, model):
self.request = request
next_url = get_valid_next_url_from_request(request)
if not next_url:
next_url = request.path
self.next_url = next_url
self.num_parent_objects = self.num_child_objects = 0
if model in self.models:
self.model = model
else:
raise Exception("model {} is not among the specified list of models".format(model.__class__.__name__))
@classmethod
def get_queryset(cls, object_ids):
if cls.model is None:
raise Exception("model should be provided")
return get_list_or_404(cls.model, pk__in=object_ids)
def get_queryset(cls, model, object_ids):
return get_list_or_404(model, pk__in=object_ids)
def check_perm(self, obj):
return True
@ -62,6 +66,12 @@ class BulkAction(ABC, FormView):
'item': obj
}
@classmethod
def get_default_model(cls):
if len(cls.models) == 1:
return cls.models[0]
raise Exception("Cannot get default model if number of models is greater than 1")
def __run_before_hooks(self, action_type, request, objects):
for hook in hooks.get_hooks('before_bulk_action'):
result = hook(request, action_type, objects, self)
@ -82,7 +92,7 @@ class BulkAction(ABC, FormView):
parent_page_id = int(self.request.GET.get('childOf'))
object_ids = self.model.objects.get(id=parent_page_id).get_children().values_list('id', flat=True)
for obj in self.get_queryset(object_ids):
for obj in self.get_queryset(self.model, object_ids):
if not self.check_perm(obj):
items_with_no_access.append(obj)
else:

Wyświetl plik

@ -0,0 +1,12 @@
from django.apps import apps
from django.http.response import Http404
from wagtail.admin.views.bulk_action.registry import bulk_action_registry as registry
def index(request, app_label, model_name, action):
model = apps.get_model(app_label, model_name)
action_class = registry.get_bulk_action_class(app_label, model_name, action)
if action_class is not None:
return action_class(request, model).dispatch(request)
return Http404()

Wyświetl plik

@ -0,0 +1,30 @@
from wagtail.admin.views.bulk_action import BulkAction
from wagtail.core import hooks
class BulkActionRegistry:
def __init__(self):
self.actions = dict() # {app_name: {model_name: {action_name: action_class]}}
self.has_scanned_for_bulk_actions = False
def _scan_for_bulk_actions(self):
if not self.has_scanned_for_bulk_actions:
for action_class in hooks.get_hooks('register_bulk_action'):
if not issubclass(action_class, BulkAction):
raise Exception("{} is not a subclass of {}".format(action_class.__name__, BulkAction.__name__))
for model in action_class.models:
self.actions.setdefault(model._meta.app_label, {})
self.actions[model._meta.app_label].setdefault(model._meta.model_name, {})
self.actions[model._meta.app_label][model._meta.model_name][action_class.action_type] = action_class
self.has_scanned_for_bulk_actions = True
def get_bulk_actions_for_model(self, app_label, model_name):
self._scan_for_bulk_actions()
return self.actions.get(app_label, {}).get(model_name, {}).values()
def get_bulk_action_class(self, app_label, model_name, action_type):
self._scan_for_bulk_actions()
return self.actions.get(app_label, {}).get(model_name, {}).get(action_type, None)
bulk_action_registry = BulkActionRegistry()

Wyświetl plik

@ -1,8 +1,7 @@
from .delete import delete
from .dispatcher import index
from .move import move
from .publish import publish
from .unpublish import unpublish
from .delete import DeleteBulkAction
from .move import MoveBulkAction
from .publish import PublishBulkAction
from .unpublish import UnpublishBulkAction
__all__ = ['delete', 'index', 'move', 'publish', 'unpublish']
__all__ = ['DeleteBulkAction', 'MoveBulkAction', 'PublishBulkAction', 'UnpublishBulkAction']

Wyświetl plik

@ -2,7 +2,6 @@ from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext
from wagtail.admin.views.pages.bulk_actions.page_bulk_action import PageBulkAction
from wagtail.core import hooks
class DeleteBulkAction(PageBulkAction):
@ -56,8 +55,3 @@ class DeleteBulkAction(PageBulkAction):
'num_parent_objects': num_parent_objects
}
return success_message
@hooks.register('register_page_bulk_action')
def delete(request):
return DeleteBulkAction(request)

Wyświetl plik

@ -1,14 +0,0 @@
from django.http.response import Http404
from wagtail.core import hooks
def index(request, action):
bulk_actions = hooks.get_hooks('register_page_bulk_action')
for action_func in bulk_actions:
_action = action_func(request)
if _action.action_type != action:
continue
return _action.dispatch(request)
return Http404()

Wyświetl plik

@ -5,7 +5,6 @@ from django.utils.translation import ngettext
from wagtail.admin import widgets
from wagtail.admin.views.pages.bulk_actions.page_bulk_action import PageBulkAction
from wagtail.core import hooks
from wagtail.core.models import Page
@ -113,8 +112,3 @@ class MoveBulkAction(PageBulkAction):
page.move(destination, pos='last-child', user=user)
num_parent_objects += 1
return num_parent_objects, 0
@hooks.register('register_page_bulk_action')
def move(request):
return MoveBulkAction(request)

Wyświetl plik

@ -9,7 +9,8 @@ class DefaultPageForm(forms.Form):
class PageBulkAction(BulkAction):
model = Page
models = [Page]
object_key = 'page'
form_class = DefaultPageForm
def get_context_data(self, **kwargs):

Wyświetl plik

@ -2,7 +2,6 @@ from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext
from wagtail.admin.views.pages.bulk_actions.page_bulk_action import PageBulkAction
from wagtail.core import hooks
class PublishBulkAction(PageBulkAction):
@ -79,8 +78,3 @@ class PublishBulkAction(PageBulkAction):
else:
success_message = _("%(num_parent_objects)d pages have been published") % {'num_parent_objects': num_parent_objects}
return success_message
@hooks.register('register_page_bulk_action')
def publish(request):
return PublishBulkAction(request)

Wyświetl plik

@ -2,7 +2,6 @@ from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext
from wagtail.admin.views.pages.bulk_actions.page_bulk_action import PageBulkAction
from wagtail.core import hooks
class UnpublishBulkAction(PageBulkAction):
@ -79,8 +78,3 @@ class UnpublishBulkAction(PageBulkAction):
else:
success_message = _("%(num_parent_objects)d pages have been unpublished") % {'num_parent_objects': num_parent_objects}
return success_message
@hooks.register('register_page_bulk_action')
def unpublish(request):
return UnpublishBulkAction(request)

Wyświetl plik

@ -26,6 +26,8 @@ from wagtail.admin.search import SearchArea
from wagtail.admin.site_summary import PagesSummaryItem
from wagtail.admin.ui.sidebar import PageExplorerMenuItem as PageExplorerMenuItemComponent
from wagtail.admin.ui.sidebar import SubMenuItem as SubMenuItemComponent
from wagtail.admin.views.pages.bulk_actions import (
DeleteBulkAction, MoveBulkAction, PublishBulkAction, UnpublishBulkAction)
from wagtail.admin.viewsets import viewsets
from wagtail.admin.widgets import Button, ButtonWithDropdownFromHook, PageListingButton
from wagtail.core import hooks
@ -859,3 +861,7 @@ class WorkflowTaskAdminURLFinder(ModelAdminURLFinder):
register_admin_url_finder(Task, WorkflowTaskAdminURLFinder)
for action_class in [DeleteBulkAction, MoveBulkAction, PublishBulkAction, UnpublishBulkAction]:
hooks.register('register_bulk_action', action_class)

Wyświetl plik

@ -1,6 +1,6 @@
from django.urls import path
from wagtail.documents.views import bulk_actions, chooser, documents, multiple
from wagtail.documents.views import chooser, documents, multiple
app_name = 'wagtaildocs'
@ -22,6 +22,4 @@ urlpatterns = [
path('chooser/<int:document_id>/', chooser.document_chosen, name='document_chosen'),
path('chooser/upload/', chooser.chooser_upload, name='chooser_upload'),
path('usage/<int:document_id>/', documents.usage, name='document_usage'),
path('multiple/<str:action>/', bulk_actions.index, name='document_bulk_action'),
]

Wyświetl plik

@ -47,5 +47,5 @@
</div>
</div>
{% trans "Select all documents in listing" as select_all_text %}
{% include 'wagtailadmin/bulk_actions/footer.html' with select_all_obj_text=select_all_text bulk_action_register_hook='register_document_bulk_action' %}
{% include 'wagtailadmin/bulk_actions/footer.html' with select_all_obj_text=select_all_text app_label='wagtaildocs' model_name='document' %}
{% endblock %}

Wyświetl plik

@ -20,7 +20,7 @@ class TestBulkAddTags(TestCase, WagtailTestUtils):
self.documents = [
Document.objects.create(title=f"Test document - {i}") for i in range(1, 6)
]
self.url = reverse('wagtaildocs:document_bulk_action', args=('add_tags',)) + '?'
self.url = reverse('wagtail_bulk_action', args=('wagtaildocs', 'document', 'add_tags',)) + '?'
for document in self.documents:
self.url += f'id={document.id}&'
self.post_data = {'tags': ','.join(self.new_tags)}

Wyświetl plik

@ -18,7 +18,7 @@ class TestBulkAddDocumentsToCollection(TestCase, WagtailTestUtils):
self.documents = [
Document.objects.create(title=f"Test document - {i}") for i in range(1, 6)
]
self.url = reverse('wagtaildocs:document_bulk_action', args=('add_to_collection',)) + '?'
self.url = reverse('wagtail_bulk_action', args=('wagtaildocs', 'document', 'add_to_collection',)) + '?'
for document in self.documents:
self.url += f'id={document.id}&'
self.post_data = {'collection': str(self.dest_collection.id)}

Wyświetl plik

@ -18,7 +18,7 @@ class TestDocumentBulkDeleteView(TestCase, WagtailTestUtils):
self.documents = [
Document.objects.create(title=f"Test document - {i}") for i in range(1, 6)
]
self.url = reverse('wagtaildocs:document_bulk_action', args=('delete',)) + '?'
self.url = reverse('wagtail_bulk_action', args=('wagtaildocs', 'document', 'delete',)) + '?'
for document in self.documents:
self.url += f'id={document.id}&'

Wyświetl plik

@ -1,7 +1,6 @@
from .add_tags import add_tags
from .add_to_collection import add_to_collection
from .delete import delete
from .dispatcher import index
from .add_tags import AddTagsBulkAction
from .add_to_collection import AddToCollectionBulkAction
from .delete import DeleteBulkAction
__all__ = ['add_to_collection', 'add_tags', 'delete', 'index']
__all__ = ['AddToCollectionBulkAction', 'AddTagsBulkAction', 'DeleteBulkAction']

Wyświetl plik

@ -3,7 +3,6 @@ from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext
from wagtail.admin import widgets
from wagtail.core import hooks
from wagtail.documents.views.bulk_actions.document_bulk_action import DocumentBulkAction
@ -45,8 +44,3 @@ class AddTagsBulkAction(DocumentBulkAction):
) % {
'num_parent_objects': num_parent_objects
}
@hooks.register('register_document_bulk_action')
def add_tags(request):
return AddTagsBulkAction(request)

Wyświetl plik

@ -2,7 +2,6 @@ from django import forms
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext
from wagtail.core import hooks
from wagtail.documents.views.bulk_actions.document_bulk_action import DocumentBulkAction
@ -42,7 +41,7 @@ class AddToCollectionBulkAction(DocumentBulkAction):
def execute_action(cls, objects, collection=None, **kwargs):
if collection is None:
return
num_parent_objects = cls.model.objects.filter(pk__in=[obj.pk for obj in objects]).update(collection=collection)
num_parent_objects = cls.get_default_model().objects.filter(pk__in=[obj.pk for obj in objects]).update(collection=collection)
return num_parent_objects, 0
def get_success_message(self, num_parent_objects, num_child_objects):
@ -55,8 +54,3 @@ class AddToCollectionBulkAction(DocumentBulkAction):
'num_parent_objects': num_parent_objects,
'collection': collection
}
@hooks.register('register_document_bulk_action')
def add_to_collection(request):
return AddToCollectionBulkAction(request)

Wyświetl plik

@ -1,7 +1,6 @@
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext
from wagtail.core import hooks
from wagtail.documents.views.bulk_actions.document_bulk_action import DocumentBulkAction
@ -19,7 +18,7 @@ class DeleteBulkAction(DocumentBulkAction):
@classmethod
def execute_action(cls, objects, **kwargs):
num_parent_objects = len(objects)
cls.model.objects.filter(pk__in=[obj.pk for obj in objects]).delete()
cls.get_default_model().objects.filter(pk__in=[obj.pk for obj in objects]).delete()
return num_parent_objects, 0
def get_success_message(self, num_parent_objects, num_child_objects):
@ -30,8 +29,3 @@ class DeleteBulkAction(DocumentBulkAction):
) % {
'num_parent_objects': num_parent_objects
}
@hooks.register('register_document_bulk_action')
def delete(request):
return DeleteBulkAction(request)

Wyświetl plik

@ -1,14 +0,0 @@
from django.http.response import Http404
from wagtail.core import hooks
def index(request, action):
bulk_actions = hooks.get_hooks('register_document_bulk_action')
for action_func in bulk_actions:
_action = action_func(request)
if _action.action_type != action:
continue
return _action.dispatch(request)
return Http404()

Wyświetl plik

@ -5,7 +5,8 @@ from wagtail.documents.permissions import permission_policy as documents_permiss
class DocumentBulkAction(BulkAction):
permission_policy = documents_permission_policy
model = get_document_model()
models = [get_document_model()]
object_key = 'document'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)

Wyświetl plik

@ -24,6 +24,8 @@ from wagtail.documents.permissions import permission_policy
from wagtail.documents.rich_text import DocumentLinkHandler
from wagtail.documents.rich_text.contentstate import ContentstateDocumentLinkConversionRule
from wagtail.documents.rich_text.editor_html import EditorHTMLDocumentLinkConversionRule
from wagtail.documents.views.bulk_actions import (
AddTagsBulkAction, AddToCollectionBulkAction, DeleteBulkAction)
@hooks.register('register_admin_urls')
@ -195,3 +197,7 @@ class DocumentAdminURLFinder(ModelAdminURLFinder):
register_admin_url_finder(get_document_model(), DocumentAdminURLFinder)
for action_class in [AddTagsBulkAction, AddToCollectionBulkAction, DeleteBulkAction]:
hooks.register('register_bulk_action', action_class)

Wyświetl plik

@ -1,6 +1,6 @@
from django.urls import path
from wagtail.images.views import bulk_actions, chooser, images, multiple
from wagtail.images.views import chooser, images, multiple
app_name = 'wagtailimages'
@ -26,6 +26,4 @@ urlpatterns = [
path('chooser/<int:image_id>/', chooser.image_chosen, name='image_chosen'),
path('chooser/upload/', chooser.chooser_upload, name='chooser_upload'),
path('chooser/<int:image_id>/select_format/', chooser.chooser_select_format, name='chooser_select_format'),
path('multiple/<str:action>/', bulk_actions.index, name='image_bulk_action'),
]

Wyświetl plik

@ -69,7 +69,7 @@
{% include "wagtailimages/images/results.html" %}
</div>
{% trans "Select all images in listing" as select_all_text %}
{% include 'wagtailadmin/bulk_actions/footer.html' with select_all_obj_text=select_all_text bulk_action_register_hook='register_image_bulk_action' %}
{% include 'wagtailadmin/bulk_actions/footer.html' with select_all_obj_text=select_all_text app_label='wagtailimages' model_name='image' %}
</div>
{% endblock %}

Wyświetl plik

@ -22,7 +22,7 @@ class TestBulkAddTags(TestCase, WagtailTestUtils):
self.images = [
Image.objects.create(title=f"Test image - {i}", file=test_file) for i in range(1, 6)
]
self.url = reverse('wagtailimages:image_bulk_action', args=('add_tags',)) + '?'
self.url = reverse('wagtail_bulk_action', args=('wagtailimages', 'image', 'add_tags',)) + '?'
for image in self.images:
self.url += f'id={image.id}&'
self.post_data = {'tags': ','.join(self.new_tags)}

Wyświetl plik

@ -20,7 +20,7 @@ class TestBulkAddImagesToCollection(TestCase, WagtailTestUtils):
self.images = [
Image.objects.create(title=f"Test image - {i}", file=test_file) for i in range(1, 6)
]
self.url = reverse('wagtailimages:image_bulk_action', args=('add_to_collection',)) + '?'
self.url = reverse('wagtail_bulk_action', args=('wagtailimages', 'image', 'add_to_collection',)) + '?'
for image in self.images:
self.url += f'id={image.id}&'
self.post_data = {'collection': str(self.dest_collection.id)}

Wyświetl plik

@ -20,7 +20,7 @@ class TestImageBulkDeleteView(TestCase, WagtailTestUtils):
self.images = [
Image.objects.create(title=f"Test image - {i}", file=test_file) for i in range(1, 6)
]
self.url = reverse('wagtailimages:image_bulk_action', args=('delete',)) + '?'
self.url = reverse('wagtail_bulk_action', args=('wagtailimages', 'image', 'delete',)) + '?'
for image in self.images:
self.url += f'id={image.id}&'

Wyświetl plik

@ -1,7 +1,6 @@
from .add_tags import add_tags
from .add_to_collection import add_to_collection
from .delete import delete
from .dispatcher import index
from .add_tags import AddTagsBulkAction
from .add_to_collection import AddToCollectionBulkAction
from .delete import DeleteBulkAction
__all__ = ['add_tags', 'add_to_collection', 'delete', 'index']
__all__ = ['AddToCollectionBulkAction', 'AddTagsBulkAction', 'DeleteBulkAction']

Wyświetl plik

@ -3,7 +3,6 @@ from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext
from wagtail.admin import widgets
from wagtail.core import hooks
from wagtail.images.views.bulk_actions.image_bulk_action import ImageBulkAction
@ -45,8 +44,3 @@ class AddTagsBulkAction(ImageBulkAction):
) % {
'num_parent_objects': num_parent_objects
}
@hooks.register('register_image_bulk_action')
def add_tags(request):
return AddTagsBulkAction(request)

Wyświetl plik

@ -2,7 +2,6 @@ from django import forms
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext
from wagtail.core import hooks
from wagtail.images.views.bulk_actions.image_bulk_action import ImageBulkAction
@ -42,7 +41,7 @@ class AddToCollectionBulkAction(ImageBulkAction):
def execute_action(cls, images, collection=None, **kwargs):
if collection is None:
return
num_parent_objects = cls.model.objects.filter(pk__in=[obj.pk for obj in images]).update(collection=collection)
num_parent_objects = cls.get_default_model().objects.filter(pk__in=[obj.pk for obj in images]).update(collection=collection)
return num_parent_objects, 0
def get_success_message(self, num_parent_objects, num_child_objects):
@ -55,8 +54,3 @@ class AddToCollectionBulkAction(ImageBulkAction):
'num_parent_objects': num_parent_objects,
'collection': collection.name
}
@hooks.register('register_image_bulk_action')
def add_to_collection(request):
return AddToCollectionBulkAction(request)

Wyświetl plik

@ -1,7 +1,6 @@
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext
from wagtail.core import hooks
from wagtail.images.views.bulk_actions.image_bulk_action import ImageBulkAction
@ -19,7 +18,7 @@ class DeleteBulkAction(ImageBulkAction):
@classmethod
def execute_action(cls, objects, **kwargs):
num_parent_objects = len(objects)
cls.model.objects.filter(pk__in=[obj.pk for obj in objects]).delete()
cls.get_default_model().objects.filter(pk__in=[obj.pk for obj in objects]).delete()
return num_parent_objects, 0
def get_success_message(self, num_parent_objects, num_child_objects):
@ -30,8 +29,3 @@ class DeleteBulkAction(ImageBulkAction):
) % {
'num_parent_objects': num_parent_objects
}
@hooks.register('register_image_bulk_action')
def delete(request):
return DeleteBulkAction(request)

Wyświetl plik

@ -1,14 +0,0 @@
from django.http.response import Http404
from wagtail.core import hooks
def index(request, action):
bulk_actions = hooks.get_hooks('register_image_bulk_action')
for action_func in bulk_actions:
_action = action_func(request)
if _action.action_type != action:
continue
return _action.dispatch(request)
return Http404()

Wyświetl plik

@ -5,7 +5,8 @@ from wagtail.images.permissions import permission_policy as images_permission_po
class ImageBulkAction(BulkAction):
permission_policy = images_permission_policy
model = get_image_model()
models = [get_image_model()]
object_key = 'image'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)

Wyświetl plik

@ -20,6 +20,8 @@ from wagtail.images.permissions import permission_policy
from wagtail.images.rich_text import ImageEmbedHandler
from wagtail.images.rich_text.contentstate import ContentstateImageConversionRule
from wagtail.images.rich_text.editor_html import EditorHTMLImageConversionRule
from wagtail.images.views.bulk_actions import (
AddTagsBulkAction, AddToCollectionBulkAction, DeleteBulkAction)
@hooks.register('register_admin_urls')
@ -191,3 +193,7 @@ class ImageAdminURLFinder(ModelAdminURLFinder):
register_admin_url_finder(get_image_model(), ImageAdminURLFinder)
for action_class in [AddTagsBulkAction, AddToCollectionBulkAction, DeleteBulkAction]:
hooks.register('register_bulk_action', action_class)

Wyświetl plik

@ -32,6 +32,6 @@
{% include "wagtailusers/users/results.html" %}
</div>
{% trans "Select all users in listing" as select_all_text %}
{% include 'wagtailadmin/bulk_actions/footer.html' with select_all_obj_text=select_all_text bulk_action_register_hook='register_user_bulk_action' objects=users %}
{% include 'wagtailadmin/bulk_actions/footer.html' with select_all_obj_text=select_all_text app_label='auth' model_name='user' objects=users %}
</div>
{% endblock %}

Wyświetl plik

@ -8,6 +8,9 @@ from wagtail.tests.utils import WagtailTestUtils
from wagtail.users.views.bulk_actions.user_bulk_action import UserBulkAction
User = get_user_model()
class TestUserToggleActivityView(TestCase, WagtailTestUtils):
def setUp(self):
# create a set of test users
@ -20,7 +23,7 @@ class TestUserToggleActivityView(TestCase, WagtailTestUtils):
]
self.new_group = Group.objects.create(name='group')
self.current_user = self.login()
self.url = reverse('wagtailusers_users:user_bulk_action', args=('assign_role',)) + '?'
self.url = reverse('wagtail_bulk_action', args=(User._meta.app_label, User._meta.model_name, 'assign_role',)) + '?'
self.self_toggle_url = self.url + f'id={self.current_user.pk}'
for user in self.test_users:
self.url += f'id={user.pk}&'
@ -39,7 +42,7 @@ class TestUserToggleActivityView(TestCase, WagtailTestUtils):
# Check that the users were added to the new group
for user in self.test_users:
self.assertTrue(get_user_model().objects.get(email=user.email).groups.filter(name=self.new_group).exists())
self.assertTrue(User.objects.get(email=user.email).groups.filter(name=self.new_group).exists())
def test_user_cannot_mark_self_as_not_admin(self):
response = self.client.get(self.self_toggle_url)
@ -54,7 +57,7 @@ class TestUserToggleActivityView(TestCase, WagtailTestUtils):
self.assertInHTML(needle, html)
# Check user was not added to the group
self.assertFalse(get_user_model().objects.get(email=self.current_user.email).groups.filter(name=self.new_group).exists())
self.assertFalse(User.objects.get(email=self.current_user.email).groups.filter(name=self.new_group).exists())
def test_before_toggle_user_hook_post(self):
def hook_func(request, action_type, users, action_class_instance):
@ -72,7 +75,7 @@ class TestUserToggleActivityView(TestCase, WagtailTestUtils):
self.assertEqual(response.content, b"Overridden!")
for user in self.test_users:
self.assertFalse(get_user_model().objects.get(email=user.email).groups.filter(name=self.new_group).exists())
self.assertFalse(User.objects.get(email=user.email).groups.filter(name=self.new_group).exists())
def test_after_toggle_user_hook(self):
def hook_func(request, action_type, users, action_class_instance):
@ -90,4 +93,4 @@ class TestUserToggleActivityView(TestCase, WagtailTestUtils):
self.assertEqual(response.content, b"Overridden!")
for user in self.test_users:
self.assertTrue(get_user_model().objects.get(email=user.email).groups.filter(name=self.new_group).exists())
self.assertTrue(User.objects.get(email=user.email).groups.filter(name=self.new_group).exists())

Wyświetl plik

@ -7,6 +7,9 @@ from wagtail.tests.utils import WagtailTestUtils
from wagtail.users.views.bulk_actions.user_bulk_action import UserBulkAction
User = get_user_model()
class TestUserDeleteView(TestCase, WagtailTestUtils):
def setUp(self):
# create a set of test users
@ -24,7 +27,7 @@ class TestUserDeleteView(TestCase, WagtailTestUtils):
password='password'
)
self.current_user = self.login()
self.url = reverse('wagtailusers_users:user_bulk_action', args=('delete',)) + '?'
self.url = reverse('wagtail_bulk_action', args=(User._meta.app_label, User._meta.model_name, 'delete',)) + '?'
for user in self.test_users:
self.url += f'id={user.pk}&'
@ -44,7 +47,7 @@ class TestUserDeleteView(TestCase, WagtailTestUtils):
# Check that the users were deleted
for user in self.test_users:
self.assertFalse(get_user_model().objects.filter(email=user.email).exists())
self.assertFalse(User.objects.filter(email=user.email).exists())
def test_user_cannot_delete_self(self):
response = self.client.get(self.self_delete_url)
@ -61,7 +64,7 @@ class TestUserDeleteView(TestCase, WagtailTestUtils):
response = self.client.post(self.self_delete_url)
# Check user was not deleted
self.assertTrue(get_user_model().objects.filter(pk=self.current_user.pk).exists())
self.assertTrue(User.objects.filter(pk=self.current_user.pk).exists())
def test_user_can_delete_other_superuser(self):
response = self.client.get(self.superuser_delete_url)
@ -73,7 +76,7 @@ class TestUserDeleteView(TestCase, WagtailTestUtils):
self.assertEqual(response.status_code, 302)
# Check that the user was deleted
users = get_user_model().objects.filter(email=self.superuser.email)
users = User.objects.filter(email=self.superuser.email)
self.assertEqual(users.count(), 0)
def test_before_delete_user_hook_post(self):
@ -92,7 +95,7 @@ class TestUserDeleteView(TestCase, WagtailTestUtils):
self.assertEqual(response.content, b"Overridden!")
for user in self.test_users:
self.assertTrue(get_user_model().objects.filter(email=user.email).exists())
self.assertTrue(User.objects.filter(email=user.email).exists())
def test_after_delete_user_hook(self):
def hook_func(request, action_type, users, action_class_instance):
@ -109,4 +112,4 @@ class TestUserDeleteView(TestCase, WagtailTestUtils):
self.assertEqual(response.content, b"Overridden!")
for user in self.test_users:
self.assertFalse(get_user_model().objects.filter(email=user.email).exists())
self.assertFalse(User.objects.filter(email=user.email).exists())

Wyświetl plik

@ -7,6 +7,9 @@ from wagtail.tests.utils import WagtailTestUtils
from wagtail.users.views.bulk_actions.user_bulk_action import UserBulkAction
User = get_user_model()
class TestUserToggleActivityView(TestCase, WagtailTestUtils):
def setUp(self):
# create a set of test users
@ -21,7 +24,7 @@ class TestUserToggleActivityView(TestCase, WagtailTestUtils):
user.is_active = (i & 1) # odd numbered users will be active
user.save()
self.current_user = self.login()
self.url = reverse('wagtailusers_users:user_bulk_action', args=('toggle_activity',)) + '?'
self.url = reverse('wagtail_bulk_action', args=(User._meta.app_label, User._meta.model_name, 'toggle_activity',)) + '?'
self.self_toggle_url = self.url + f'id={self.current_user.pk}'
for user in self.test_users:
self.url += f'id={user.pk}&'
@ -41,7 +44,7 @@ class TestUserToggleActivityView(TestCase, WagtailTestUtils):
# Check that the users were marked as active
for user in self.test_users:
self.assertTrue(get_user_model().objects.get(email=user.email).is_active)
self.assertTrue(User.objects.get(email=user.email).is_active)
def test_user_cannot_mark_self_as_inactive(self):
response = self.client.get(self.self_toggle_url)
@ -56,7 +59,7 @@ class TestUserToggleActivityView(TestCase, WagtailTestUtils):
self.assertInHTML(needle, html)
# Check user was not marked as inactive
self.assertTrue(get_user_model().objects.get(pk=self.current_user.pk).is_active)
self.assertTrue(User.objects.get(pk=self.current_user.pk).is_active)
def test_before_toggle_user_hook_post(self):
def hook_func(request, action_type, users, action_class_instance):
@ -89,4 +92,4 @@ class TestUserToggleActivityView(TestCase, WagtailTestUtils):
self.assertEqual(response.content, b"Overridden!")
for user in self.test_users:
self.assertTrue(get_user_model().objects.get(email=user.email).is_active)
self.assertTrue(User.objects.get(email=user.email).is_active)

Wyświetl plik

@ -1,12 +1,11 @@
from django.urls import path
from wagtail.users.views import bulk_actions, users
from wagtail.users.views import users
app_name = 'wagtailusers_users'
urlpatterns = [
path('', users.index, name='index'),
path('multiple/<str:action>/', bulk_actions.index, name='user_bulk_action'),
path('add/', users.create, name='add'),
path('<str:user_id>/', users.edit, name='edit'),
path('<str:user_id>/delete/', users.delete, name='delete'),

Wyświetl plik

@ -1,7 +1,6 @@
from .assign_role import assign_role
from .delete import delete
from .dispatcher import index
from .toggle_activity import toggle_activity
from .assign_role import AssignRoleBulkAction
from .delete import DeleteBulkAction
from .toggle_activity import ToggleActivityBulkAction
__all__ = ['assign_role', 'delete', 'index', 'toggle_activity']
__all__ = ['AssignRoleBulkAction', 'DeleteBulkAction', 'ToggleActivityBulkAction']

Wyświetl plik

@ -3,7 +3,6 @@ from django.contrib.auth.models import Group
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext
from wagtail.core import hooks
from wagtail.users.views.bulk_actions.user_bulk_action import UserBulkAction
from wagtail.users.views.users import change_user_perm
@ -55,8 +54,3 @@ class AssignRoleBulkAction(UserBulkAction):
'num_parent_objects': num_parent_objects,
'role': self.cleaned_form.cleaned_data['role'].name
}
@hooks.register('register_user_bulk_action')
def assign_role(request):
return AssignRoleBulkAction(request)

Wyświetl plik

@ -1,7 +1,6 @@
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext
from wagtail.core import hooks
from wagtail.users.utils import user_can_delete_user
from wagtail.users.views.bulk_actions.user_bulk_action import UserBulkAction
@ -17,11 +16,18 @@ class DeleteBulkAction(UserBulkAction):
def check_perm(self, obj):
return user_can_delete_user(self.request.user, obj)
def get_execution_context(self):
return {
**super().get_execution_context(),
'model': self.model
}
@classmethod
def execute_action(cls, objects, **kwargs):
cls.model.objects.filter(pk__in=[obj.pk for obj in objects]).delete()
num_parent_objects = len(objects)
return num_parent_objects, 0
def execute_action(cls, objects, model=None, **kwargs):
if model is None:
model = cls.get_default_model()
model.objects.filter(pk__in=[obj.pk for obj in objects]).delete()
return len(objects), 0
def get_success_message(self, num_parent_objects, num_child_objects):
return ngettext(
@ -31,8 +37,3 @@ class DeleteBulkAction(UserBulkAction):
) % {
'num_parent_objects': num_parent_objects
}
@hooks.register('register_user_bulk_action')
def delete(request):
return DeleteBulkAction(request)

Wyświetl plik

@ -1,14 +0,0 @@
from django.http.response import Http404
from wagtail.core import hooks
def index(request, action):
bulk_actions = hooks.get_hooks('register_user_bulk_action')
for action_func in bulk_actions:
_action = action_func(request)
if _action.action_type != action:
continue
return _action.dispatch(request)
return Http404()

Wyświetl plik

@ -2,7 +2,6 @@ from django import forms
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext
from wagtail.core import hooks
from wagtail.users.views.bulk_actions.user_bulk_action import UserBulkAction
from wagtail.users.views.users import change_user_perm
@ -28,7 +27,8 @@ class ToggleActivityBulkAction(UserBulkAction):
def get_execution_context(self):
return {
'mark_as_active': self.cleaned_form.cleaned_data['mark_as_active'],
'user': self.request.user
'user': self.request.user,
'model': self.model
}
def get_actionable_objects(self):
@ -40,10 +40,13 @@ class ToggleActivityBulkAction(UserBulkAction):
return users, objects_without_access
@classmethod
def execute_action(cls, objects, mark_as_active=False, user=None, **kwargs):
def execute_action(cls, objects, mark_as_active=False, model=None, **kwargs):
if model is None:
model = cls.get_default_model()
user = kwargs.get('user', None)
if user is not None:
objects = list(filter(lambda x: x.pk != user.pk, objects))
num_parent_objects = cls.model.objects.filter(pk__in=[obj.pk for obj in objects]).update(is_active=mark_as_active)
num_parent_objects = model.objects.filter(pk__in=[obj.pk for obj in objects]).update(is_active=mark_as_active)
return num_parent_objects, 0
def get_success_message(self, num_parent_objects, num_child_objects):
@ -63,8 +66,3 @@ class ToggleActivityBulkAction(UserBulkAction):
) % {
'num_parent_objects': num_parent_objects,
}
@hooks.register('register_user_bulk_action')
def toggle_activity(request):
return ToggleActivityBulkAction(request)

Wyświetl plik

@ -4,4 +4,5 @@ from wagtail.admin.views.bulk_action import BulkAction
class UserBulkAction(BulkAction):
model = get_user_model()
models = [get_user_model()]
object_key = 'user'

Wyświetl plik

@ -15,6 +15,8 @@ from wagtail.core.compat import AUTH_USER_APP_LABEL, AUTH_USER_MODEL_NAME
from wagtail.core.permission_policies import ModelPermissionPolicy
from wagtail.users.urls import users
from wagtail.users.utils import user_can_delete_user
from wagtail.users.views.bulk_actions import (
AssignRoleBulkAction, DeleteBulkAction, ToggleActivityBulkAction)
from wagtail.users.widgets import UserListingButton
@ -137,3 +139,7 @@ class UserAdminURLFinder(ModelAdminURLFinder):
register_admin_url_finder(User, UserAdminURLFinder)
for action_class in [AssignRoleBulkAction, DeleteBulkAction, ToggleActivityBulkAction]:
hooks.register('register_bulk_action', action_class)