def edit(request, user_id): user = get_object_or_404(User, pk=user_id) can_delete = user_can_delete_user(request.user, user) editing_self = request.user == user for fn in hooks.get_hooks('before_edit_user'): result = fn(request, user) if hasattr(result, 'status_code'): return result if request.method == 'POST': form = get_user_edit_form()(request.POST, request.FILES, instance=user, editing_self=editing_self) if form.is_valid(): user = form.save() messages.success(request, _("Your details have been updated. You've been logged out for security reasons, " "please login to continue.")) for fn in hooks.get_hooks('after_edit_user'): result = fn(request, user) if hasattr(result, 'status_code'): return result return redirect('wagtailusers_users:index') else: messages.error(request, _("The user could not be saved due to errors.")) else: form = get_user_edit_form()(instance=user, editing_self=editing_self) return render(request, 'wagtailusers/users/edit.html', { 'user': user, 'form': form, 'can_delete': can_delete, })
def create(request): for fn in hooks.get_hooks('before_create_user'): result = fn(request) if hasattr(result, 'status_code'): return result if request.method == 'POST': form = get_user_creation_form()(request.POST, request.FILES) if form.is_valid(): user = form.save() messages.success(request, _("User '{0}' created.").format(user), buttons=[ messages.button(reverse('wagtailusers_users:edit', args=(user.pk,)), _('Edit')) ]) for fn in hooks.get_hooks('after_create_user'): result = fn(request, user) if hasattr(result, 'status_code'): return result return redirect('wagtailusers_users:index') else: messages.error(request, _("The user could not be created due to errors.")) else: form = get_user_creation_form()() return render(request, 'wagtailusers/users/create.html', { 'form': form, })
def delete(request, page_id): page = get_object_or_404(Page, id=page_id).specific if not page.permissions_for_user(request.user).can_delete(): raise PermissionDenied with transaction.atomic(): for fn in hooks.get_hooks('before_delete_page'): result = fn(request, page) if hasattr(result, 'status_code'): return result next_url = get_valid_next_url_from_request(request) if request.method == 'POST': parent_id = page.get_parent().id page.delete() messages.success(request, _("Page '{0}' deleted.").format(page.get_admin_display_title())) for fn in hooks.get_hooks('after_delete_page'): result = fn(request, page) if hasattr(result, 'status_code'): return result if next_url: return redirect(next_url) return redirect('wagtailadmin_explore', parent_id) return render(request, 'wagtailadmin/pages/confirm_delete.html', { 'page': page, 'descendant_count': page.get_descendant_count(), 'next': next_url, })
def edit(request, user_id): user = get_object_or_404(User, pk=user_id) can_delete = user_can_delete_user(request.user, user) editing_self = request.user == user for fn in hooks.get_hooks('before_edit_user'): result = fn(request, user) if hasattr(result, 'status_code'): return result if request.method == 'POST': form = get_user_edit_form()(request.POST, request.FILES, instance=user, editing_self=editing_self) if form.is_valid(): user = form.save() messages.success(request, _("User '{0}' updated.").format(user), buttons=[ messages.button(reverse('wagtailusers_users:edit', args=(user.pk,)), _('Edit')) ]) for fn in hooks.get_hooks('after_edit_user'): result = fn(request, user) if hasattr(result, 'status_code'): return result return redirect('wagtailusers_users:index') else: messages.error(request, _("The user could not be saved due to errors.")) else: form = get_user_edit_form()(instance=user, editing_self=editing_self) return render(request, 'wagtailusers/users/edit.html', { 'user': user, 'form': form, 'can_delete': can_delete, })
def test_before_hook(self): def before_hook(): pass with self.register_hook('test_hook_name', before_hook, order=-1): hook_fns = hooks.get_hooks('test_hook_name') self.assertEqual(hook_fns, [before_hook, test_hook])
def test_after_hook(self): def after_hook(): pass with self.register_hook('test_hook_name', after_hook, order=1): hook_fns = hooks.get_hooks('test_hook_name') self.assertEqual(hook_fns, [test_hook, after_hook])
def home(request): panels = [ SiteSummaryPanel(request), UpgradeNotificationPanel(request), PagesForModerationPanel(request), RecentEditsPanel(request), ] for fn in hooks.get_hooks('construct_homepage_panels'): fn(request, panels) root_page = get_explorable_root_page(request.user) if root_page: root_site = root_page.get_site() else: root_site = None real_site_name = None if root_site: real_site_name = root_site.site_name if root_site.site_name else root_site.hostname return render(request, "wagtailadmin/home.html", { 'root_page': root_page, 'root_site': root_site, 'site_name': real_site_name if real_site_name else settings.WAGTAIL_SITE_NAME, 'panels': sorted(panels, key=lambda p: p.order), 'user': request.user })
def chooser(request): Image = get_image_model() if permission_policy.user_has_permission(request.user, 'add'): ImageForm = get_image_form(Image) uploadform = ImageForm(user=request.user) else: uploadform = None images = Image.objects.order_by('-created_at') # allow hooks to modify the queryset for hook in hooks.get_hooks('construct_image_chooser_queryset'): images = hook(images, request) if ( 'q' in request.GET or 'p' in request.GET or 'tag' in request.GET or 'collection_id' in request.GET ): # this request is triggered from search, pagination or 'popular tags'; # we will just render the results.html fragment collection_id = request.GET.get('collection_id') if collection_id: images = images.filter(collection=collection_id) searchform = SearchForm(request.GET) if searchform.is_valid(): q = searchform.cleaned_data['q'] images = images.search(q) is_searching = True else: is_searching = False q = None tag_name = request.GET.get('tag') if tag_name: images = images.filter(tags__name=tag_name) # Pagination paginator, images = paginate(request, images, per_page=12) return render(request, "wagtailimages/chooser/results.html", { 'images': images, 'is_searching': is_searching, 'query_string': q, 'will_select_format': request.GET.get('select_format') }) else: paginator, images = paginate(request, images, per_page=12) context = get_chooser_context(request) context.update({ 'images': images, 'uploadform': uploadform, }) return render_modal_workflow( request, 'wagtailimages/chooser/chooser.html', None, context, json_data=get_chooser_js_data() )
def chooser_upload(request): Image = get_image_model() ImageForm = get_image_form(Image) if request.method == 'POST': image = Image(uploaded_by_user=request.user) form = ImageForm( request.POST, request.FILES, instance=image, user=request.user, prefix='image-chooser-upload' ) if form.is_valid(): # Set image file size image.file_size = image.file.size # Set image file hash image.file.seek(0) image._set_file_hash(image.file.read()) image.file.seek(0) form.save() # Reindex the image to make sure all tags are indexed search_index.insert_or_update_object(image) if request.GET.get('select_format'): form = ImageInsertionForm( initial={'alt_text': image.default_alt_text}, prefix='image-chooser-insertion' ) return render_modal_workflow( request, 'wagtailimages/chooser/select_format.html', None, {'image': image, 'form': form}, json_data={'step': 'select_format'} ) else: # not specifying a format; return the image details now return render_modal_workflow( request, None, None, None, json_data={'step': 'image_chosen', 'result': get_image_result_data(image)} ) else: form = ImageForm(user=request.user, prefix='image-chooser-upload') images = Image.objects.order_by('-created_at') # allow hooks to modify the queryset for hook in hooks.get_hooks('construct_image_chooser_queryset'): images = hook(images, request) paginator = Paginator(images, per_page=12) images = paginator.get_page(request.GET.get('p')) context = get_chooser_context(request) context.update({ 'images': images, 'uploadform': form, }) return render_modal_workflow( request, 'wagtailimages/chooser/chooser.html', None, context, json_data=get_chooser_js_data() )
def get_permission_panel_classes(): global _permission_panel_classes if _permission_panel_classes is None: _permission_panel_classes = [GroupPagePermissionFormSet] for fn in hooks.get_hooks('register_group_permission_panel'): _permission_panel_classes.append(fn()) return _permission_panel_classes
def test_registered_permission(self): permission = Permission.objects.get_by_natural_key( app_label='tests', model='testsetting', codename='change_testsetting') for fn in hooks.get_hooks('register_permissions'): if permission in fn(): break else: self.fail('Change permission for tests.TestSetting not registered')
def chooser(request): Document = get_document_model() if permission_policy.user_has_permission(request.user, 'add'): DocumentForm = get_document_form(Document) uploadform = DocumentForm(user=request.user) else: uploadform = None documents = Document.objects.all() # allow hooks to modify the queryset for hook in hooks.get_hooks('construct_document_chooser_queryset'): documents = hook(documents, request) q = None if 'q' in request.GET or 'p' in request.GET or 'collection_id' in request.GET: collection_id = request.GET.get('collection_id') if collection_id: documents = documents.filter(collection=collection_id) searchform = SearchForm(request.GET) if searchform.is_valid(): q = searchform.cleaned_data['q'] documents = documents.search(q) is_searching = True else: documents = documents.order_by('-created_at') is_searching = False # Pagination paginator, documents = paginate(request, documents, per_page=10) return render(request, "wagtaildocs/chooser/results.html", { 'documents': documents, 'query_string': q, 'is_searching': is_searching, }) else: searchform = SearchForm() collections = Collection.objects.all() if len(collections) < 2: collections = None documents = documents.order_by('-created_at') paginator, documents = paginate(request, documents, per_page=10) return render_modal_workflow(request, 'wagtaildocs/chooser/chooser.html', 'wagtaildocs/chooser/chooser.js', { 'documents': documents, 'uploadform': uploadform, 'searchform': searchform, 'collections': collections, 'is_searching': False, 'uploadid': uuid.uuid4(), })
def _search_for_operations(cls): if cls._registered_operations is not None: return operations = [] for fn in hooks.get_hooks('register_image_operations'): operations.extend(fn()) cls._registered_operations = dict(operations)
def hook_output(hook_name): """ Example: {% hook_output 'insert_editor_css' %} Whenever we have a hook whose functions take no parameters and return a string, this tag can be used to output the concatenation of all of those return values onto the page. Note that the output is not escaped - it is the hook function's responsibility to escape unsafe content. """ snippets = [fn() for fn in hooks.get_hooks(hook_name)] return mark_safe(''.join(snippets))
def serve(request, document_id, document_filename): Document = get_document_model() doc = get_object_or_404(Document, id=document_id) # We want to ensure that the document filename provided in the URL matches the one associated with the considered # document_id. If not we can't be sure that the document the user wants to access is the one corresponding to the # <document_id, document_filename> pair. if doc.filename != document_filename: raise Http404('This document does not match the given filename.') for fn in hooks.get_hooks('before_serve_document'): result = fn(doc, request) if isinstance(result, HttpResponse): return result # Send document_served signal document_served.send(sender=Document, instance=doc, request=request) try: local_path = doc.file.path except NotImplementedError: local_path = None if local_path: # Use wagtail.utils.sendfile to serve the file; # this provides support for mimetypes, if-modified-since and django-sendfile backends if hasattr(settings, 'SENDFILE_BACKEND'): return sendfile(request, local_path, attachment=True, attachment_filename=doc.filename) else: # Fallback to streaming backend if user hasn't specified SENDFILE_BACKEND return sendfile( request, local_path, attachment=True, attachment_filename=doc.filename, backend=sendfile_streaming_backend.sendfile ) else: # We are using a storage backend which does not expose filesystem paths # (e.g. storages.backends.s3boto.S3BotoStorage). # Fall back on pre-sendfile behaviour of reading the file content and serving it # as a StreamingHttpResponse wrapper = FileWrapper(doc.file) response = StreamingHttpResponse(wrapper, content_type='application/octet-stream') response['Content-Disposition'] = 'attachment; filename=%s' % doc.filename # FIXME: storage backends are not guaranteed to implement 'size' response['Content-Length'] = doc.file.size return response
def filter_queryset(self, request, queryset, view): if request.GET.get('for_explorer'): if not hasattr(queryset, '_filtered_by_child_of'): raise BadRequestError("filtering by for_explorer without child_of is not supported") parent_page = queryset._filtered_by_child_of for hook in hooks.get_hooks('construct_explorer_page_queryset'): queryset = hook(parent_page, queryset, request) return queryset
def account(request): items = [] for fn in hooks.get_hooks('register_account_menu_item'): item = fn(request) if item: items.append(item) return render(request, 'wagtailadmin/account/account.html', { 'items': items, })
def get_forms_for_user(user): """ Return a queryset of form pages that this user is allowed to access the submissions for """ editable_forms = UserPagePermissionsProxy(user).editable_pages() editable_forms = editable_forms.filter(content_type__in=get_form_types()) # Apply hooks for fn in hooks.get_hooks('filter_form_submissions_for_user'): editable_forms = fn(user, editable_forms) return editable_forms
def get_collection_contents(self): collection_contents = [ hook(self.object) for hook in hooks.get_hooks('describe_collection_contents') ] # filter out any hook responses that report that the collection is empty # (by returning None, or a dict with 'count': 0) def is_nonempty(item_type): return item_type and item_type['count'] > 0 return list(filter(is_nonempty, collection_contents))
def delete(request, user_id): user = get_object_or_404(User, pk=user_id) if not user_can_delete_user(request.user, user): return permission_denied(request) for fn in hooks.get_hooks('before_delete_user'): result = fn(request, user) if hasattr(result, 'status_code'): return result if request.method == 'POST': user.delete() messages.success(request, _("User '{0}' deleted.").format(user)) for fn in hooks.get_hooks('after_delete_user'): result = fn(request, user) if hasattr(result, 'status_code'): return result return redirect('wagtailusers_users:index') return render(request, "wagtailusers/users/confirm_delete.html", { 'user': user, })
def render_html(self, request): menu_items = self.menu_items_for_request(request) # provide a hook for modifying the menu, if construct_hook_name has been set if self.construct_hook_name: for fn in hooks.get_hooks(self.construct_hook_name): fn(request, menu_items) rendered_menu_items = [] for item in sorted(menu_items, key=lambda i: i.order): rendered_menu_items.append(item.render_html(request)) return mark_safe(''.join(rendered_menu_items))
def move_confirm(request, page_to_move_id, destination_id): page_to_move = get_object_or_404(Page, id=page_to_move_id).specific destination = get_object_or_404(Page, id=destination_id) if not page_to_move.permissions_for_user(request.user).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, destination.id) for fn in hooks.get_hooks('before_move_page'): result = fn(request, page_to_move, destination) if hasattr(result, 'status_code'): return result if request.method == 'POST': # any invalid moves *should* be caught by the permission check above, # so don't bother to catch InvalidMoveToDescendant page_to_move.move(destination, pos='last-child') messages.success(request, _("Page '{0}' moved.").format(page_to_move.get_admin_display_title()), buttons=[ messages.button(reverse('wagtailadmin_pages:edit', args=(page_to_move.id,)), _('Edit')) ]) for fn in hooks.get_hooks('after_move_page'): result = fn(request, page_to_move) if hasattr(result, 'status_code'): return result return redirect('wagtailadmin_explore', destination.id) return render(request, 'wagtailadmin/pages/confirm_move.html', { 'page_to_move': page_to_move, 'destination': destination, })
def __init__(self, request, **kwargs): self.request = request self.context = kwargs self.context['user_page_permissions'] = UserPagePermissionsProxy(self.request.user) self.menu_items = [ menu_item for menu_item in _get_base_page_action_menu_items() if menu_item.is_shown(self.request, self.context) ] self.menu_items.sort(key=lambda item: item.order) for hook in hooks.get_hooks('construct_page_action_menu'): hook(self.menu_items, self.request, self.context)
def render_html(self, request): menu_items = self.menu_items_for_request(request) # provide a hook for modifying the menu, if construct_hook_name has been set if self.construct_hook_name: for fn in hooks.get_hooks(self.construct_hook_name): fn(request, menu_items) rendered_menu_items = [] for item in sorted(menu_items, key=lambda i: i.order): try: rendered_menu_items.append(item.render_html(request)) except TypeError: # fallback for older render_html methods that don't accept a request arg rendered_menu_items.append(item.render_html(request)) return mark_safe(''.join(rendered_menu_items))
def _get_base_page_action_menu_items(): """ Retrieve the global list of menu items for the page action menu, which may then be customised on a per-request basis """ global BASE_PAGE_ACTION_MENU_ITEMS if BASE_PAGE_ACTION_MENU_ITEMS is None: BASE_PAGE_ACTION_MENU_ITEMS = [ UnpublishMenuItem(order=10), DeleteMenuItem(order=20), PublishMenuItem(order=30), SubmitForModerationMenuItem(order=40), ] for hook in hooks.get_hooks('register_page_action_menu_item'): BASE_PAGE_ACTION_MENU_ITEMS.append(hook()) return BASE_PAGE_ACTION_MENU_ITEMS
def __init__(self, converter_rules): self.converter_rules = converter_rules self.element_rules = BASE_WHITELIST_RULES.copy() for rule in self.converter_rules: if isinstance(rule, WhitelistRule): self.element_rules[rule.element] = rule.handler # apply legacy construct_whitelister_element_rules hooks to the assembled list construct_whitelist_hooks = hooks.get_hooks('construct_whitelister_element_rules') if construct_whitelist_hooks: warnings.warn( 'The construct_whitelister_element_rules hook is deprecated and will be removed ' 'in Wagtail 2.2. Use register_rich_text_features instead', RemovedInWagtail22Warning ) for fn in construct_whitelist_hooks: self.element_rules.update(fn())
def for_frontend(request, page_id): items = [ EditPageItem(Page.objects.get(id=page_id)), AddPageItem(Page.objects.get(id=page_id)), ] for fn in hooks.get_hooks('construct_wagtail_userbar'): fn(request, items) # Render the items rendered_items = [item.render(request) for item in items] # Remove any unrendered items rendered_items = [item for item in rendered_items if item] # Render the edit bird return render(request, 'wagtailadmin/userbar/base.html', { 'items': rendered_items, })
def for_moderation(request, revision_id): items = [ EditPageItem(PageRevision.objects.get(id=revision_id).page), AddPageItem(PageRevision.objects.get(id=revision_id).page), ApproveModerationEditPageItem(PageRevision.objects.get(id=revision_id)), RejectModerationEditPageItem(PageRevision.objects.get(id=revision_id)), ] for fn in hooks.get_hooks('construct_wagtail_userbar'): fn(request, items) # Render the items rendered_items = [item.render(request) for item in items] # Remove any unrendered items rendered_items = [item for item in rendered_items if item] # Render the edit bird return render(request, 'wagtailadmin/userbar/base.html', { 'items': rendered_items, })
def search(request, parent_page_id=None): # A missing or empty page_type parameter indicates 'all page types' (i.e. descendants of wagtailcore.page) page_type_string = request.GET.get('page_type') or 'wagtailcore.page' try: desired_classes = page_models_from_string(page_type_string) except (ValueError, LookupError): raise Http404 pages = Page.objects.all() # allow hooks to modify the queryset for hook in hooks.get_hooks('construct_page_chooser_queryset'): pages = hook(pages, request) search_form = SearchForm(request.GET) if search_form.is_valid() and search_form.cleaned_data['q']: pages = pages.exclude( depth=1 # never include root ) pages = filter_page_type(pages, desired_classes) pages = pages.specific() pages = pages.search(search_form.cleaned_data['q']) else: pages = pages.none() paginator = Paginator(pages, per_page=25) pages = paginator.get_page(request.GET.get('p')) for page in pages: page.can_choose = True return render( request, 'wagtailadmin/chooser/_search_results.html', shared_context(request, { 'searchform': search_form, 'pages': pages, 'page_type_string': page_type_string, }) )
def archive(request, page_id): page = get_object_or_404(Page, id=page_id) if not page.permissions_for_user(request.user).can_archive(): raise PermissionDenied if request.method == 'POST': parent_id = page.get_parent().id page.archive() messages.success(request, _("Page '{0}' archived.").format(page.title)) for fn in hooks.get_hooks('after_delete_archive'): result = fn(request, page) if hasattr(result, 'status_code'): return result return redirect('wagtailadmin_explore', parent_id) return render(request, 'wagtailadmin/pages/confirm_archive.html', { 'page': page, 'descendant_count': page.get_descendant_count() })
def copy(request, page_id): page = Page.objects.get(id=page_id) # Parent page defaults to parent of source page parent_page = page.get_parent() # Check if the user has permission to publish subpages on the parent can_publish = parent_page.permissions_for_user( request.user).can_publish_subpage() # Create the form form = CopyForm(request.POST or None, user=request.user, page=page, can_publish=can_publish) next_url = get_valid_next_url_from_request(request) for fn in hooks.get_hooks('before_copy_page'): result = fn(request, page) if hasattr(result, 'status_code'): return result # Check if user is submitting if request.method == 'POST': # Prefill parent_page in case the form is invalid (as prepopulated value for the form field, # because ModelChoiceField seems to not fall back to the user given value) parent_page = Page.objects.get(id=request.POST['new_parent_page']) if form.is_valid(): # Receive the parent page (this should never be empty) if form.cleaned_data['new_parent_page']: parent_page = form.cleaned_data['new_parent_page'] if not page.permissions_for_user(request.user).can_copy_to( parent_page, form.cleaned_data.get('copy_subpages')): raise PermissionDenied # Re-check if the user has permission to publish subpages on the new parent can_publish = parent_page.permissions_for_user( request.user).can_publish_subpage() # Copy the page new_page = page.copy( recursive=form.cleaned_data.get('copy_subpages'), to=parent_page, update_attrs={ 'title': form.cleaned_data['new_title'], 'slug': form.cleaned_data['new_slug'], }, keep_live=(can_publish and form.cleaned_data.get('publish_copies')), user=request.user, ) # Give a success message back to the user if form.cleaned_data.get('copy_subpages'): messages.success( request, _("Page '{0}' and {1} subpages copied.").format( page.get_admin_display_title(), new_page.get_descendants().count())) else: messages.success( request, _("Page '{0}' copied.").format( page.get_admin_display_title())) for fn in hooks.get_hooks('after_copy_page'): result = fn(request, page, new_page) if hasattr(result, 'status_code'): return result # Redirect to explore of parent page if next_url: return redirect(next_url) return redirect('wagtailadmin_explore', parent_page.id) return render(request, 'wagtailadmin/pages/copy.html', { 'page': page, 'form': form, 'next': next_url, })
from wagtail.core import hooks REGISTERED_COLLECTIONS = {} for fn in hooks.get_hooks('rai_document_collection'): collection = fn() REGISTERED_COLLECTIONS.update(collection) REGISTERED_ONDEMAND_DOCUMENTS = {} for fn in hooks.get_hooks('rai_document_on_demand'): key, doc = fn() relation = doc.relation.__name__ docs = REGISTERED_ONDEMAND_DOCUMENTS.get(relation, {}) docs.update({key: doc}) REGISTERED_ONDEMAND_DOCUMENTS.update({relation: docs})
def get_base_page_queryset(self): qs = Page.objects.filter(live=True, expired=False, show_in_menus=True) # allow hooks to modify the queryset for hook in hooks.get_hooks('menus_modify_base_page_queryset'): qs = hook(qs, **self.common_hook_kwargs) return qs
def edit(request, page_id): latest_revision = get_object_or_404(Page, id=page_id).get_latest_revision() page = get_object_or_404(Page, id=page_id).get_latest_revision_as_page() parent = page.get_parent() content_type = ContentType.objects.get_for_model(page) page_class = content_type.model_class() page_perms = page.permissions_for_user(request.user) if not page_perms.can_edit(): raise PermissionDenied for fn in hooks.get_hooks('before_edit_page'): result = fn(request, page) if hasattr(result, 'status_code'): return result edit_handler = page_class.get_edit_handler() form_class = edit_handler.get_form_class() next_url = get_valid_next_url_from_request(request) errors_debug = None if request.method == 'POST': form = form_class(request.POST, request.FILES, instance=page, parent_page=parent) if form.is_valid() and not page.locked: page = form.save(commit=False) is_publishing = bool(request.POST.get( 'action-publish')) and page_perms.can_publish() is_submitting = bool(request.POST.get('action-submit')) is_reverting = bool(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 is_reverting: previous_revision = get_object_or_404( page.revisions, id=request.POST.get('revision')) # Save revision revision = page.save_revision( user=request.user, submitted_for_moderation=is_submitting, ) # store submitted go_live_at for messaging below go_live_at = page.go_live_at # Publish if is_publishing: revision.publish() # Need to reload the page because the URL may have changed, and we # need the up-to-date URL for the "View Live" button. page = page.specific_class.objects.get(pk=page.pk) # Notifications if is_publishing: if go_live_at and go_live_at > timezone.now(): # Page has been scheduled for publishing in the future if is_reverting: message = _( "Revision from {0} of page '{1}' has been scheduled for publishing." ).format( previous_revision.created_at.strftime( "%d %b %Y %H:%M"), page.get_admin_display_title()) else: if page.live: message = _( "Page '{0}' is live and this revision has been scheduled for publishing." ).format(page.get_admin_display_title()) else: message = _( "Page '{0}' has been scheduled for publishing." ).format(page.get_admin_display_title()) messages.success(request, message, buttons=[ messages.button( reverse('wagtailadmin_pages:edit', args=(page.id, )), _('Edit')) ]) else: # Page is being published now if is_reverting: message = _( "Revision from {0} of page '{1}' has been published." ).format( previous_revision.created_at.strftime( "%d %b %Y %H:%M"), page.get_admin_display_title()) else: message = _("Page '{0}' has been published.").format( page.get_admin_display_title()) buttons = [] if page.url is not None: buttons.append( messages.button(page.url, _('View live'), new_window=True)) buttons.append( messages.button( reverse('wagtailadmin_pages:edit', args=(page_id, )), _('Edit'))) messages.success(request, message, buttons=buttons) elif is_submitting: message = _( "Page '{0}' has been submitted for moderation.").format( page.get_admin_display_title()) messages.success(request, message, buttons=[ messages.button(reverse( 'wagtailadmin_pages:view_draft', args=(page_id, )), _('View draft'), new_window=True), messages.button( reverse('wagtailadmin_pages:edit', args=(page_id, )), _('Edit')) ]) if not send_notification(page.get_latest_revision().id, 'submitted', request.user.pk): messages.error( request, _("Failed to send notifications to moderators")) else: # Saving if is_reverting: message = _( "Page '{0}' has been replaced with revision from {1}." ).format( page.get_admin_display_title(), previous_revision.created_at.strftime( "%d %b %Y %H:%M")) else: message = _("Page '{0}' has been updated.").format( page.get_admin_display_title()) messages.success(request, message) for fn in hooks.get_hooks('after_edit_page'): result = fn(request, page) if hasattr(result, 'status_code'): return result if is_publishing or is_submitting: # we're done here - redirect back to the explorer if next_url: # redirect back to 'next' url if present return redirect(next_url) # redirect back to the explorer return redirect('wagtailadmin_explore', page.get_parent().id) else: # Just saving - remain on edit page for further edits target_url = reverse('wagtailadmin_pages:edit', args=[page.id]) if next_url: # Ensure the 'next' url is passed through again if present target_url += '?next=%s' % urlquote(next_url) return redirect(target_url) else: if page.locked: messages.error( request, _("The page could not be saved as it is locked")) else: messages.validation_error( request, _("The page could not be saved due to validation errors"), form) edit_handler = edit_handler.bind_to_instance(instance=page, form=form) errors_debug = (repr(edit_handler.form.errors) + repr( [(name, formset.errors) for (name, formset) in edit_handler.form.formsets.items() if formset.errors])) has_unsaved_changes = True else: form = form_class(instance=page, parent_page=parent) edit_handler = edit_handler.bind_to_instance(instance=page, form=form) has_unsaved_changes = False # Check for revisions still undergoing moderation and warn if latest_revision and latest_revision.submitted_for_moderation: buttons = [] if page.live: buttons.append( messages.button( reverse('wagtailadmin_pages:revisions_compare', args=(page.id, 'live', latest_revision.id)), _('Compare with live version'))) messages.warning(request, _("This page is currently awaiting moderation"), buttons=buttons) # Page status needs to present the version of the page containing the correct live URL if page.has_unpublished_changes: page_for_status = latest_revision.page.specific else: page_for_status = page return render( request, 'wagtailadmin/pages/edit.html', { 'page': page, 'page_for_status': page_for_status, 'content_type': content_type, 'edit_handler': edit_handler, 'errors_debug': errors_debug, 'preview_modes': page.preview_modes, 'form': form, 'next': next_url, 'has_unsaved_changes': has_unsaved_changes, })
def chooser(request): Image = get_image_model() if permission_policy.user_has_permission(request.user, 'add'): ImageForm = get_image_form(Image) uploadform = ImageForm(user=request.user, prefix='image-chooser-upload') else: uploadform = None images = Image.objects.order_by('-created_at') # allow hooks to modify the queryset for hook in hooks.get_hooks('construct_image_chooser_queryset'): images = hook(images, request) if ('q' in request.GET or 'p' in request.GET or 'tag' in request.GET or 'collection_id' in request.GET): # this request is triggered from search, pagination or 'popular tags'; # we will just render the results.html fragment collection_id = request.GET.get('collection_id') if collection_id: images = images.filter(collection=collection_id) searchform = SearchForm(request.GET) if searchform.is_valid(): q = searchform.cleaned_data['q'] images = images.search(q) is_searching = True else: is_searching = False q = None tag_name = request.GET.get('tag') if tag_name: images = images.filter(tags__name=tag_name) # Pagination paginator = Paginator(images, per_page=CHOOSER_PAGE_SIZE) images = paginator.get_page(request.GET.get('p')) return TemplateResponse( request, "wagtailimages/chooser/results.html", { 'images': images, 'is_searching': is_searching, 'query_string': q, 'will_select_format': request.GET.get('select_format') }) else: paginator = Paginator(images, per_page=CHOOSER_PAGE_SIZE) images = paginator.get_page(request.GET.get('p')) context = get_chooser_context(request) context.update({ 'images': images, 'uploadform': uploadform, }) return render_modal_workflow(request, 'wagtailimages/chooser/chooser.html', None, context, json_data=get_chooser_js_data())
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.registered_permissions = Permission.objects.none() for fn in hooks.get_hooks('register_permissions'): self.registered_permissions = self.registered_permissions | fn() self.fields['permissions'].queryset = self.registered_permissions.select_related('content_type')
def index(request, parent_page_id=None): if parent_page_id: parent_page = get_object_or_404(Page, id=parent_page_id).specific else: parent_page = Page.get_first_root_node().specific pages = parent_page.get_children().prefetch_related( 'content_type', 'sites_rooted_here') # Get page ordering ordering = request.GET.get('ordering', '-latest_revision_created_at') if ordering not in [ 'title', '-title', 'content_type', '-content_type', 'live', '-live', 'latest_revision_created_at', '-latest_revision_created_at', 'ord' ]: ordering = '-latest_revision_created_at' if ordering == 'ord': # preserve the native ordering from get_children() pass elif ordering == 'latest_revision_created_at': # order by oldest revision first. # Special case NULL entries - these should go at the top of the list. # Do this by annotating with Count('latest_revision_created_at'), # which returns 0 for these pages = pages.annotate( null_position=Count('latest_revision_created_at')).order_by( 'null_position', 'latest_revision_created_at') elif ordering == '-latest_revision_created_at': # order by oldest revision first. # Special case NULL entries - these should go at the end of the list. pages = pages.annotate( null_position=Count('latest_revision_created_at')).order_by( '-null_position', '-latest_revision_created_at') else: pages = pages.order_by(ordering) # Don't paginate if sorting by page order - all pages must be shown to # allow drag-and-drop reordering do_paginate = ordering != 'ord' if do_paginate: # Retrieve pages in their most specific form. # Only do this for paginated listings, as this could potentially be a # very expensive operation when performed on a large queryset. pages = pages.specific() # allow hooks to modify the queryset for hook in hooks.get_hooks('construct_explorer_page_queryset'): pages = hook(parent_page, pages, request) # Pagination if do_paginate: paginator, pages = paginate(request, pages, per_page=50) return render( request, 'wagtailadmin/pages/index.html', { 'parent_page': parent_page.specific, 'ordering': ordering, 'pagination_query_params': "ordering=%s" % ordering, 'pages': pages, 'do_paginate': do_paginate, })
def registered_menu_items(self): if self._registered_menu_items is None: self._registered_menu_items = [ fn() for fn in hooks.get_hooks(self.register_hook_name) ] return self._registered_menu_items
def chooser(request): Image = get_image_model() if permission_policy.user_has_permission(request.user, 'add'): ImageForm = get_image_form(Image) uploadform = ImageForm(user=request.user) else: uploadform = None images = Image.objects.order_by('-created_at') # allow hooks to modify the queryset for hook in hooks.get_hooks('construct_image_chooser_queryset'): images = hook(images, request) q = None if ( 'q' in request.GET or 'p' in request.GET or 'tag' in request.GET or 'collection_id' in request.GET ): # this request is triggered from search, pagination or 'popular tags'; # we will just render the results.html fragment collection_id = request.GET.get('collection_id') if collection_id: images = images.filter(collection=collection_id) searchform = SearchForm(request.GET) if searchform.is_valid(): q = searchform.cleaned_data['q'] images = images.search(q) is_searching = True else: is_searching = False tag_name = request.GET.get('tag') if tag_name: images = images.filter(tags__name=tag_name) # Pagination paginator, images = paginate(request, images, per_page=12) return render(request, "wagtailimages/chooser/results.html", { 'images': images, 'is_searching': is_searching, 'query_string': q, 'will_select_format': request.GET.get('select_format') }) else: searchform = SearchForm() collections = Collection.objects.all() if len(collections) < 2: collections = None paginator, images = paginate(request, images, per_page=12) return render_modal_workflow(request, 'wagtailimages/chooser/chooser.html', None, { 'images': images, 'uploadform': uploadform, 'searchform': searchform, 'is_searching': False, 'query_string': q, 'will_select_format': request.GET.get('select_format'), 'popular_tags': popular_tags_for_model(Image), 'collections': collections, }, json_data=get_chooser_context())
def chooser_upload(request): Image = get_image_model() ImageForm = get_image_form(Image) if request.method == 'POST': image = Image(uploaded_by_user=request.user) form = ImageForm(request.POST, request.FILES, instance=image, user=request.user) if form.is_valid(): # Set image file size image.file_size = image.file.size # Set image file hash image.file.seek(0) image._set_file_hash(image.file.read()) image.file.seek(0) form.save() # Reindex the image to make sure all tags are indexed search_index.insert_or_update_object(image) if request.GET.get('select_format'): form = ImageInsertionForm( initial={'alt_text': image.default_alt_text}) return render_modal_workflow( request, 'wagtailimages/chooser/select_format.html', None, { 'image': image, 'form': form }, json_data={'step': 'select_format'}) else: # not specifying a format; return the image details now return render_modal_workflow(request, None, None, None, json_data={ 'step': 'image_chosen', 'result': get_image_result_data(image) }) else: form = ImageForm(user=request.user) images = Image.objects.order_by('-created_at') # allow hooks to modify the queryset for hook in hooks.get_hooks('construct_image_chooser_queryset'): images = hook(images, request) paginator, images = paginate(request, images, per_page=12) context = get_chooser_context(request) context.update({ 'images': images, 'uploadform': form, }) return render_modal_workflow(request, 'wagtailimages/chooser/chooser.html', None, context, json_data=get_chooser_js_data())
name='wagtailadmin_account_change_email'), url(r'^account/notification_preferences/$', account.notification_preferences, name='wagtailadmin_account_notification_preferences'), url(r'^account/language_preferences/$', account.language_preferences, name='wagtailadmin_account_language_preferences'), url(r'^account/current_time_zone/$', account.current_time_zone, name='wagtailadmin_account_current_time_zone'), url(r'^logout/$', account.LogoutView.as_view(), name='wagtailadmin_logout'), ] # Import additional urlpatterns from any apps that define a register_admin_urls hook for fn in hooks.get_hooks('register_admin_urls'): urls = fn() if urls: urlpatterns += urls # Add "wagtailadmin.access_admin" permission check urlpatterns = decorate_urlpatterns(urlpatterns, require_admin_access) # These url patterns do not require an authenticated admin user urlpatterns += [ url(r'^login/$', account.LoginView.as_view(), name='wagtailadmin_login'), # These two URLs have the "permission_required" decorator applied directly # as they need to fail with a 403 error rather than redirect to the login page url(r'^userbar/(\d+)/$', userbar.for_frontend,
def chooser(request): Document = get_document_model() if permission_policy.user_has_permission(request.user, 'add'): DocumentForm = get_document_form(Document) uploadform = DocumentForm(user=request.user, prefix='document-chooser-upload') else: uploadform = None documents = Document.objects.all() # allow hooks to modify the queryset for hook in hooks.get_hooks('construct_document_chooser_queryset'): documents = hook(documents, request) q = None if 'q' in request.GET or 'p' in request.GET or 'collection_id' in request.GET: collection_id = request.GET.get('collection_id') if collection_id: documents = documents.filter(collection=collection_id) documents_exist = documents.exists() searchform = SearchForm(request.GET) if searchform.is_valid(): q = searchform.cleaned_data['q'] documents = documents.search(q) is_searching = True else: documents = documents.order_by('-created_at') is_searching = False # Pagination paginator = Paginator(documents, per_page=10) documents = paginator.get_page(request.GET.get('p')) return render(request, "wagtaildocs/chooser/results.html", { 'documents': documents, 'documents_exist': documents_exist, 'uploadform': uploadform, 'query_string': q, 'is_searching': is_searching, 'collection_id': collection_id, }) else: searchform = SearchForm() collections = Collection.objects.all() if len(collections) < 2: collections = None else: collections = Collection.order_for_display(collections) documents = documents.order_by('-created_at') documents_exist = documents.exists() paginator = Paginator(documents, per_page=10) documents = paginator.get_page(request.GET.get('p')) return render_modal_workflow(request, 'wagtaildocs/chooser/chooser.html', None, { 'documents': documents, 'documents_exist': documents_exist, 'uploadform': uploadform, 'searchform': searchform, 'collections': collections, 'is_searching': False, }, json_data=get_chooser_context())
def page_listing_buttons(context, page, page_perms, is_parent=False): button_hooks = hooks.get_hooks('register_page_listing_buttons') buttons = sorted(itertools.chain.from_iterable( hook(page, page_perms, is_parent) for hook in button_hooks)) return {'page': page, 'buttons': buttons}
def before_copy_page(request, page): parent_page = page.get_parent() can_publish = parent_page.permissions_for_user( request.user).can_publish_subpage() form = PatchedCopyForm(request.POST or None, user=request.user, page=page, can_publish=can_publish) next_url = get_valid_next_url_from_request(request) if request.method == 'POST': # Prefill parent_page in case the form is invalid (as prepopulated value for the form field, # because ModelChoiceField seems to not fall back to the user given value) parent_page = Page.objects.get(id=request.POST['new_parent_page']) if form.is_valid(): # Receive the parent page (this should never be empty) if form.cleaned_data['new_parent_page']: parent_page = form.cleaned_data['new_parent_page'] if not page.permissions_for_user(request.user).can_copy_to( parent_page, form.cleaned_data.get('copy_subpages')): raise PermissionDenied # Re-check if the user has permission to publish subpages on the new parent can_publish = parent_page.permissions_for_user( request.user).can_publish_subpage() update_attrs = {} for code, name in settings.LANGUAGES: slug = build_localized_fieldname('slug', code) title = build_localized_fieldname('title', code) update_attrs[slug] = form.cleaned_data["new_{}".format(slug)] update_attrs[title] = form.cleaned_data["new_{}".format(title)] # Copy the page new_page = page.copy( recursive=form.cleaned_data.get('copy_subpages'), to=parent_page, update_attrs=update_attrs, keep_live=(can_publish and form.cleaned_data.get('publish_copies')), user=request.user, ) # Give a success message back to the user if form.cleaned_data.get('copy_subpages'): messages.success( request, _("Page '{0}' and {1} subpages copied.").format( page.get_admin_display_title(), new_page.get_descendants().count())) else: messages.success( request, _("Page '{0}' copied.").format( page.get_admin_display_title())) for fn in hooks.get_hooks('after_copy_page'): result = fn(request, page, new_page) if hasattr(result, 'status_code'): return result # Redirect to explore of parent page if next_url: return redirect(next_url) return redirect('wagtailadmin_explore', parent_page.id) return render(request, 'modeltranslation_copy.html', { 'page': page, 'form': form, 'next': next_url })
def chooser(request): media_files = permission_policy.instances_user_has_any_permission_for( request.user, ["change", "delete"]) # allow hooks to modify the queryset for hook in hooks.get_hooks("construct_media_chooser_queryset"): media_files = hook(media_files, request) if permission_policy.user_has_permission(request.user, "add"): Media = get_media_model() MediaForm = get_media_form(Media) uploadform = MediaForm(user=request.user, prefix="media-chooser-upload") else: uploadform = None q = None is_searching = False if "q" in request.GET or "p" in request.GET or "collection_id" in request.GET: collection_id = request.GET.get("collection_id") if collection_id: media_files = media_files.filter(collection=collection_id) searchform = SearchForm(request.GET) if searchform.is_valid(): q = searchform.cleaned_data["q"] media_files = media_files.search(q) is_searching = True else: media_files = media_files.order_by("-created_at") is_searching = False # Pagination paginator, media_files = paginate(request, media_files, per_page=10) return render( request, "wagtailmedia/chooser/results.html", { "media_files": media_files, "query_string": q, "is_searching": is_searching, "pagination_template": pagination_template, }, ) else: searchform = SearchForm() collections = Collection.objects.all() if len(collections) < 2: collections = None media_files = media_files.order_by("-created_at") paginator, media_files = paginate(request, media_files, per_page=10) return render_modal_workflow( request, "wagtailmedia/chooser/chooser.html", None, { "media_files": media_files, "searchform": searchform, "collections": collections, "uploadform": uploadform, "is_searching": False, "pagination_template": pagination_template, }, json_data={ "step": "chooser", "error_label": "Server Error", "error_message": "Report this error to your webmaster with the following information:", "tag_autocomplete_url": reverse("wagtailadmin_tag_autocomplete"), }, )
def __init__(self, request): self.request = request self.summary_items = [] for fn in hooks.get_hooks('construct_homepage_summary_items'): fn(request, self.summary_items)
def chooser_upload(request, media_type): Media = get_media_model() MediaForm = get_media_form(Media) if request.method == "POST": media = Media(uploaded_by_user=request.user, type=media_type) form = MediaForm( request.POST, request.FILES, instance=media, user=request.user, prefix="media-chooser-upload", ) if form.is_valid(): form.save() # Reindex the media entry to make sure all tags are indexed for backend in get_search_backends(): backend.add(media) return render_modal_workflow( request, None, None, None, json_data={ "step": "media_chosen", "result": get_media_json(media) }, ) media_files = permission_policy.instances_user_has_any_permission_for( request.user, ["change", "delete"]) # allow hooks to modify the queryset for hook in hooks.get_hooks("construct_media_chooser_queryset"): media_files = hook(media_files, request) searchform = SearchForm() collections = Collection.objects.all() if len(collections) < 2: collections = None media_files = media_files.order_by("-created_at") paginator, media_files = paginate(request, media_files, per_page=10) context = { "media_files": media_files, "searchform": searchform, "collections": collections, "uploadform": form, "is_searching": False, "pagination_template": pagination_template, "media_type": media_type, } return render_modal_workflow( request, "wagtailmedia/chooser/chooser.html", None, context, json_data={"step": "chooser"}, )
def serve(request, document_id, document_filename): Document = get_document_model() doc = get_object_or_404(Document, id=document_id) # We want to ensure that the document filename provided in the URL matches the one associated with the considered # document_id. If not we can't be sure that the document the user wants to access is the one corresponding to the # <document_id, document_filename> pair. if doc.filename != document_filename: raise Http404('This document does not match the given filename.') for fn in hooks.get_hooks('before_serve_document'): result = fn(doc, request) if isinstance(result, HttpResponse): return result # Send document_served signal document_served.send(sender=Document, instance=doc, request=request) try: local_path = doc.file.path except NotImplementedError: local_path = None try: direct_url = doc.file.url except NotImplementedError: direct_url = None serve_method = getattr(settings, 'WAGTAILDOCS_SERVE_METHOD', None) # If no serve method has been specified, select an appropriate default for the storage backend: # redirect for remote storages (i.e. ones that provide a url but not a local path) and # serve_view for all other cases if serve_method is None: if direct_url and not local_path: serve_method = 'redirect' else: serve_method = 'serve_view' if serve_method in ('redirect', 'direct') and direct_url: # Serve the file by redirecting to the URL provided by the underlying storage; # this saves the cost of delivering the file via Python. # For serve_method == 'direct', this view should not normally be reached # (the document URL as used in links should point directly to the storage URL instead) # but we handle it as a redirect to provide sensible fallback / # backwards compatibility behaviour. return redirect(direct_url) if local_path: # Use wagtail.utils.sendfile to serve the file; # this provides support for mimetypes, if-modified-since and django-sendfile backends if hasattr(settings, 'SENDFILE_BACKEND'): return sendfile(request, local_path, attachment=True, attachment_filename=doc.filename) else: # Fallback to streaming backend if user hasn't specified SENDFILE_BACKEND return sendfile( request, local_path, attachment=True, attachment_filename=doc.filename, backend=sendfile_streaming_backend.sendfile ) else: # We are using a storage backend which does not expose filesystem paths # (e.g. storages.backends.s3boto.S3BotoStorage) AND the developer has not allowed # redirecting to the file url directly. # Fall back on pre-sendfile behaviour of reading the file content and serving it # as a StreamingHttpResponse wrapper = FileWrapper(doc.file) response = StreamingHttpResponse(wrapper, content_type='application/octet-stream') response['Content-Disposition'] = 'attachment; filename=%s' % doc.filename # FIXME: storage backends are not guaranteed to implement 'size' response['Content-Length'] = doc.file.size return response
def create(request, content_type_app_name, content_type_model_name, parent_page_id): parent_page = get_object_or_404(Page, id=parent_page_id).specific parent_page_perms = parent_page.permissions_for_user(request.user) if not parent_page_perms.can_add_subpage(): raise PermissionDenied try: content_type = ContentType.objects.get_by_natural_key( content_type_app_name, content_type_model_name) except ContentType.DoesNotExist: raise Http404 # Get class page_class = content_type.model_class() # Make sure the class is a descendant of Page if not issubclass(page_class, Page): raise Http404 # page must be in the list of allowed subpage types for this parent ID if page_class not in parent_page.creatable_subpage_models(): raise PermissionDenied if not page_class.can_create_at(parent_page): raise PermissionDenied for fn in hooks.get_hooks('before_create_page'): result = fn(request, parent_page, page_class) if hasattr(result, 'status_code'): return result page = page_class(owner=request.user) edit_handler = page_class.get_edit_handler() form_class = edit_handler.get_form_class() next_url = get_valid_next_url_from_request(request) if request.method == 'POST': form = form_class(request.POST, request.FILES, instance=page, parent_page=parent_page) if form.is_valid(): page = form.save(commit=False) is_publishing = bool(request.POST.get( 'action-publish')) and parent_page_perms.can_publish_subpage() is_submitting = bool(request.POST.get('action-submit')) if not is_publishing: page.live = False # Save page parent_page.add_child(instance=page) # Save revision revision = page.save_revision( user=request.user, submitted_for_moderation=is_submitting, ) # Publish if is_publishing: revision.publish() # Notifications if is_publishing: if page.go_live_at and page.go_live_at > timezone.now(): messages.success( request, _("Page '{0}' created and scheduled for publishing." ).format(page.get_admin_display_title()), buttons=[ messages.button( reverse('wagtailadmin_pages:edit', args=(page.id, )), _('Edit')) ]) else: buttons = [] if page.url is not None: buttons.append( messages.button(page.url, _('View live'), new_window=True)) buttons.append( messages.button( reverse('wagtailadmin_pages:edit', args=(page.id, )), _('Edit'))) messages.success( request, _("Page '{0}' created and published.").format( page.get_admin_display_title()), buttons=buttons) elif is_submitting: messages.success( request, _("Page '{0}' created and submitted for moderation." ).format(page.get_admin_display_title()), buttons=[ messages.button(reverse( 'wagtailadmin_pages:view_draft', args=(page.id, )), _('View draft'), new_window=True), messages.button( reverse('wagtailadmin_pages:edit', args=(page.id, )), _('Edit')) ]) if not send_notification(page.get_latest_revision().id, 'submitted', request.user.pk): messages.error( request, _("Failed to send notifications to moderators")) else: messages.success( request, _("Page '{0}' created.").format( page.get_admin_display_title())) for fn in hooks.get_hooks('after_create_page'): result = fn(request, page) if hasattr(result, 'status_code'): return result if is_publishing or is_submitting: # we're done here if next_url: # redirect back to 'next' url if present return redirect(next_url) # redirect back to the explorer return redirect('wagtailadmin_explore', page.get_parent().id) else: # Just saving - remain on edit page for further edits target_url = reverse('wagtailadmin_pages:edit', args=[page.id]) if next_url: # Ensure the 'next' url is passed through again if present target_url += '?next=%s' % urlquote(next_url) return redirect(target_url) else: messages.validation_error( request, _("The page could not be created due to validation errors"), form) edit_handler = edit_handler.bind_to_instance(instance=page, form=form) has_unsaved_changes = True else: signals.init_new_page.send(sender=create, page=page, parent=parent_page) form = form_class(instance=page, parent_page=parent_page) edit_handler = edit_handler.bind_to_instance(instance=page, form=form) has_unsaved_changes = False return render( request, 'wagtailadmin/pages/create.html', { 'content_type': content_type, 'page_class': page_class, 'parent_page': parent_page, 'edit_handler': edit_handler, 'preview_modes': page.preview_modes, 'form': form, 'next': next_url, 'has_unsaved_changes': has_unsaved_changes, })
def dropdown_buttons(self): button_hooks = hooks.get_hooks(self.hook_name) return sorted(itertools.chain.from_iterable( hook(self.page, self.page_perms, self.is_parent) for hook in button_hooks))
def _scan_for_features(self): for fn in hooks.get_hooks('register_rich_text_features'): fn(self) self.has_scanned_for_features = True
def custom_admin_round_copy_view(request, page): # Custom view to handle copied Round pages. # https://github.com/wagtail/wagtail/blob/124827911463f0cb959edbb9d8d5685578540bd3/wagtail/admin/views/pages.py#L824 # Parent page defaults to parent of source page parent_page = page.get_parent() # Check if the user has permission to publish subpages on the parent can_publish = parent_page.permissions_for_user( request.user).can_publish_subpage() form = CopyForm(request.POST or None, user=request.user, page=page, can_publish=can_publish) next_url = get_valid_next_url_from_request(request) # Prefill parent_page in case the form is invalid (as prepopulated value for the form field, # because ModelChoiceField seems to not fall back to the user given value) parent_page = Page.objects.get(id=request.POST['new_parent_page']) if form.is_valid(): # Receive the parent page (this should never be empty) if form.cleaned_data['new_parent_page']: parent_page = form.cleaned_data['new_parent_page'] if not page.permissions_for_user(request.user).can_copy_to( parent_page, form.cleaned_data.get('copy_subpages')): raise PermissionDenied # Re-check if the user has permission to publish subpages on the new parent can_publish = parent_page.permissions_for_user( request.user).can_publish_subpage() # Copy the page new_page = page.copy( recursive=form.cleaned_data.get('copy_subpages'), to=parent_page, update_attrs={ 'title': form.cleaned_data['new_title'], 'slug': form.cleaned_data['new_slug'], 'start_date': None, 'end_date': None, }, keep_live=(can_publish and form.cleaned_data.get('publish_copies')), user=request.user, ) messages.info( request, _(("Please select the date in the copied page. " "Newly copied pages have NONE value for the start and end date" ))) # Give a success message back to the user if form.cleaned_data.get('copy_subpages'): messages.success( request, _("Page '{0}' and {1} subpages copied.").format( page.get_admin_display_title(), new_page.get_descendants().count())) else: messages.success( request, _("Page '{0}' copied.").format(page.get_admin_display_title())) for fn in hooks.get_hooks('after_copy_page'): result = fn(request, page, new_page) if hasattr(result, 'status_code'): return result # Redirect to explore of parent page if next_url: return redirect(next_url) return redirect('wagtailadmin_explore', parent_page.id)
from django.conf.urls import url from wagtail.api.v2.router import WagtailAPIRouter from wagtail.core import hooks from .views import PagesAdminAPIViewSet admin_api = WagtailAPIRouter('wagtailadmin_api') admin_api.register_endpoint('pages', PagesAdminAPIViewSet) for fn in hooks.get_hooks('construct_admin_api'): fn(admin_api) urlpatterns = [ url(r'^main/', admin_api.urls), ]
def index(request, parent_page_id=None): if parent_page_id: parent_page = get_object_or_404(Page, id=parent_page_id) else: parent_page = Page.get_first_root_node() # This will always succeed because of the @user_passes_test above. root_page = get_explorable_root_page(request.user) # If this page isn't a descendant of the user's explorable root page, # then redirect to that explorable root page instead. if not ( parent_page.pk == root_page.pk or parent_page.is_descendant_of(root_page) ): return redirect('wagtailadmin_explore', root_page.pk) parent_page = parent_page.specific user_perms = UserPagePermissionsProxy(request.user) pages = ( parent_page.get_children().prefetch_related( "content_type", "sites_rooted_here" ) & user_perms.explorable_pages() ) # Get page ordering ordering = request.GET.get('ordering', '-latest_revision_created_at') if ordering not in [ 'title', '-title', 'content_type', '-content_type', 'live', '-live', 'latest_revision_created_at', '-latest_revision_created_at', 'ord' ]: ordering = '-latest_revision_created_at' if ordering == 'ord': # preserve the native ordering from get_children() pass elif ordering == 'latest_revision_created_at': # order by oldest revision first. # Special case NULL entries - these should go at the top of the list. # Do this by annotating with Count('latest_revision_created_at'), # which returns 0 for these pages = pages.annotate( null_position=Count('latest_revision_created_at') ).order_by('null_position', 'latest_revision_created_at') elif ordering == '-latest_revision_created_at': # order by oldest revision first. # Special case NULL entries - these should go at the end of the list. pages = pages.annotate( null_position=Count('latest_revision_created_at') ).order_by('-null_position', '-latest_revision_created_at') else: pages = pages.order_by(ordering) # Don't paginate if sorting by page order - all pages must be shown to # allow drag-and-drop reordering do_paginate = ordering != 'ord' # We want specific page instances, but do not need streamfield values here pages = pages.defer_streamfields().specific() # allow hooks defer_streamfieldsyset for hook in hooks.get_hooks('construct_explorer_page_queryset'): pages = hook(parent_page, pages, request) # Annotate queryset with various states to be used later for performance optimisations if getattr(settings, 'WAGTAIL_WORKFLOW_ENABLED', True): pages = pages.prefetch_workflow_states() pages = pages.annotate_site_root_state().annotate_approved_schedule() # Pagination if do_paginate: paginator = Paginator(pages, per_page=50) pages = paginator.get_page(request.GET.get('p')) context = { 'parent_page': parent_page.specific, 'ordering': ordering, 'pagination_query_params': "ordering=%s" % ordering, 'pages': pages, 'do_paginate': do_paginate, 'locale': None, 'translations': [], } if getattr(settings, 'WAGTAIL_I18N_ENABLED', False) and not parent_page.is_root(): context.update({ 'locale': parent_page.locale, 'translations': [ { 'locale': translation.locale, 'url': reverse('wagtailadmin_explore', args=[translation.id]), } for translation in parent_page.get_translations().only('id', 'locale').select_related('locale') ], }) return TemplateResponse(request, 'wagtailadmin/pages/index.html', context)
def process_response(self, request: WSGIRequest, response: HttpResponse) -> HttpResponse: if not wagtailcache_settings.WAGTAIL_CACHE: return response if getattr(request, "_wagtailcache_skip", False): # If we should skip this response, add header and return. _patch_header(response, Status.SKIP) return response if not getattr(request, "_wagtailcache_update", False): # We don't need to update the cache, just return. return response # Check if the response is cacheable # Don't cache private or no-cache responses. # Do cache 200, 301, 302, 304, and 404 codes so that wagtail doesn't # have to repeatedly look up these URLs in the database. # Don't cache streaming responses. is_cacheable = \ CacheControl.NOCACHE.value not in response.get("Cache-Control", "") and \ CacheControl.PRIVATE.value not in response.get("Cache-Control", "") and \ response.status_code in (200, 301, 302, 304, 404) and \ not response.streaming # Don't cache 200 responses that set a user-specific cookie in response to # a cookie-less request (e.g. CSRF tokens). if is_cacheable and response.status_code == 200: is_cacheable = not ( not request.COOKIES and response.cookies and has_vary_header(response, 'Cookie') ) # Allow the user to override our caching decision. for fn in hooks.get_hooks('is_response_cacheable'): result = fn(response, is_cacheable) if isinstance(result, bool): is_cacheable = result # If we are not allowed to cache the response, just return. if not is_cacheable: # Add a response header to indicate this was intentionally not cached. _patch_header(response, Status.SKIP) return response # Try to get the timeout from the "max-age" section of the "Cache- # Control" header before reverting to using the cache's default. timeout = get_max_age(response) if timeout is None: timeout = self._wagcache.default_timeout patch_response_headers(response, timeout) if timeout: cache_key = learn_cache_key(request, response, timeout, None, cache=self._wagcache) if isinstance(response, SimpleTemplateResponse): response.add_post_render_callback( lambda r: self._wagcache.set(cache_key, r, timeout) ) else: self._wagcache.set(cache_key, response, timeout) # Add a response header to indicate this was a cache miss. _patch_header(response, Status.MISS) return response
def populate(self): for fn in hooks.get_hooks('register_admin_viewset'): viewset = fn() self.register(viewset)
def browse(request, parent_page_id=None): # A missing or empty page_type parameter indicates 'all page types' # (i.e. descendants of wagtailcore.page) page_type_string = request.GET.get('page_type') or 'wagtailcore.page' user_perm = request.GET.get('user_perms', False) try: desired_classes = page_models_from_string(page_type_string) except (ValueError, LookupError): raise Http404 # Find parent page if parent_page_id: parent_page = get_object_or_404(Page, id=parent_page_id) elif desired_classes == (Page,): # Just use the root page parent_page = Page.get_first_root_node() else: # Find the highest common ancestor for the specific classes passed in # In many cases, such as selecting an EventPage under an EventIndex, # this will help the administrator find their page quicker. all_desired_pages = Page.objects.all().type(*desired_classes) parent_page = all_desired_pages.first_common_ancestor() parent_page = parent_page.specific # Get children of parent page (without streamfields) pages = parent_page.get_children().defer_streamfields().specific() # allow hooks to modify the queryset for hook in hooks.get_hooks('construct_page_chooser_queryset'): pages = hook(pages, request) # Filter them by page type if desired_classes != (Page,): # restrict the page listing to just those pages that: # - are of the given content type (taking into account class inheritance) # - or can be navigated into (i.e. have children) choosable_pages = pages.type(*desired_classes) descendable_pages = pages.filter(numchild__gt=0) pages = choosable_pages | descendable_pages can_choose_root = request.GET.get('can_choose_root', False) # Do permission lookups for this user now, instead of for every page. permission_proxy = UserPagePermissionsProxy(request.user) # Parent page can be chosen if it is a instance of desired_classes parent_page.can_choose = can_choose_page( parent_page, permission_proxy, desired_classes, can_choose_root, user_perm) # Pagination # We apply pagination first so we don't need to walk the entire list # in the block below paginator = Paginator(pages, per_page=25) pages = paginator.get_page(request.GET.get('p')) # Annotate each page with can_choose/can_decend flags for page in pages: page.can_choose = can_choose_page(page, permission_proxy, desired_classes, can_choose_root, user_perm) page.can_descend = page.get_children_count() # Render context = shared_context(request, { 'parent_page': parent_page, 'parent_page_id': parent_page.pk, 'pages': pages, 'search_form': SearchForm(), 'page_type_string': page_type_string, 'page_type_names': [desired_class.get_verbose_name() for desired_class in desired_classes], 'page_types_restricted': (page_type_string != 'wagtailcore.page') }) return render_modal_workflow( request, 'wagtailadmin/chooser/browse.html', None, context, json_data={'step': 'browse', 'parent_page_id': context['parent_page_id']}, )
def user_listing_buttons(context, user): button_hooks = hooks.get_hooks('register_user_listing_buttons') buttons = sorted(itertools.chain.from_iterable( hook(context, user) for hook in button_hooks)) return {'user': user, 'buttons': buttons}
def __init__(self, request, **kwargs): self.request = request self.context = kwargs self.context['request'] = request page = self.context.get('page') user_page_permissions = UserPagePermissionsProxy(self.request.user) self.context['user_page_permissions'] = user_page_permissions if page: self.context[ 'user_page_permissions_tester'] = user_page_permissions.for_page( page) self.menu_items = [] if page: task = page.current_workflow_task current_workflow_state = page.current_workflow_state is_final_task = current_workflow_state and current_workflow_state.is_at_final_task if task: actions = task.get_actions(page, request.user) workflow_menu_items = [] for name, label, launch_modal in actions: icon_name = 'edit' if name == "approve": if is_final_task and not getattr( settings, 'WAGTAIL_WORKFLOW_REQUIRE_REAPPROVAL_ON_EDIT', False): label = _("%(label)s and Publish") % { 'label': label } icon_name = 'success' item = WorkflowMenuItem(name, label, launch_modal, icon_name=icon_name) if requires_request_arg(item.is_shown): warn( "%s.is_shown should no longer take a 'request' argument. " "See https://docs.wagtail.io/en/stable/releases/2.15.html#template-components-2-15" % type(item).__name__, category=RemovedInWagtail217Warning) is_shown = item.is_shown(self.request, self.context) else: is_shown = item.is_shown(self.context) if is_shown: workflow_menu_items.append(item) self.menu_items.extend(workflow_menu_items) for menu_item in _get_base_page_action_menu_items(): if requires_request_arg(menu_item.is_shown): warn( "%s.is_shown should no longer take a 'request' argument. " "See https://docs.wagtail.io/en/stable/releases/2.15.html#template-components-2-15" % type(menu_item).__name__, category=RemovedInWagtail217Warning) is_shown = menu_item.is_shown(self.request, self.context) else: is_shown = menu_item.is_shown(self.context) if is_shown: self.menu_items.append(menu_item) self.menu_items.sort(key=lambda item: item.order) for hook in hooks.get_hooks('construct_page_action_menu'): hook(self.menu_items, self.request, self.context) try: self.default_item = self.menu_items.pop(0) except IndexError: self.default_item = None
def edit(request, page_id): real_page_record = get_object_or_404(Page, id=page_id) latest_revision = real_page_record.get_latest_revision() content_type = real_page_record.cached_content_type page_class = real_page_record.specific_class if page_class is None: raise PageClassNotFoundError( f"The page '{real_page_record}' cannot be edited because the " f"model class used to create it ({content_type.app_label}." f"{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." ) page = real_page_record.get_latest_revision_as_page() parent = page.get_parent() page_perms = page.permissions_for_user(request.user) if not page_perms.can_edit(): raise PermissionDenied next_url = get_valid_next_url_from_request(request) for fn in hooks.get_hooks('before_edit_page'): result = fn(request, page) if hasattr(result, 'status_code'): return result edit_handler = page_class.get_edit_handler() edit_handler = edit_handler.bind_to(instance=page, request=request) form_class = edit_handler.get_form_class() if request.method == 'GET': if page_perms.user_has_lock(): if page.locked_at: lock_message = format_html(_("<b>Page '{}' was locked</b> by <b>you</b> on <b>{}</b>."), page.get_admin_display_title(), page.locked_at.strftime("%d %b %Y %H:%M")) else: lock_message = format_html(_("<b>Page '{}' is locked</b> by <b>you</b>."), 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=(page.id,)), _("Unlock") ) messages.warning(request, lock_message, extra_tags='lock') elif page.locked and 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 page.locked_by and page.locked_at: lock_message = format_html(_("<b>Page '{}' was locked</b> by <b>{}</b> on <b>{}</b>."), page.get_admin_display_title(), str(page.locked_by), 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>."), page.get_admin_display_title()) if 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=(page.id,)), _("Unlock") ) messages.error(request, lock_message, extra_tags='lock') if page.current_workflow_state: workflow_state = page.current_workflow_state workflow = workflow_state.workflow workflow_tasks = workflow_state.all_tasks_with_status() task = workflow_state.current_task_state.task if ( workflow_state.status != WorkflowState.STATUS_NEEDS_CHANGES and task.specific.page_locked_for_user(page, request.user) ): # Check for revisions still undergoing moderation and warn if len(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(request, mark_safe(workflow_info + " " + _("Only reviewers for this task can edit the page.")), extra_tags="lock") # Check for revisions still undergoing moderation and warn - this is for the old moderation system if latest_revision and latest_revision.submitted_for_moderation: buttons = [] if page.live: buttons.append(messages.button( reverse('wagtailadmin_pages:revisions_compare', args=(page.id, 'live', latest_revision.id)), _('Compare with live version') )) messages.warning(request, _("This page is currently awaiting moderation"), buttons=buttons) # Show current workflow state if set, default to last workflow state workflow_state = page.current_workflow_state or page.workflow_states.order_by('created_at').last() if workflow_state: workflow_tasks = workflow_state.all_tasks_with_status() else: workflow_tasks = [] errors_debug = None if request.method == 'POST': form = form_class(request.POST, request.FILES, instance=page, parent_page=parent) is_publishing = False is_submitting = False is_restarting_workflow = False is_reverting = False is_saving = False is_cancelling_workflow = bool(request.POST.get('action-cancel-workflow')) and workflow_state and workflow_state.user_can_cancel(request.user) if is_cancelling_workflow: workflow_state.cancel(user=request.user) # do this here so even if the page is locked due to not having permissions, the original submitter can still cancel the workflow if form.is_valid() and not page_perms.page_locked(): page = form.save(commit=False) is_publishing = bool(request.POST.get('action-publish')) and page_perms.can_publish() is_submitting = bool(request.POST.get('action-submit')) and page_perms.can_submit_for_moderation() is_restarting_workflow = bool(request.POST.get('action-restart-workflow')) and page_perms.can_submit_for_moderation() and workflow_state and workflow_state.user_can_cancel(request.user) is_reverting = bool(request.POST.get('revision')) is_performing_workflow_action = bool(request.POST.get('action-workflow-action')) if is_performing_workflow_action: workflow_action = request.POST['workflow-action-name'] available_actions = page.current_workflow_task.get_actions(page, request.user) available_action_names = [name for name, verbose_name, modal in available_actions] if workflow_action not in available_action_names: # prevent this action is_performing_workflow_action = False is_saving = True has_content_changes = form.has_changed() if is_restarting_workflow: workflow_state.cancel(user=request.user) # If a revision ID was passed in the form, get that revision so its # date can be referenced in notification messages if is_reverting: previous_revision = get_object_or_404(page.revisions, id=request.POST.get('revision')) if is_performing_workflow_action and not has_content_changes: # don't save a new revision, as we're just going to update the page's # workflow state with no content changes revision = latest_revision else: # Save revision revision = page.save_revision( user=request.user, log_action=True, # Always log the new revision on edit previous_revision=(previous_revision if is_reverting else None) ) # store submitted go_live_at for messaging below go_live_at = page.go_live_at # Publish if is_publishing: for fn in hooks.get_hooks('before_publish_page'): result = fn(request, page) if hasattr(result, 'status_code'): return result revision.publish( user=request.user, changed=has_content_changes, previous_revision=(previous_revision if is_reverting else None) ) # Need to reload the page because the URL may have changed, and we # need the up-to-date URL for the "View Live" button. page = page.specific_class.objects.get(pk=page.pk) for fn in hooks.get_hooks('after_publish_page'): result = fn(request, page) if hasattr(result, 'status_code'): return result # Submit if is_submitting or is_restarting_workflow: if workflow_state and workflow_state.status == WorkflowState.STATUS_NEEDS_CHANGES: # If the workflow was in the needs changes state, resume the existing workflow on submission workflow_state.resume(request.user) else: # Otherwise start a new workflow workflow = page.get_workflow() workflow.start(page, request.user) if is_performing_workflow_action: extra_workflow_data_json = request.POST.get('workflow-action-extra-data', '{}') extra_workflow_data = json.loads(extra_workflow_data_json) page.current_workflow_task.on_action(page.current_workflow_task_state, request.user, workflow_action, **extra_workflow_data) # Notifications if is_publishing: if go_live_at and go_live_at > timezone.now(): # Page has been scheduled for publishing in the future if is_reverting: message = _( "Version from {0} of page '{1}' has been scheduled for publishing." ).format( previous_revision.created_at.strftime("%d %b %Y %H:%M"), page.get_admin_display_title() ) else: if page.live: message = _( "Page '{0}' is live and this version has been scheduled for publishing." ).format( page.get_admin_display_title() ) else: message = _( "Page '{0}' has been scheduled for publishing." ).format( page.get_admin_display_title() ) messages.success(request, message, buttons=[ messages.button( reverse('wagtailadmin_pages:edit', args=(page.id,)), _('Edit') ) ]) else: # Page is being published now if is_reverting: message = _( "Version from {0} of page '{1}' has been published." ).format( previous_revision.created_at.strftime("%d %b %Y %H:%M"), page.get_admin_display_title() ) else: message = _( "Page '{0}' has been published." ).format( page.get_admin_display_title() ) buttons = [] if page.url is not None: buttons.append(messages.button(page.url, _('View live'), new_window=True)) buttons.append(messages.button(reverse('wagtailadmin_pages:edit', args=(page_id,)), _('Edit'))) messages.success(request, message, buttons=buttons) elif is_submitting: message = _( "Page '{0}' has been submitted for moderation." ).format( page.get_admin_display_title() ) messages.success(request, message, buttons=[ messages.button( reverse('wagtailadmin_pages:view_draft', args=(page_id,)), _('View draft'), new_window=True ), messages.button( reverse('wagtailadmin_pages:edit', args=(page_id,)), _('Edit') ) ]) elif is_cancelling_workflow: message = _( "Workflow on page '{0}' has been cancelled." ).format( page.get_admin_display_title() ) messages.success(request, message, buttons=[ messages.button( reverse('wagtailadmin_pages:view_draft', args=(page_id,)), _('View draft'), new_window=True ), messages.button( reverse('wagtailadmin_pages:edit', args=(page_id,)), ('Edit') ) ]) elif is_restarting_workflow: message = _( "Workflow on page '{0}' has been restarted." ).format( page.get_admin_display_title() ) messages.success(request, message, buttons=[ messages.button( reverse('wagtailadmin_pages:view_draft', args=(page_id,)), _('View draft'), new_window=True ), messages.button( reverse('wagtailadmin_pages:edit', args=(page_id,)), _('Edit') ) ]) elif is_reverting: message = _( "Page '{0}' has been replaced with version from {1}." ).format( page.get_admin_display_title(), previous_revision.created_at.strftime("%d %b %Y %H:%M") ) messages.success(request, message) elif is_saving: message = _( "Page '{0}' has been updated." ).format( page.get_admin_display_title() ) messages.success(request, message) if is_saving: for fn in hooks.get_hooks('after_edit_page'): result = fn(request, page) if hasattr(result, 'status_code'): return result if is_publishing or is_submitting or is_restarting_workflow or is_performing_workflow_action: # we're done here - redirect back to the explorer if next_url: # redirect back to 'next' url if present return redirect(next_url) # redirect back to the explorer return redirect('wagtailadmin_explore', page.get_parent().id) else: # Just saving - remain on edit page for further edits target_url = reverse('wagtailadmin_pages:edit', args=[page.id]) if next_url: # Ensure the 'next' url is passed through again if present target_url += '?next=%s' % urlquote(next_url) return redirect(target_url) else: if page_perms.page_locked(): messages.error(request, _("The page could not be saved as it is locked")) else: messages.validation_error( request, _("The page could not be saved due to validation errors"), form ) errors_debug = ( repr(form.errors) + repr([ (name, formset.errors) for (name, formset) in form.formsets.items() if formset.errors ]) ) has_unsaved_changes = True else: form = form_class(instance=page, parent_page=parent) has_unsaved_changes = False edit_handler = edit_handler.bind_to(form=form) # Check for revisions still undergoing moderation and warn if latest_revision and latest_revision.submitted_for_moderation: buttons = [] if page.live: buttons.append(messages.button( reverse('wagtailadmin_pages:revisions_compare', args=(page.id, 'live', latest_revision.id)), _('Compare with live version') )) messages.warning(request, _("This page is currently awaiting moderation"), buttons=buttons) if page.live and page.has_unpublished_changes: # Page status needs to present the version of the page containing the correct live URL page_for_status = real_page_record.specific else: page_for_status = page return TemplateResponse(request, 'wagtailadmin/pages/edit.html', { 'page': page, 'page_for_status': page_for_status, 'content_type': content_type, 'edit_handler': edit_handler, 'errors_debug': errors_debug, 'action_menu': PageActionMenu(request, view='edit', page=page), 'preview_modes': page.preview_modes, 'form': form, 'next': next_url, 'has_unsaved_changes': has_unsaved_changes, 'page_locked': page_perms.page_locked(), 'workflow_state': workflow_state if workflow_state and workflow_state.is_active else None, 'current_task_state': page.current_workflow_task_state, 'publishing_will_cancel_workflow': workflow_tasks and getattr(settings, 'WAGTAIL_WORKFLOW_CANCEL_ON_PUBLISH', True) })