Initial commit
This commit is contained in:
970
env/lib/python3.10/site-packages/wagtail/admin/views/pages/edit.py
vendored
Normal file
970
env/lib/python3.10/site-packages/wagtail/admin/views/pages/edit.py
vendored
Normal file
@@ -0,0 +1,970 @@
|
||||
import json
|
||||
from urllib.parse import quote
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.db.models import Prefetch, Q
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django.utils.html import format_html
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic.base import View
|
||||
|
||||
from wagtail.actions.publish_page_revision import PublishPageRevisionAction
|
||||
from wagtail.admin import messages
|
||||
from wagtail.admin.action_menu import PageActionMenu
|
||||
from wagtail.admin.mail import send_notification
|
||||
from wagtail.admin.models import EditingSession
|
||||
from wagtail.admin.ui.components import MediaContainer
|
||||
from wagtail.admin.ui.editing_sessions import EditingSessionsModule
|
||||
from wagtail.admin.ui.side_panels import (
|
||||
ChecksSidePanel,
|
||||
CommentsSidePanel,
|
||||
PageStatusSidePanel,
|
||||
PreviewSidePanel,
|
||||
)
|
||||
from wagtail.admin.utils import get_valid_next_url_from_request
|
||||
from wagtail.admin.views.generic import HookResponseMixin
|
||||
from wagtail.admin.views.generic.base import WagtailAdminTemplateMixin
|
||||
from wagtail.exceptions import PageClassNotFoundError
|
||||
from wagtail.locks import BasicLock, ScheduledForPublishLock, WorkflowLock
|
||||
from wagtail.models import (
|
||||
COMMENTS_RELATION_NAME,
|
||||
Comment,
|
||||
CommentReply,
|
||||
Page,
|
||||
PageSubscription,
|
||||
WorkflowState,
|
||||
get_default_page_content_type,
|
||||
)
|
||||
from wagtail.utils.timestamps import render_timestamp
|
||||
|
||||
|
||||
class EditView(WagtailAdminTemplateMixin, HookResponseMixin, View):
|
||||
def get_page_title(self):
|
||||
return _("Editing %(page_type)s") % {
|
||||
"page_type": self.page_class.get_verbose_name()
|
||||
}
|
||||
|
||||
def get_page_subtitle(self):
|
||||
return self.page.get_admin_display_title()
|
||||
|
||||
def get_template_names(self):
|
||||
if self.page.alias_of_id:
|
||||
return ["wagtailadmin/pages/edit_alias.html"]
|
||||
|
||||
else:
|
||||
return ["wagtailadmin/pages/edit.html"]
|
||||
|
||||
def add_save_confirmation_message(self):
|
||||
if self.is_reverting:
|
||||
message = _(
|
||||
"Page '%(page_title)s' has been replaced "
|
||||
"with version from %(previous_revision_datetime)s."
|
||||
) % {
|
||||
"page_title": self.page.get_admin_display_title(),
|
||||
"previous_revision_datetime": render_timestamp(
|
||||
self.previous_revision.created_at
|
||||
),
|
||||
}
|
||||
else:
|
||||
message = _("Page '%(page_title)s' has been updated.") % {
|
||||
"page_title": self.page.get_admin_display_title()
|
||||
}
|
||||
|
||||
messages.success(self.request, message)
|
||||
|
||||
def get_commenting_changes(self):
|
||||
"""
|
||||
Finds comments that have been changed during this request.
|
||||
|
||||
Returns a tuple of 5 lists:
|
||||
- New comments
|
||||
- Deleted comments
|
||||
- Resolved comments
|
||||
- Edited comments
|
||||
- Replied comments (dict containing the instance and list of replies)
|
||||
"""
|
||||
# Get changes
|
||||
comments_formset = self.form.formsets["comments"]
|
||||
new_comments = comments_formset.new_objects
|
||||
deleted_comments = comments_formset.deleted_objects
|
||||
|
||||
# Assume any changed comments that are resolved were only just resolved
|
||||
resolved_comments = []
|
||||
edited_comments = []
|
||||
for changed_comment, changed_fields in comments_formset.changed_objects:
|
||||
if changed_comment.resolved_at and "resolved" in changed_fields:
|
||||
resolved_comments.append(changed_comment)
|
||||
|
||||
if "text" in changed_fields:
|
||||
edited_comments.append(changed_comment)
|
||||
|
||||
new_replies = []
|
||||
deleted_replies = []
|
||||
edited_replies = []
|
||||
for comment_form in comments_formset.forms:
|
||||
# New
|
||||
replies = getattr(comment_form.formsets["replies"], "new_objects", [])
|
||||
if replies:
|
||||
new_replies.append((comment_form.instance, replies))
|
||||
|
||||
# Deleted
|
||||
replies = getattr(comment_form.formsets["replies"], "deleted_objects", [])
|
||||
if replies:
|
||||
deleted_replies.append((comment_form.instance, replies))
|
||||
|
||||
# Edited
|
||||
replies = getattr(comment_form.formsets["replies"], "changed_objects", [])
|
||||
replies = [
|
||||
reply for reply, changed_fields in replies if "text" in changed_fields
|
||||
]
|
||||
if replies:
|
||||
edited_replies.append((comment_form.instance, replies))
|
||||
|
||||
return {
|
||||
"new_comments": new_comments,
|
||||
"deleted_comments": deleted_comments,
|
||||
"resolved_comments": resolved_comments,
|
||||
"edited_comments": edited_comments,
|
||||
"new_replies": new_replies,
|
||||
"deleted_replies": deleted_replies,
|
||||
"edited_replies": edited_replies,
|
||||
}
|
||||
|
||||
def send_commenting_notifications(self, changes):
|
||||
"""
|
||||
Sends notifications about any changes to comments to anyone who is subscribed.
|
||||
"""
|
||||
relevant_comment_ids = []
|
||||
relevant_comment_ids.extend(
|
||||
comment.pk for comment in changes["resolved_comments"]
|
||||
)
|
||||
relevant_comment_ids.extend(
|
||||
comment.pk for comment, replies in changes["new_replies"]
|
||||
)
|
||||
|
||||
# Skip if no changes were made
|
||||
# Note: We don't email about edited comments so ignore those here
|
||||
if (
|
||||
not changes["new_comments"]
|
||||
and not changes["deleted_comments"]
|
||||
and not changes["resolved_comments"]
|
||||
and not changes["new_replies"]
|
||||
):
|
||||
return
|
||||
|
||||
# Get global page comment subscribers
|
||||
subscribers = PageSubscription.objects.filter(
|
||||
page=self.page, comment_notifications=True
|
||||
).select_related("user")
|
||||
global_recipient_users = [
|
||||
subscriber.user
|
||||
for subscriber in subscribers
|
||||
if subscriber.user != self.request.user
|
||||
]
|
||||
|
||||
# Get subscribers to individual threads
|
||||
replies = CommentReply.objects.filter(comment_id__in=relevant_comment_ids)
|
||||
comments = Comment.objects.filter(id__in=relevant_comment_ids)
|
||||
thread_users = (
|
||||
get_user_model()
|
||||
.objects.exclude(pk=self.request.user.pk)
|
||||
.exclude(pk__in=subscribers.values_list("user_id", flat=True))
|
||||
.filter(
|
||||
Q(comment_replies__comment_id__in=relevant_comment_ids)
|
||||
| Q(**{("%s__pk__in" % COMMENTS_RELATION_NAME): relevant_comment_ids})
|
||||
)
|
||||
.prefetch_related(
|
||||
Prefetch("comment_replies", queryset=replies),
|
||||
Prefetch(COMMENTS_RELATION_NAME, queryset=comments),
|
||||
)
|
||||
)
|
||||
|
||||
# Skip if no recipients
|
||||
if not (global_recipient_users or thread_users):
|
||||
return
|
||||
thread_users = [
|
||||
(
|
||||
user,
|
||||
set(
|
||||
list(user.comment_replies.values_list("comment_id", flat=True))
|
||||
+ list(
|
||||
getattr(user, COMMENTS_RELATION_NAME).values_list(
|
||||
"pk", flat=True
|
||||
)
|
||||
)
|
||||
),
|
||||
)
|
||||
for user in thread_users
|
||||
]
|
||||
mailed_users = set()
|
||||
|
||||
for current_user, current_threads in thread_users:
|
||||
# We are trying to avoid calling send_notification for each user for performance reasons
|
||||
# so group the users receiving the same thread notifications together here
|
||||
if current_user in mailed_users:
|
||||
continue
|
||||
users = [current_user]
|
||||
mailed_users.add(current_user)
|
||||
for user, threads in thread_users:
|
||||
if user not in mailed_users and threads == current_threads:
|
||||
users.append(user)
|
||||
mailed_users.add(user)
|
||||
send_notification(
|
||||
users,
|
||||
"updated_comments",
|
||||
{
|
||||
"page": self.page,
|
||||
"editor": self.request.user,
|
||||
"new_comments": [
|
||||
comment
|
||||
for comment in changes["new_comments"]
|
||||
if comment.pk in threads
|
||||
],
|
||||
"resolved_comments": [
|
||||
comment
|
||||
for comment in changes["resolved_comments"]
|
||||
if comment.pk in threads
|
||||
],
|
||||
"deleted_comments": [],
|
||||
"replied_comments": [
|
||||
{
|
||||
"comment": comment,
|
||||
"replies": replies,
|
||||
}
|
||||
for comment, replies in changes["new_replies"]
|
||||
if comment.pk in threads
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
return send_notification(
|
||||
global_recipient_users,
|
||||
"updated_comments",
|
||||
{
|
||||
"page": self.page,
|
||||
"editor": self.request.user,
|
||||
"new_comments": changes["new_comments"],
|
||||
"resolved_comments": changes["resolved_comments"],
|
||||
"deleted_comments": changes["deleted_comments"],
|
||||
"replied_comments": [
|
||||
{
|
||||
"comment": comment,
|
||||
"replies": replies,
|
||||
}
|
||||
for comment, replies in changes["new_replies"]
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
def log_commenting_changes(self, changes, revision):
|
||||
"""
|
||||
Generates log entries for any changes made to comments or replies.
|
||||
"""
|
||||
for comment in changes["new_comments"]:
|
||||
comment.log_create(page_revision=revision, user=self.request.user)
|
||||
|
||||
for comment in changes["edited_comments"]:
|
||||
comment.log_edit(page_revision=revision, user=self.request.user)
|
||||
|
||||
for comment in changes["resolved_comments"]:
|
||||
comment.log_resolve(page_revision=revision, user=self.request.user)
|
||||
|
||||
for comment in changes["deleted_comments"]:
|
||||
comment.log_delete(page_revision=revision, user=self.request.user)
|
||||
|
||||
for comment, replies in changes["new_replies"]:
|
||||
for reply in replies:
|
||||
reply.log_create(page_revision=revision, user=self.request.user)
|
||||
|
||||
for comment, replies in changes["edited_replies"]:
|
||||
for reply in replies:
|
||||
reply.log_edit(page_revision=revision, user=self.request.user)
|
||||
|
||||
for comment, replies in changes["deleted_replies"]:
|
||||
for reply in replies:
|
||||
reply.log_delete(page_revision=revision, user=self.request.user)
|
||||
|
||||
def get_edit_message_button(self):
|
||||
return messages.button(
|
||||
reverse("wagtailadmin_pages:edit", args=(self.page.id,)), _("Edit")
|
||||
)
|
||||
|
||||
def get_view_draft_message_button(self):
|
||||
return messages.button(
|
||||
reverse("wagtailadmin_pages:view_draft", args=(self.page.id,)),
|
||||
_("View draft"),
|
||||
new_window=False,
|
||||
)
|
||||
|
||||
def get_view_live_message_button(self):
|
||||
return messages.button(self.page.url, _("View live"), new_window=False)
|
||||
|
||||
def get_compare_with_live_message_button(self):
|
||||
return messages.button(
|
||||
reverse(
|
||||
"wagtailadmin_pages:revisions_compare",
|
||||
args=(self.page.id, "live", self.latest_revision.id),
|
||||
),
|
||||
_("Compare with live version"),
|
||||
)
|
||||
|
||||
def get_page_for_status(self):
|
||||
if self.page.live and self.page.has_unpublished_changes:
|
||||
# Page status needs to present the version of the page containing the correct live URL
|
||||
return self.real_page_record.specific
|
||||
else:
|
||||
return self.page
|
||||
|
||||
def dispatch(self, request, page_id):
|
||||
self.real_page_record = get_object_or_404(
|
||||
Page.objects.prefetch_workflow_states(), id=page_id
|
||||
)
|
||||
self.latest_revision = self.real_page_record.get_latest_revision()
|
||||
self.scheduled_revision = self.real_page_record.scheduled_revision
|
||||
self.page_content_type = self.real_page_record.cached_content_type
|
||||
self.page_class = self.real_page_record.specific_class
|
||||
|
||||
if self.page_class is None:
|
||||
raise PageClassNotFoundError(
|
||||
f"The page '{self.real_page_record}' cannot be edited because the "
|
||||
f"model class used to create it ({self.page_content_type.app_label}."
|
||||
f"{self.page_content_type.model}) can no longer be found in the codebase. "
|
||||
"This usually happens as a result of switching between git "
|
||||
"branches without running migrations to trigger the removal of "
|
||||
"unused ContentTypes. To edit the page, you will need to switch "
|
||||
"back to a branch where the model class is still present."
|
||||
)
|
||||
|
||||
self.page = self.real_page_record.get_latest_revision_as_object()
|
||||
self.parent = self.page.get_parent()
|
||||
self.scheduled_page = self.real_page_record.get_scheduled_revision_as_object()
|
||||
|
||||
self.page_perms = self.page.permissions_for_user(self.request.user)
|
||||
self.lock = self.page.get_lock()
|
||||
self.locked_for_user = self.lock is not None and self.lock.for_user(
|
||||
self.request.user
|
||||
)
|
||||
|
||||
if not self.page_perms.can_edit():
|
||||
raise PermissionDenied
|
||||
|
||||
self.next_url = get_valid_next_url_from_request(self.request)
|
||||
|
||||
response = self.run_hook("before_edit_page", self.request, self.page)
|
||||
if response:
|
||||
return response
|
||||
|
||||
self.subscription, created = PageSubscription.objects.get_or_create(
|
||||
page=self.page,
|
||||
user=self.request.user,
|
||||
defaults={
|
||||
"comment_notifications": False,
|
||||
},
|
||||
)
|
||||
|
||||
self.edit_handler = self.page_class.get_edit_handler()
|
||||
self.form_class = self.edit_handler.get_form_class()
|
||||
|
||||
if getattr(settings, "WAGTAIL_WORKFLOW_ENABLED", True):
|
||||
# Retrieve current workflow state if set, default to last workflow state
|
||||
self.workflow_state = (
|
||||
self.page.current_workflow_state
|
||||
or self.page.workflow_states.order_by("created_at").last()
|
||||
)
|
||||
else:
|
||||
self.workflow_state = None
|
||||
|
||||
if getattr(settings, "WAGTAIL_I18N_ENABLED", False):
|
||||
self.locale = self.page.locale
|
||||
self.translations = self.get_translations()
|
||||
else:
|
||||
self.locale = None
|
||||
self.translations = []
|
||||
|
||||
if self.workflow_state:
|
||||
self.workflow_tasks = self.workflow_state.all_tasks_with_status()
|
||||
else:
|
||||
self.workflow_tasks = []
|
||||
|
||||
self.errors_debug = None
|
||||
|
||||
return super().dispatch(request)
|
||||
|
||||
def get(self, request):
|
||||
if self.lock:
|
||||
lock_message = self.lock.get_message(self.request.user)
|
||||
if lock_message:
|
||||
if isinstance(self.lock, BasicLock) and self.page_perms.can_unlock():
|
||||
lock_message = format_html(
|
||||
'{} <span class="buttons"><button type="button" class="button button-small button-secondary" data-action="w-action#post" data-controller="w-action" data-w-action-url-value="{}">{}</button></span>',
|
||||
lock_message,
|
||||
reverse("wagtailadmin_pages:unlock", args=(self.page.id,)),
|
||||
_("Unlock"),
|
||||
)
|
||||
|
||||
if (
|
||||
isinstance(self.lock, ScheduledForPublishLock)
|
||||
and self.page_perms.can_unschedule()
|
||||
):
|
||||
lock_message = format_html(
|
||||
'{} <span class="buttons"><button type="button" class="button button-small button-secondary" data-action="w-action#post" data-controller="w-action" data-w-action-url-value="{}">{}</button></span>',
|
||||
lock_message,
|
||||
reverse(
|
||||
"wagtailadmin_pages:revisions_unschedule",
|
||||
args=[self.page.id, self.scheduled_revision.pk],
|
||||
),
|
||||
_("Cancel scheduled publish"),
|
||||
)
|
||||
|
||||
if (
|
||||
not isinstance(self.lock, ScheduledForPublishLock)
|
||||
and self.locked_for_user
|
||||
):
|
||||
messages.warning(self.request, lock_message, extra_tags="lock")
|
||||
else:
|
||||
messages.info(self.request, lock_message, extra_tags="lock")
|
||||
|
||||
self.form = self.form_class(
|
||||
instance=self.page,
|
||||
subscription=self.subscription,
|
||||
parent_page=self.parent,
|
||||
for_user=self.request.user,
|
||||
)
|
||||
self.has_unsaved_changes = False
|
||||
self.page_for_status = self.get_page_for_status()
|
||||
|
||||
return self.render_to_response(self.get_context_data())
|
||||
|
||||
def add_cancel_workflow_confirmation_message(self):
|
||||
message = _("Workflow on page '%(page_title)s' has been cancelled.") % {
|
||||
"page_title": self.page.get_admin_display_title()
|
||||
}
|
||||
|
||||
messages.success(
|
||||
self.request,
|
||||
message,
|
||||
buttons=[
|
||||
self.get_view_draft_message_button(),
|
||||
self.get_edit_message_button(),
|
||||
],
|
||||
)
|
||||
|
||||
def post(self, request):
|
||||
# Don't allow POST requests if the page is an alias
|
||||
if self.page.alias_of_id:
|
||||
# Return 405 "Method Not Allowed" response
|
||||
return HttpResponse(status=405)
|
||||
|
||||
self.form = self.form_class(
|
||||
self.request.POST,
|
||||
self.request.FILES,
|
||||
instance=self.page,
|
||||
subscription=self.subscription,
|
||||
parent_page=self.parent,
|
||||
for_user=self.request.user,
|
||||
)
|
||||
|
||||
self.is_cancelling_workflow = (
|
||||
bool(self.request.POST.get("action-cancel-workflow"))
|
||||
and self.workflow_state
|
||||
and self.workflow_state.user_can_cancel(self.request.user)
|
||||
)
|
||||
|
||||
if self.form.is_valid() and not self.locked_for_user:
|
||||
return self.form_valid(self.form)
|
||||
else:
|
||||
return self.form_invalid(self.form)
|
||||
|
||||
def workflow_action_is_valid(self):
|
||||
self.workflow_action = self.request.POST["workflow-action-name"]
|
||||
available_actions = self.page.current_workflow_task.get_actions(
|
||||
self.page, self.request.user
|
||||
)
|
||||
available_action_names = [
|
||||
name for name, verbose_name, modal in available_actions
|
||||
]
|
||||
return self.workflow_action in available_action_names
|
||||
|
||||
def form_valid(self, form):
|
||||
self.is_reverting = bool(self.request.POST.get("revision"))
|
||||
# If a revision ID was passed in the form, get that revision so its
|
||||
# date can be referenced in notification messages
|
||||
if self.is_reverting:
|
||||
self.previous_revision = get_object_or_404(
|
||||
self.page.revisions, id=self.request.POST.get("revision")
|
||||
)
|
||||
|
||||
self.has_content_changes = self.form.has_changed()
|
||||
|
||||
if self.request.POST.get("action-publish") and self.page_perms.can_publish():
|
||||
return self.publish_action()
|
||||
elif (
|
||||
self.request.POST.get("action-submit")
|
||||
and self.page_perms.can_submit_for_moderation()
|
||||
):
|
||||
return self.submit_action()
|
||||
elif (
|
||||
self.request.POST.get("action-restart-workflow")
|
||||
and self.page_perms.can_submit_for_moderation()
|
||||
and self.workflow_state
|
||||
and self.workflow_state.user_can_cancel(self.request.user)
|
||||
):
|
||||
return self.restart_workflow_action()
|
||||
elif (
|
||||
self.request.POST.get("action-workflow-action")
|
||||
and self.workflow_action_is_valid()
|
||||
):
|
||||
return self.perform_workflow_action()
|
||||
elif self.is_cancelling_workflow:
|
||||
return self.cancel_workflow_action()
|
||||
else:
|
||||
return self.save_action()
|
||||
|
||||
def save_action(self):
|
||||
self.page = self.form.save(commit=not self.page.live)
|
||||
self.subscription.save()
|
||||
|
||||
# Save revision
|
||||
revision = self.page.save_revision(
|
||||
user=self.request.user,
|
||||
log_action=True, # Always log the new revision on edit
|
||||
previous_revision=(self.previous_revision if self.is_reverting else None),
|
||||
)
|
||||
|
||||
self.add_save_confirmation_message()
|
||||
|
||||
if self.has_content_changes and "comments" in self.form.formsets:
|
||||
changes = self.get_commenting_changes()
|
||||
self.log_commenting_changes(changes, revision)
|
||||
self.send_commenting_notifications(changes)
|
||||
|
||||
response = self.run_hook("after_edit_page", self.request, self.page)
|
||||
if response:
|
||||
return response
|
||||
|
||||
# Just saving - remain on edit page for further edits
|
||||
return self.redirect_and_remain()
|
||||
|
||||
def publish_action(self):
|
||||
self.page = self.form.save(commit=not self.page.live)
|
||||
self.subscription.save()
|
||||
|
||||
# Save revision
|
||||
revision = self.page.save_revision(
|
||||
user=self.request.user,
|
||||
log_action=True, # Always log the new revision on edit
|
||||
previous_revision=(self.previous_revision if self.is_reverting else None),
|
||||
)
|
||||
|
||||
# store submitted go_live_at for messaging below
|
||||
go_live_at = self.page.go_live_at
|
||||
|
||||
response = self.run_hook("before_publish_page", self.request, self.page)
|
||||
if response:
|
||||
return response
|
||||
|
||||
action = PublishPageRevisionAction(
|
||||
revision,
|
||||
user=self.request.user,
|
||||
changed=self.has_content_changes,
|
||||
previous_revision=(self.previous_revision if self.is_reverting else None),
|
||||
)
|
||||
action.execute(skip_permission_checks=True)
|
||||
|
||||
if self.has_content_changes and "comments" in self.form.formsets:
|
||||
changes = self.get_commenting_changes()
|
||||
self.log_commenting_changes(changes, revision)
|
||||
self.send_commenting_notifications(changes)
|
||||
|
||||
# Need to reload the page because the URL may have changed, and we
|
||||
# need the up-to-date URL for the "View Live" button.
|
||||
self.page = self.page.specific_class.objects.get(pk=self.page.pk)
|
||||
|
||||
response = self.run_hook("after_publish_page", self.request, self.page)
|
||||
if response:
|
||||
return response
|
||||
|
||||
# Notifications
|
||||
if go_live_at and go_live_at > timezone.now():
|
||||
# Page has been scheduled for publishing in the future
|
||||
|
||||
if self.is_reverting:
|
||||
message = _(
|
||||
"Version from %(previous_revision_datetime)s "
|
||||
"of page '%(page_title)s' has been scheduled for publishing."
|
||||
) % {
|
||||
"previous_revision_datetime": render_timestamp(
|
||||
self.previous_revision.created_at
|
||||
),
|
||||
"page_title": self.page.get_admin_display_title(),
|
||||
}
|
||||
else:
|
||||
if self.page.live:
|
||||
message = _(
|
||||
"Page '%(page_title)s' is live and this version has been scheduled for publishing."
|
||||
) % {"page_title": self.page.get_admin_display_title()}
|
||||
|
||||
else:
|
||||
message = _(
|
||||
"Page '%(page_title)s' has been scheduled for publishing."
|
||||
) % {"page_title": self.page.get_admin_display_title()}
|
||||
|
||||
messages.success(
|
||||
self.request, message, buttons=[self.get_edit_message_button()]
|
||||
)
|
||||
|
||||
else:
|
||||
# Page is being published now
|
||||
|
||||
if self.is_reverting:
|
||||
message = _(
|
||||
"Version from %(datetime)s of page '%(page_title)s' has been published."
|
||||
) % {
|
||||
"datetime": render_timestamp(self.previous_revision.created_at),
|
||||
"page_title": self.page.get_admin_display_title(),
|
||||
}
|
||||
else:
|
||||
message = _("Page '%(page_title)s' has been published.") % {
|
||||
"page_title": self.page.get_admin_display_title()
|
||||
}
|
||||
|
||||
buttons = []
|
||||
if self.page.url is not None:
|
||||
buttons.append(self.get_view_live_message_button())
|
||||
buttons.append(self.get_edit_message_button())
|
||||
messages.success(self.request, message, buttons=buttons)
|
||||
|
||||
response = self.run_hook("after_edit_page", self.request, self.page)
|
||||
if response:
|
||||
return response
|
||||
|
||||
# we're done here - redirect back to the explorer
|
||||
return self.redirect_away()
|
||||
|
||||
def submit_action(self):
|
||||
self.page = self.form.save(commit=not self.page.live)
|
||||
self.subscription.save()
|
||||
|
||||
# Save revision
|
||||
revision = self.page.save_revision(
|
||||
user=self.request.user,
|
||||
log_action=True, # Always log the new revision on edit
|
||||
previous_revision=(self.previous_revision if self.is_reverting else None),
|
||||
)
|
||||
|
||||
if self.has_content_changes and "comments" in self.form.formsets:
|
||||
changes = self.get_commenting_changes()
|
||||
self.log_commenting_changes(changes, revision)
|
||||
self.send_commenting_notifications(changes)
|
||||
|
||||
if (
|
||||
self.workflow_state
|
||||
and self.workflow_state.status == WorkflowState.STATUS_NEEDS_CHANGES
|
||||
):
|
||||
# If the workflow was in the needs changes state, resume the existing workflow on submission
|
||||
self.workflow_state.resume(self.request.user)
|
||||
else:
|
||||
# Otherwise start a new workflow
|
||||
workflow = self.page.get_workflow()
|
||||
workflow.start(self.page, self.request.user)
|
||||
|
||||
message = _("Page '%(page_title)s' has been submitted for moderation.") % {
|
||||
"page_title": self.page.get_admin_display_title()
|
||||
}
|
||||
|
||||
messages.success(
|
||||
self.request,
|
||||
message,
|
||||
buttons=[
|
||||
self.get_view_draft_message_button(),
|
||||
self.get_edit_message_button(),
|
||||
],
|
||||
)
|
||||
|
||||
response = self.run_hook("after_edit_page", self.request, self.page)
|
||||
if response:
|
||||
return response
|
||||
|
||||
# we're done here - redirect back to the explorer
|
||||
return self.redirect_away()
|
||||
|
||||
def restart_workflow_action(self):
|
||||
self.page = self.form.save(commit=not self.page.live)
|
||||
self.subscription.save()
|
||||
|
||||
# save revision
|
||||
revision = self.page.save_revision(
|
||||
user=self.request.user,
|
||||
log_action=True, # Always log the new revision on edit
|
||||
previous_revision=(self.previous_revision if self.is_reverting else None),
|
||||
)
|
||||
|
||||
if self.has_content_changes and "comments" in self.form.formsets:
|
||||
changes = self.get_commenting_changes()
|
||||
self.log_commenting_changes(changes, revision)
|
||||
self.send_commenting_notifications(changes)
|
||||
|
||||
# cancel workflow
|
||||
self.workflow_state.cancel(user=self.request.user)
|
||||
# start new workflow
|
||||
workflow = self.page.get_workflow()
|
||||
workflow.start(self.page, self.request.user)
|
||||
|
||||
message = _("Workflow on page '%(page_title)s' has been restarted.") % {
|
||||
"page_title": self.page.get_admin_display_title()
|
||||
}
|
||||
|
||||
messages.success(
|
||||
self.request,
|
||||
message,
|
||||
buttons=[
|
||||
self.get_view_draft_message_button(),
|
||||
self.get_edit_message_button(),
|
||||
],
|
||||
)
|
||||
|
||||
response = self.run_hook("after_edit_page", self.request, self.page)
|
||||
if response:
|
||||
return response
|
||||
|
||||
# we're done here - redirect back to the explorer
|
||||
return self.redirect_away()
|
||||
|
||||
def perform_workflow_action(self):
|
||||
self.page = self.form.save(commit=not self.page.live)
|
||||
self.subscription.save()
|
||||
|
||||
if self.has_content_changes:
|
||||
# Save revision
|
||||
revision = self.page.save_revision(
|
||||
user=self.request.user,
|
||||
log_action=True, # Always log the new revision on edit
|
||||
previous_revision=(
|
||||
self.previous_revision if self.is_reverting else None
|
||||
),
|
||||
)
|
||||
|
||||
if "comments" in self.form.formsets:
|
||||
changes = self.get_commenting_changes()
|
||||
self.log_commenting_changes(changes, revision)
|
||||
self.send_commenting_notifications(changes)
|
||||
|
||||
extra_workflow_data_json = self.request.POST.get(
|
||||
"workflow-action-extra-data", "{}"
|
||||
)
|
||||
extra_workflow_data = json.loads(extra_workflow_data_json)
|
||||
self.page.current_workflow_task.on_action(
|
||||
self.page.current_workflow_task_state,
|
||||
self.request.user,
|
||||
self.workflow_action,
|
||||
**extra_workflow_data,
|
||||
)
|
||||
|
||||
self.add_save_confirmation_message()
|
||||
|
||||
response = self.run_hook("after_edit_page", self.request, self.page)
|
||||
if response:
|
||||
return response
|
||||
|
||||
# we're done here - redirect back to the explorer
|
||||
return self.redirect_away()
|
||||
|
||||
def cancel_workflow_action(self):
|
||||
self.workflow_state.cancel(user=self.request.user)
|
||||
self.page = self.form.save(commit=not self.page.live)
|
||||
self.subscription.save()
|
||||
|
||||
# Save revision
|
||||
revision = self.page.save_revision(
|
||||
user=self.request.user,
|
||||
log_action=True, # Always log the new revision on edit
|
||||
previous_revision=(self.previous_revision if self.is_reverting else None),
|
||||
)
|
||||
|
||||
if self.has_content_changes and "comments" in self.form.formsets:
|
||||
changes = self.get_commenting_changes()
|
||||
self.log_commenting_changes(changes, revision)
|
||||
self.send_commenting_notifications(changes)
|
||||
|
||||
# Notifications
|
||||
self.add_cancel_workflow_confirmation_message()
|
||||
|
||||
response = self.run_hook("after_edit_page", self.request, self.page)
|
||||
if response:
|
||||
return response
|
||||
|
||||
# Just saving - remain on edit page for further edits
|
||||
return self.redirect_and_remain()
|
||||
|
||||
def redirect_away(self):
|
||||
if self.next_url:
|
||||
# redirect back to 'next' url if present
|
||||
return redirect(self.next_url)
|
||||
else:
|
||||
# redirect back to the explorer
|
||||
return redirect("wagtailadmin_explore", self.page.get_parent().id)
|
||||
|
||||
def redirect_and_remain(self):
|
||||
target_url = reverse("wagtailadmin_pages:edit", args=[self.page.id])
|
||||
if self.next_url:
|
||||
# Ensure the 'next' url is passed through again if present
|
||||
target_url += "?next=%s" % quote(self.next_url)
|
||||
return redirect(target_url)
|
||||
|
||||
def form_invalid(self, form):
|
||||
# even if the page is locked due to not having permissions, the original submitter can still cancel the workflow
|
||||
if self.is_cancelling_workflow:
|
||||
self.workflow_state.cancel(user=self.request.user)
|
||||
self.add_cancel_workflow_confirmation_message()
|
||||
|
||||
# Refresh the lock object as now WorkflowLock no longer applies
|
||||
self.lock = self.page.get_lock()
|
||||
self.locked_for_user = self.lock is not None and self.lock.for_user(
|
||||
self.request.user
|
||||
)
|
||||
elif self.locked_for_user:
|
||||
messages.error(
|
||||
self.request, _("The page could not be saved as it is locked")
|
||||
)
|
||||
else:
|
||||
messages.validation_error(
|
||||
self.request,
|
||||
_("The page could not be saved due to validation errors"),
|
||||
self.form,
|
||||
)
|
||||
self.errors_debug = repr(self.form.errors) + repr(
|
||||
[
|
||||
(name, formset.errors)
|
||||
for (name, formset) in self.form.formsets.items()
|
||||
if formset.errors
|
||||
]
|
||||
)
|
||||
self.has_unsaved_changes = True
|
||||
|
||||
self.page_for_status = self.get_page_for_status()
|
||||
|
||||
return self.render_to_response(self.get_context_data())
|
||||
|
||||
def get_preview_url(self):
|
||||
return reverse("wagtailadmin_pages:preview_on_edit", args=[self.page.id])
|
||||
|
||||
def get_history_url(self):
|
||||
permissions = self.page.permissions_for_user(self.request.user)
|
||||
if permissions.can_view_revisions():
|
||||
return reverse("wagtailadmin_pages:history", args=[self.page.id])
|
||||
|
||||
def get_side_panels(self):
|
||||
side_panels = [
|
||||
PageStatusSidePanel(
|
||||
self.page,
|
||||
self.request,
|
||||
show_schedule_publishing_toggle=self.form.show_schedule_publishing_toggle,
|
||||
live_object=self.real_page_record,
|
||||
scheduled_object=self.scheduled_page,
|
||||
locale=self.locale,
|
||||
translations=self.translations,
|
||||
),
|
||||
]
|
||||
if self.page.is_previewable():
|
||||
side_panels.append(
|
||||
PreviewSidePanel(
|
||||
self.page, self.request, preview_url=self.get_preview_url()
|
||||
)
|
||||
)
|
||||
side_panels.append(ChecksSidePanel(self.page, self.request))
|
||||
if self.form.show_comments_toggle:
|
||||
side_panels.append(CommentsSidePanel(self.page, self.request))
|
||||
return MediaContainer(side_panels)
|
||||
|
||||
def get_editing_sessions(self):
|
||||
EditingSession.cleanup()
|
||||
content_type = get_default_page_content_type()
|
||||
session = EditingSession.objects.create(
|
||||
user=self.request.user,
|
||||
content_type=content_type,
|
||||
object_id=self.page.pk,
|
||||
last_seen_at=timezone.now(),
|
||||
)
|
||||
return EditingSessionsModule(
|
||||
session,
|
||||
reverse(
|
||||
"wagtailadmin_editing_sessions:ping",
|
||||
args=("wagtailcore", "page", self.page.pk, session.id),
|
||||
),
|
||||
reverse(
|
||||
"wagtailadmin_editing_sessions:release",
|
||||
args=(session.id,),
|
||||
),
|
||||
[],
|
||||
self.page.latest_revision_id,
|
||||
)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
user_perms = self.page.permissions_for_user(self.request.user)
|
||||
bound_panel = self.edit_handler.get_bound_panel(
|
||||
instance=self.page, request=self.request, form=self.form
|
||||
)
|
||||
action_menu = PageActionMenu(
|
||||
self.request,
|
||||
view="edit",
|
||||
page=self.page,
|
||||
lock=self.lock,
|
||||
locked_for_user=self.locked_for_user,
|
||||
)
|
||||
side_panels = self.get_side_panels()
|
||||
|
||||
media = MediaContainer([bound_panel, self.form, action_menu, side_panels]).media
|
||||
|
||||
context.update(
|
||||
{
|
||||
"page": self.page,
|
||||
"page_for_status": self.page_for_status,
|
||||
"content_type": self.page_content_type,
|
||||
"edit_handler": bound_panel,
|
||||
"errors_debug": self.errors_debug,
|
||||
"action_menu": action_menu,
|
||||
"side_panels": side_panels,
|
||||
"form": self.form,
|
||||
"next": self.next_url,
|
||||
"history_url": self.get_history_url(),
|
||||
"has_unsaved_changes": self.has_unsaved_changes,
|
||||
"page_locked": self.locked_for_user,
|
||||
"workflow_state": self.workflow_state
|
||||
if self.workflow_state and self.workflow_state.is_active
|
||||
else None,
|
||||
"current_task_state": self.page.current_workflow_task_state,
|
||||
"publishing_will_cancel_workflow": self.workflow_tasks
|
||||
and getattr(settings, "WAGTAIL_WORKFLOW_CANCEL_ON_PUBLISH", True),
|
||||
"confirm_workflow_cancellation_url": reverse(
|
||||
"wagtailadmin_pages:confirm_workflow_cancellation",
|
||||
args=(self.page.id,),
|
||||
),
|
||||
"user_can_lock": (not self.lock or isinstance(self.lock, WorkflowLock))
|
||||
and user_perms.can_lock(),
|
||||
"user_can_unlock": isinstance(self.lock, BasicLock)
|
||||
and user_perms.can_unlock(),
|
||||
"locale": self.locale,
|
||||
"media": media,
|
||||
"editing_sessions": self.get_editing_sessions(),
|
||||
}
|
||||
)
|
||||
|
||||
return context
|
||||
|
||||
def get_translations(self):
|
||||
return [
|
||||
{
|
||||
"locale": translation.locale,
|
||||
"url": reverse("wagtailadmin_pages:edit", args=[translation.id]),
|
||||
}
|
||||
for translation in self.page.get_translations()
|
||||
.only("id", "locale", "depth")
|
||||
.select_related("locale")
|
||||
if translation.permissions_for_user(self.request.user).can_edit()
|
||||
]
|
||||
Reference in New Issue
Block a user