108 lines
3.7 KiB
Python
108 lines
3.7 KiB
Python
|
|
import logging
|
||
|
|
|
||
|
|
from django.core.exceptions import PermissionDenied
|
||
|
|
from django.db import transaction
|
||
|
|
from treebeard.mp_tree import MP_MoveHandler
|
||
|
|
|
||
|
|
from wagtail.log_actions import log
|
||
|
|
from wagtail.signals import post_page_move, pre_page_move
|
||
|
|
|
||
|
|
logger = logging.getLogger("wagtail")
|
||
|
|
|
||
|
|
|
||
|
|
class MovePagePermissionError(PermissionDenied):
|
||
|
|
"""
|
||
|
|
Raised when the page move cannot be performed due to insufficient permissions.
|
||
|
|
"""
|
||
|
|
|
||
|
|
pass
|
||
|
|
|
||
|
|
|
||
|
|
class MovePageAction:
|
||
|
|
def __init__(self, page, target, pos=None, user=None):
|
||
|
|
self.page = page
|
||
|
|
self.target = target
|
||
|
|
self.pos = pos
|
||
|
|
self.user = user
|
||
|
|
|
||
|
|
def check(self, parent_after, skip_permission_checks=False):
|
||
|
|
if self.user and not skip_permission_checks:
|
||
|
|
if not self.page.permissions_for_user(self.user).can_move_to(parent_after):
|
||
|
|
raise MovePagePermissionError(
|
||
|
|
"You do not have permission to move the page to the target specified."
|
||
|
|
)
|
||
|
|
|
||
|
|
def _move_page(self, page, target, parent_after):
|
||
|
|
from wagtail.models import Page
|
||
|
|
|
||
|
|
# Determine old and new url_paths
|
||
|
|
# Fetching new object to avoid affecting `page`
|
||
|
|
parent_before = page.get_parent()
|
||
|
|
old_page = Page.objects.get(id=page.id)
|
||
|
|
old_url_path = old_page.url_path
|
||
|
|
new_url_path = old_page.set_url_path(parent=parent_after)
|
||
|
|
url_path_changed = old_url_path != new_url_path
|
||
|
|
|
||
|
|
# Emit pre_page_move signal
|
||
|
|
pre_page_move.send(
|
||
|
|
sender=page.specific_class or page.__class__,
|
||
|
|
instance=page,
|
||
|
|
parent_page_before=parent_before,
|
||
|
|
parent_page_after=parent_after,
|
||
|
|
url_path_before=old_url_path,
|
||
|
|
url_path_after=new_url_path,
|
||
|
|
)
|
||
|
|
|
||
|
|
# Only commit when all descendants are properly updated
|
||
|
|
with transaction.atomic():
|
||
|
|
# Allow treebeard to update `path` values
|
||
|
|
MP_MoveHandler(page, target, self.pos).process()
|
||
|
|
|
||
|
|
# Treebeard's move method doesn't actually update the in-memory instance,
|
||
|
|
# so we need to work with a freshly loaded one now
|
||
|
|
new_page = Page.objects.get(id=page.id)
|
||
|
|
new_page.url_path = new_url_path
|
||
|
|
new_page.save()
|
||
|
|
|
||
|
|
# Update descendant paths if url_path has changed
|
||
|
|
if url_path_changed:
|
||
|
|
new_page._update_descendant_url_paths(old_url_path, new_url_path)
|
||
|
|
|
||
|
|
# Emit post_page_move signal
|
||
|
|
post_page_move.send(
|
||
|
|
sender=page.specific_class or page.__class__,
|
||
|
|
instance=new_page,
|
||
|
|
parent_page_before=parent_before,
|
||
|
|
parent_page_after=parent_after,
|
||
|
|
url_path_before=old_url_path,
|
||
|
|
url_path_after=new_url_path,
|
||
|
|
)
|
||
|
|
|
||
|
|
# Log
|
||
|
|
log(
|
||
|
|
instance=page,
|
||
|
|
action="wagtail.move" if url_path_changed else "wagtail.reorder",
|
||
|
|
user=self.user,
|
||
|
|
data={
|
||
|
|
"source": {
|
||
|
|
"id": parent_before.id,
|
||
|
|
"title": parent_before.specific_deferred.get_admin_display_title(),
|
||
|
|
},
|
||
|
|
"destination": {
|
||
|
|
"id": parent_after.id,
|
||
|
|
"title": parent_after.specific_deferred.get_admin_display_title(),
|
||
|
|
},
|
||
|
|
},
|
||
|
|
)
|
||
|
|
logger.info('Page moved: "%s" id=%d path=%s', page.title, page.id, new_url_path)
|
||
|
|
|
||
|
|
def execute(self, skip_permission_checks=False):
|
||
|
|
if self.pos in ("first-child", "last-child", "sorted-child"):
|
||
|
|
parent_after = self.target
|
||
|
|
else:
|
||
|
|
parent_after = self.target.get_parent()
|
||
|
|
|
||
|
|
self.check(parent_after, skip_permission_checks=skip_permission_checks)
|
||
|
|
|
||
|
|
return self._move_page(self.page, self.target, parent_after)
|