class EditView(TemplateResponseMixin, ContextMixin, HookResponseMixin, View): 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_legacy_moderation_warning(self): # Check for revisions still undergoing moderation and warn - this is for the old moderation system if self.latest_revision and self.latest_revision.submitted_for_moderation: buttons = [] if self.page.live: buttons.append(self.get_compare_with_live_message_button()) messages.warning(self.request, _("This page is currently awaiting moderation"), buttons=buttons) def add_save_confirmation_message(self): if self.is_reverting: message = _( "Page '{0}' has been replaced with version from {1}.").format( self.page.get_admin_display_title(), self.previous_revision.created_at.strftime( "%d %b %Y %H:%M")) else: message = _("Page '{0}' has been updated.").format( 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)).prefetch_related( Prefetch('comment_replies', queryset=replies), Prefetch('comments', queryset=comments)).exclude( Q(comment_replies__isnull=True) & Q(comments__isnull=True)) # 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(user.comments.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=True) def get_view_live_message_button(self): return messages.button(self.page.url, _('View live'), new_window=True) 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, id=page_id) self.latest_revision = self.real_page_record.get_latest_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_page() self.parent = self.page.get_parent() self.page_perms = self.page.permissions_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 try: self.subscription = PageSubscription.objects.get( page=self.page, user=self.request.user) except PageSubscription.DoesNotExist: self.subscription = PageSubscription(page=self.page, user=self.request.user) self.edit_handler = self.page_class.get_edit_handler() self.edit_handler = self.edit_handler.bind_to(instance=self.page, request=self.request) self.form_class = self.edit_handler.get_form_class() # 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() 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.page_perms.user_has_lock(): if self.page.locked_at: lock_message = format_html( _("<b>Page '{}' was locked</b> by <b>you</b> on <b>{}</b>." ), self.page.get_admin_display_title(), self.page.locked_at.strftime("%d %b %Y %H:%M")) else: lock_message = format_html( _("<b>Page '{}' is locked</b> by <b>you</b>."), self.page.get_admin_display_title()) lock_message += format_html( '<span class="buttons"><button class="button button-small button-secondary" data-locking-action="{}">{}</button></span>', reverse('wagtailadmin_pages:unlock', args=(self.page.id, )), _("Unlock")) messages.warning(self.request, lock_message, extra_tags='lock') elif self.page.locked and self.page_perms.page_locked(): # the page can also be locked at a permissions level if in a workflow, on a task the user is not a reviewer for # this should be indicated separately if self.page.locked_by and self.page.locked_at: lock_message = format_html( _("<b>Page '{}' was locked</b> by <b>{}</b> on <b>{}</b>." ), self.page.get_admin_display_title(), str(self.page.locked_by), self.page.locked_at.strftime("%d %b %Y %H:%M")) else: # Page was probably locked with an old version of Wagtail, or a script lock_message = format_html(_("<b>Page '{}' is locked</b>."), self.page.get_admin_display_title()) if self.page_perms.can_unlock(): lock_message += format_html( '<span class="buttons"><button class="button button-small button-secondary" data-locking-action="{}">{}</button></span>', reverse('wagtailadmin_pages:unlock', args=(self.page.id, )), _("Unlock")) messages.error(self.request, lock_message, extra_tags='lock') if self.page.current_workflow_state: workflow = self.workflow_state.workflow task = self.workflow_state.current_task_state.task if (self.workflow_state.status != WorkflowState.STATUS_NEEDS_CHANGES and task.specific.page_locked_for_user( self.page, self.request.user)): # Check for revisions still undergoing moderation and warn if len(self.workflow_tasks) == 1: # If only one task in workflow, show simple message workflow_info = _( "This page is currently awaiting moderation.") else: workflow_info = format_html( _("This page is awaiting <b>'{}'</b> in the <b>'{}'</b> workflow." ), task.name, workflow.name) messages.error( self.request, mark_safe( workflow_info + " " + _("Only reviewers for this task can edit the page.")), extra_tags="lock") self.form = self.form_class(instance=self.page, subscription=self.subscription, parent_page=self.parent) self.has_unsaved_changes = False self.edit_handler = self.edit_handler.bind_to(form=self.form) self.add_legacy_moderation_warning() 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 '{0}' has been cancelled.").format( 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) 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.page_perms.page_locked(): 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=False) 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=False) 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 revision.publish(user=self.request.user, changed=self.has_content_changes, 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) # 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 {0} of page '{1}' has been scheduled for publishing." ).format( self.previous_revision.created_at.strftime( "%d %b %Y %H:%M"), self.page.get_admin_display_title()) else: if self.page.live: message = _( "Page '{0}' is live and this version has been scheduled for publishing." ).format(self.page.get_admin_display_title()) else: message = _("Page '{0}' has been scheduled for publishing." ).format(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 {0} of page '{1}' has been published." ).format( self.previous_revision.created_at.strftime( "%d %b %Y %H:%M"), self.page.get_admin_display_title()) else: message = _("Page '{0}' has been published.").format( 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=False) 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 '{0}' has been submitted for moderation.").format( 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=False) 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 '{0}' has been restarted.").format( 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=False) 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=False) 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' % urlquote(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() if self.page_perms.page_locked(): 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.edit_handler = self.edit_handler.bind_to(form=self.form) self.add_legacy_moderation_warning() self.page_for_status = self.get_page_for_status() return self.render_to_response(self.get_context_data()) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context.update({ 'page': self.page, 'page_for_status': self.page_for_status, 'content_type': self.page_content_type, 'edit_handler': self.edit_handler, 'errors_debug': self.errors_debug, 'action_menu': PageActionMenu(self.request, view='edit', page=self.page), 'preview_modes': self.page.preview_modes, 'form': self.form, 'next': self.next_url, 'has_unsaved_changes': self.has_unsaved_changes, 'page_locked': self.page_perms.page_locked(), '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), 'locale': None, 'translations': [], }) if getattr(settings, 'WAGTAIL_I18N_ENABLED', False): user_perms = UserPagePermissionsProxy(self.request.user) context.update({ 'locale': self.page.locale, 'translations': [{ '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 user_perms.for_page(translation).can_edit()], }) return context
class CreateView(TemplateResponseMixin, ContextMixin, HookResponseMixin, View): template_name = 'wagtailadmin/pages/create.html' def dispatch(self, request, content_type_app_name, content_type_model_name, parent_page_id): self.parent_page = get_object_or_404(Page, id=parent_page_id).specific self.parent_page_perms = self.parent_page.permissions_for_user( self.request.user) if not self.parent_page_perms.can_add_subpage(): raise PermissionDenied try: self.page_content_type = ContentType.objects.get_by_natural_key( content_type_app_name, content_type_model_name) except ContentType.DoesNotExist: raise Http404 # Get class self.page_class = self.page_content_type.model_class() # Make sure the class is a descendant of Page if not issubclass(self.page_class, Page): raise Http404 # page must be in the list of allowed subpage types for this parent ID if self.page_class not in self.parent_page.creatable_subpage_models(): raise PermissionDenied if not self.page_class.can_create_at(self.parent_page): raise PermissionDenied response = self.run_hook('before_create_page', self.request, self.parent_page, self.page_class) if response: return response self.locale = self.parent_page.locale # If the parent page is the root page. The user may specify any locale they like if self.parent_page.is_root(): selected_locale = request.GET.get( 'locale', None) or request.POST.get('locale', None) if selected_locale: self.locale = get_object_or_404(Locale, language_code=selected_locale) self.page = self.page_class(owner=self.request.user) self.page.locale = self.locale self.edit_handler = self.page_class.get_edit_handler() self.edit_handler = self.edit_handler.bind_to(request=self.request, instance=self.page) self.form_class = self.edit_handler.get_form_class() # Note: Comment notifications should be enabled by default for pages that a user creates self.subscription = PageSubscription(page=self.page, user=self.request.user, comment_notifications=True) self.next_url = get_valid_next_url_from_request(self.request) return super().dispatch(request) def post(self, request): self.form = self.form_class(self.request.POST, self.request.FILES, instance=self.page, subscription=self.subscription, parent_page=self.parent_page) if self.form.is_valid(): return self.form_valid(self.form) else: return self.form_invalid(self.form) def form_valid(self, form): if bool(self.request.POST.get('action-publish') ) and self.parent_page_perms.can_publish_subpage(): return self.publish_action() elif bool(self.request.POST.get( 'action-submit')) and self.parent_page.has_workflow: return self.submit_action() else: return self.save_action() 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 save_action(self): self.page = self.form.save(commit=False) self.page.live = False # Save page self.parent_page.add_child(instance=self.page) # Save revision self.page.save_revision(user=self.request.user, log_action=False) # Save subscription settings self.subscription.page = self.page self.subscription.save() # Notification messages.success( self.request, _("Page '{0}' created.").format( self.page.get_admin_display_title())) response = self.run_hook('after_create_page', self.request, self.page) if response: return response # remain on edit page for further edits return self.redirect_and_remain() def publish_action(self): self.page = self.form.save(commit=False) # Save page self.parent_page.add_child(instance=self.page) # Save revision revision = self.page.save_revision(user=self.request.user, log_action=False) # Save subscription settings self.subscription.page = self.page self.subscription.save() # Publish response = self.run_hook('before_publish_page', self.request, self.page) if response: return response revision.publish(user=self.request.user) response = self.run_hook('after_publish_page', self.request, self.page) if response: return response # Notification if self.page.go_live_at and self.page.go_live_at > timezone.now(): messages.success( self.request, _("Page '{0}' created and scheduled for publishing.").format( self.page.get_admin_display_title()), buttons=[self.get_edit_message_button()]) else: 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, _("Page '{0}' created and published.").format( self.page.get_admin_display_title()), buttons=buttons) response = self.run_hook('after_create_page', self.request, self.page) if response: return response return self.redirect_away() def submit_action(self): self.page = self.form.save(commit=False) self.page.live = False # Save page self.parent_page.add_child(instance=self.page) # Save revision self.page.save_revision(user=self.request.user, log_action=False) # Submit workflow = self.page.get_workflow() workflow.start(self.page, self.request.user) # Save subscription settings self.subscription.page = self.page self.subscription.save() # Notification buttons = [] if self.page.is_previewable(): buttons.append(self.get_view_draft_message_button()) buttons.append(self.get_edit_message_button()) messages.success( self.request, _("Page '{0}' created and submitted for moderation.").format( self.page.get_admin_display_title()), buttons=buttons) response = self.run_hook('after_create_page', self.request, self.page) if response: return response return self.redirect_away() 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): messages.validation_error( self.request, _("The page could not be created due to validation errors"), self.form) self.has_unsaved_changes = True self.edit_handler = self.edit_handler.bind_to(form=self.form) return self.render_to_response(self.get_context_data()) def get(self, request): signals.init_new_page.send(sender=CreateView, page=self.page, parent=self.parent_page) self.form = self.form_class(instance=self.page, subscription=self.subscription, parent_page=self.parent_page) self.has_unsaved_changes = False self.edit_handler = self.edit_handler.bind_to(form=self.form) return self.render_to_response(self.get_context_data()) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context.update({ 'content_type': self.page_content_type, 'page_class': self.page_class, 'parent_page': self.parent_page, 'edit_handler': self.edit_handler, 'action_menu': PageActionMenu(self.request, view='create', parent_page=self.parent_page), 'preview_modes': self.page.preview_modes, 'form': self.form, 'next': self.next_url, 'has_unsaved_changes': self.has_unsaved_changes, 'locale': None, 'translations': [], }) if getattr(settings, 'WAGTAIL_I18N_ENABLED', False): # Pages can be created in any language at the root level if self.parent_page.is_root(): translations = [{ 'locale': locale, 'url': reverse( 'wagtailadmin_pages:add', args=[ self.page_content_type.app_label, self.page_content_type.model, self.parent_page.id ]) + '?' + urlencode({'locale': locale.language_code}), } for locale in Locale.objects.all()] else: user_perms = UserPagePermissionsProxy(self.request.user) translations = [ { 'locale': translation.locale, 'url': reverse('wagtailadmin_pages:add', args=[ self.page_content_type.app_label, self.page_content_type.model, translation.id ]), } for translation in self.parent_page.get_translations( ).only('id', 'locale').select_related('locale') if user_perms.for_page(translation).can_add_subpage() and self.page_class in translation.specific_class.creatable_subpage_models() and self.page_class.can_create_at(translation) ] context.update({ 'locale': self.locale, 'translations': translations, }) return context