From 46bebde476575637f07a4527cedc91df23f41bd6 Mon Sep 17 00:00:00 2001 From: Shohan Date: Sun, 6 Jun 2021 13:16:33 +0530 Subject: [PATCH] [feat] Add bulk action for move --- .../bulk_move_choose_destination.html | 20 ++++ .../pages/bulk_actions/confirm_bulk_move.html | 22 ++++ .../wagtailadmin/pages/listing/_list.html | 1 + .../pages/listing/_list_move.html | 1 + .../pages/listing/_navigation_move.html | 2 +- .../pages/listing/_page_title_move.html | 2 +- wagtail/admin/urls/__init__.py | 3 + wagtail/admin/views/bulk_actions/__init__.py | 3 +- wagtail/admin/views/bulk_actions/move.py | 109 ++++++++++++++++++ wagtail/admin/wagtail_hooks.py | 2 +- 10 files changed, 161 insertions(+), 4 deletions(-) create mode 100644 wagtail/admin/templates/wagtailadmin/pages/bulk_actions/bulk_move_choose_destination.html create mode 100644 wagtail/admin/templates/wagtailadmin/pages/bulk_actions/confirm_bulk_move.html create mode 100644 wagtail/admin/views/bulk_actions/move.py diff --git a/wagtail/admin/templates/wagtailadmin/pages/bulk_actions/bulk_move_choose_destination.html b/wagtail/admin/templates/wagtailadmin/pages/bulk_actions/bulk_move_choose_destination.html new file mode 100644 index 0000000000..c7547c2fc1 --- /dev/null +++ b/wagtail/admin/templates/wagtailadmin/pages/bulk_actions/bulk_move_choose_destination.html @@ -0,0 +1,20 @@ +{% extends "wagtailadmin/base.html" %} +{% load i18n wagtailadmin_tags %} +{% block titletag %}{% blocktrans with title=page_to_move.specific_deferred.get_admin_display_title %}Select a new parent page for {{ title }}{% endblocktrans %}{% endblock %} +{% block content %} +
+

+ {% icon name="doc-empty-inverse" class_name="header-title-icon" %} + {% blocktrans count counter=num_pages %} + Select a new parent page for 1 page + {% plural %} + Select a new parent page for {{ counter }} pages + {% endblocktrans %} +

+
+ +
+ {% include "wagtailadmin/pages/listing/_list_move.html" with pages=child_pages parent_page=viewed_page %} + {% paginate child_pages base_url=pagination_base_url %} +
+{% endblock %} diff --git a/wagtail/admin/templates/wagtailadmin/pages/bulk_actions/confirm_bulk_move.html b/wagtail/admin/templates/wagtailadmin/pages/bulk_actions/confirm_bulk_move.html new file mode 100644 index 0000000000..70b0921ca7 --- /dev/null +++ b/wagtail/admin/templates/wagtailadmin/pages/bulk_actions/confirm_bulk_move.html @@ -0,0 +1,22 @@ +{% extends "wagtailadmin/base.html" %} +{% load i18n %} +{% block titletag %}{% blocktrans count counter=num_pages %}Move 1 page{% plural %}Move {{ counter }} pages {% endblocktrans %} +{% endblock %} +{% block content %} + {% trans "Move" as move_str %} + {% include "wagtailadmin/shared/header.html" with title=move_str icon="doc-empty-inverse" %} +
+ {% for page_to_move in pages_to_move %} + {% if page_to_move.is_leaf %} +

{% blocktrans with title=destination.get_admin_display_title subtitle=page_to_move.get_admin_display_title %}Are you sure you want to move the page {{subtitle}} into '{{ title }}'?{% endblocktrans %}

+ {% else %} +

{% blocktrans with title=destination.get_admin_display_title subtitle=page_to_move.get_admin_display_title %}Are you sure you want to move the page {{subtitle}} and all of its children into '{{ title }}'?{% endblocktrans %}

+ {% endif %} + {% endfor %} + +
+ {% csrf_token %} + +
+
+{% endblock %} diff --git a/wagtail/admin/templates/wagtailadmin/pages/listing/_list.html b/wagtail/admin/templates/wagtailadmin/pages/listing/_list.html index c85d42bf82..05e247c334 100644 --- a/wagtail/admin/templates/wagtailadmin/pages/listing/_list.html +++ b/wagtail/admin/templates/wagtailadmin/pages/listing/_list.html @@ -18,6 +18,7 @@ {% if parent_page %} {% page_permissions parent_page as parent_page_perms %} + {% if is_moving %}{% endif %} {% block parent_page_title %} {% endblock %} diff --git a/wagtail/admin/templates/wagtailadmin/pages/listing/_list_move.html b/wagtail/admin/templates/wagtailadmin/pages/listing/_list_move.html index f338e7a02a..a84f0568e7 100644 --- a/wagtail/admin/templates/wagtailadmin/pages/listing/_list_move.html +++ b/wagtail/admin/templates/wagtailadmin/pages/listing/_list_move.html @@ -8,6 +8,7 @@ {% block pre_parent_page_headers %} + Title {% if show_parent %} {% trans 'Parent' %} diff --git a/wagtail/admin/templates/wagtailadmin/pages/listing/_navigation_move.html b/wagtail/admin/templates/wagtailadmin/pages/listing/_navigation_move.html index 10a4757353..ca9aeba7d5 100644 --- a/wagtail/admin/templates/wagtailadmin/pages/listing/_navigation_move.html +++ b/wagtail/admin/templates/wagtailadmin/pages/listing/_navigation_move.html @@ -6,6 +6,6 @@ Navigation controls for the page listing in 'move' mode {% if page.can_descend %} - {% trans 'Explore' %} + {% trans 'Explore' %} {% endif %} diff --git a/wagtail/admin/templates/wagtailadmin/pages/listing/_page_title_move.html b/wagtail/admin/templates/wagtailadmin/pages/listing/_page_title_move.html index 6073c56324..2996ce852a 100644 --- a/wagtail/admin/templates/wagtailadmin/pages/listing/_page_title_move.html +++ b/wagtail/admin/templates/wagtailadmin/pages/listing/_page_title_move.html @@ -6,7 +6,7 @@ Expects a variable 'page', the page instance.
{% if page.can_choose %} - {{ page.specific.get_admin_display_title }}{% block pages_listing_link_title_extra %}{% endblock pages_listing_link_title_extra %} + {{ page.specific.get_admin_display_title }}{% block pages_listing_link_title_extra %}{% endblock pages_listing_link_title_extra %} {% else %} {{ page.get_admin_display_title }}{% block pages_listing_title_extra %}{% endblock pages_listing_title_extra %} {% endif %} diff --git a/wagtail/admin/urls/__init__.py b/wagtail/admin/urls/__init__.py index 77d98bb766..62dce4835b 100644 --- a/wagtail/admin/urls/__init__.py +++ b/wagtail/admin/urls/__init__.py @@ -40,6 +40,9 @@ urlpatterns = [ path('pages//multiple/delete/', bulk_actions.delete, name='wagtailadmin_bulk_delete'), path('pages//multiple/unpublish/', bulk_actions.unpublish, name='wagtailadmin_bulk_unpublish'), path('pages//multiple/publish/', bulk_actions.publish, name='wagtailadmin_bulk_publish'), + path('pages//multiple/move/', bulk_actions.move, name='wagtailadmin_bulk_move'), + path('pages//multiple/move//', bulk_actions.move, name='wagtailadmin_bulk_move'), + path('pages//multiple/move//confirm/', bulk_actions.move_confirm, name='wagtailadmin_bulk_move_confirm'), path('pages/', include(wagtailadmin_pages_urls, namespace='wagtailadmin_pages')), diff --git a/wagtail/admin/views/bulk_actions/__init__.py b/wagtail/admin/views/bulk_actions/__init__.py index 205964d8b3..1243090d65 100644 --- a/wagtail/admin/views/bulk_actions/__init__.py +++ b/wagtail/admin/views/bulk_actions/__init__.py @@ -1,6 +1,7 @@ from .delete import delete +from .move import move, move_confirm from .publish import publish from .unpublish import unpublish -__all__ = ['delete', 'publish', 'unpublish'] +__all__ = ['delete', 'move', 'move_confirm', 'publish', 'unpublish'] diff --git a/wagtail/admin/views/bulk_actions/move.py b/wagtail/admin/views/bulk_actions/move.py new file mode 100644 index 0000000000..58cfdb5453 --- /dev/null +++ b/wagtail/admin/views/bulk_actions/move.py @@ -0,0 +1,109 @@ +from urllib.parse import urlencode + +from django.core.exceptions import PermissionDenied +from django.core.paginator import Paginator +from django.shortcuts import get_list_or_404, get_object_or_404, redirect +from django.template.response import TemplateResponse +from django.urls import reverse +from django.utils.translation import gettext_lazy as _ + +from wagtail.admin import messages +from wagtail.admin.views.pages.utils import get_valid_next_url_from_request +from wagtail.core import hooks +from wagtail.core.models import Page + + +def move(request, parent_page_id, dest_page_id=None): + next_url = get_valid_next_url_from_request(request) + if not next_url: + next_url = reverse('wagtailadmin_explore', args=[parent_page_id]) + + page_ids = list(map(int, request.GET.getlist('id'))) + child_pages = set() + if dest_page_id: + viewed_page = get_object_or_404(Page, id=dest_page_id) + else: + viewed_page = Page.get_first_root_node() + + for _page in get_list_or_404(Page, id__in=page_ids): + page_to_move = _page.specific + page_perms = page_to_move.permissions_for_user(request.user) + if not page_perms.can_move(): + raise PermissionDenied + viewed_page.can_choose = page_perms.can_move_to(viewed_page) + for target in viewed_page.get_children(): + target.can_choose = page_perms.can_move_to(target) + + target.can_descend = ( + not(target == page_to_move + or target.is_child_of(page_to_move)) + and target.get_children_count() + ) + + child_pages.add(target) + + child_pages = list(child_pages) + paginator = Paginator(child_pages, per_page=50) + child_pages = paginator.get_page(request.GET.get('p')) + print(len(child_pages)) + + if request.method == 'GET': + args = [parent_page_id] + if dest_page_id: + args.append(dest_page_id) + return TemplateResponse(request, 'wagtailadmin/pages/bulk_actions/bulk_move_choose_destination.html', { + 'num_pages': len(page_ids), + 'viewed_page': viewed_page, + 'child_pages': child_pages, + 'parent_page_id': parent_page_id, + 'page_ids': '?id=' + '&id='.join(request.GET.getlist('id')), + 'is_moving': True + }) + + +def move_confirm(request, parent_page_id, dest_page_id): + next_url = get_valid_next_url_from_request(request) + if not next_url: + next_url = reverse('wagtailadmin_explore', args=[parent_page_id]) + + destination = get_object_or_404(Page, id=dest_page_id).specific_deferred + + page_ids = list(map(int, request.GET.getlist('id'))) + pages_to_move = [] + for _page in get_list_or_404(Page, id__in=page_ids): + page_to_move = _page.specific + page_perms = page_to_move.permissions_for_user(request.user) + if not page_perms.can_move(): + raise PermissionDenied + if not page_perms.can_move_to(destination): + raise PermissionDenied + if not Page._slug_is_available(page_to_move.slug, destination, page=page_to_move): + messages.error( + request, + _("The slug '{0}' is already in use at the selected parent page. Make sure the slug is unique and try again").format(page_to_move.slug) + ) + return redirect('wagtailadmin_pages:move_choose_destination', page_to_move.id, dest_page_id) + for fn in hooks.get_hooks('before_move_page'): + result = fn(request, page_to_move, destination) + if hasattr(result, 'status_code'): + return result + pages_to_move.append(page_to_move) + + if request.method == 'GET': + return TemplateResponse(request, 'wagtailadmin/pages/bulk_actions/confirm_bulk_move.html', { + 'num_pages': len(page_ids), + 'pages_to_move': pages_to_move, + 'destination': destination, + 'submit_url': reverse('wagtailadmin_bulk_move_confirm', args=[parent_page_id, dest_page_id]) + + '?' + urlencode([('id', page_id) for page_id in page_ids]), + }) + else: + for page_to_move in pages_to_move: + page_to_move.move(destination, pos='last-child', user=request.user) + + for fn in hooks.get_hooks('after_move_page'): + result = fn(request, page_to_move) + if hasattr(result, 'status_code'): + return result + messages.success(request, _(f"{len(page_ids)} pages moved.")) + return redirect(next_url) diff --git a/wagtail/admin/wagtail_hooks.py b/wagtail/admin/wagtail_hooks.py index 9822a3cb6e..ed411996e5 100644 --- a/wagtail/admin/wagtail_hooks.py +++ b/wagtail/admin/wagtail_hooks.py @@ -189,7 +189,7 @@ def bulk_action_choices(page, is_parent=False, next_url=None): if page: yield PageListingButton( _('Move'), - reverse('wagtailadmin_bulk_delete', args=[page.id]) + '?' + urlencode({'next': next_url}), + reverse('wagtailadmin_bulk_move', args=[page.id]) + '?' + urlencode({'next': next_url}), attrs={'aria-label': _("Move pages")}, priority=10 )