def revisions_unschedule(request, page_id, revision_id): page = get_object_or_404(Page, id=page_id).specific user_perms = UserPagePermissionsProxy(request.user) if not user_perms.for_page(page).can_unschedule(): raise PermissionDenied revision = get_object_or_404(page.revisions, id=revision_id) next_url = get_valid_next_url_from_request(request) subtitle = _('revision {0} of "{1}"').format(revision.id, page.get_admin_display_title()) if request.method == 'POST': revision.approved_go_live_at = None revision.save(user=request.user, update_fields=['approved_go_live_at']) messages.success(request, _('Version {0} of "{1}" unscheduled.').format(revision.id, page.get_admin_display_title()), buttons=[ messages.button(reverse('wagtailadmin_pages:edit', args=(page.id,)), _('Edit')) ]) if next_url: return redirect(next_url) return redirect('wagtailadmin_pages:history', page.id) return TemplateResponse(request, 'wagtailadmin/pages/revisions/confirm_unschedule.html', { 'page': page, 'revision': revision, 'next': next_url, 'subtitle': subtitle })
def delete(request, document_id): Document = get_document_model() doc = get_object_or_404(Document, id=document_id) if not permission_policy.user_has_permission_for_instance( request.user, "delete", doc): raise PermissionDenied next_url = get_valid_next_url_from_request(request) if request.method == "POST": doc.delete() messages.success(request, _("Document '{0}' deleted.").format(doc.title)) return redirect(next_url) if next_url else redirect( "wagtaildocs:index") return TemplateResponse( request, "wagtaildocs/documents/confirm_delete.html", { "document": doc, "next": next_url, }, )
def add_subpage(request, parent_page_id): parent_page = get_object_or_404(Page, id=parent_page_id).specific if not parent_page.permissions_for_user(request.user).can_add_subpage(): raise PermissionDenied page_types = [( model.get_verbose_name(), model._meta.app_label, model._meta.model_name, model.get_page_description(), ) for model in type(parent_page).creatable_subpage_models() if model.can_create_at(parent_page)] # sort by lower-cased version of verbose name page_types.sort(key=lambda page_type: page_type[0].lower()) if len(page_types) == 1: # Only one page type is available - redirect straight to the create form rather than # making the user choose verbose_name, app_label, model_name, description = page_types[0] return redirect("wagtailadmin_pages:add", app_label, model_name, parent_page.id) return TemplateResponse( request, "wagtailadmin/pages/add_subpage.html", { "parent_page": parent_page, "page_types": page_types, "next": get_valid_next_url_from_request(request), }, )
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 action = DeletePageAction(page, user=request.user) # Permission checks are done above, so skip them in execute. action.execute(skip_permission_checks=True) 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 TemplateResponse(request, 'wagtailadmin/pages/confirm_delete.html', { 'page': page, 'descendant_count': page.get_descendant_count(), 'next': next_url, })
def convert_alias(request, page_id): page = get_object_or_404(Page, id=page_id, alias_of_id__isnull=False).specific if not page.permissions_for_user(request.user).can_edit(): raise PermissionDenied with transaction.atomic(): for fn in hooks.get_hooks('before_convert_alias_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': page.alias_of_id = None page.save(update_fields=['alias_of_id'], clean=False) # Create an initial revision revision = page.save_revision(user=request.user, changed=False, clean=False) if page.live: page.live_revision = revision page.save(update_fields=['live_revision'], clean=False) # Log PageLogEntry.objects.log_action( instance=page, revision=revision, action='wagtail.convert_alias', user=request.user, data={ 'page': { 'id': page.id, 'title': page.get_admin_display_title() }, }, ) messages.success( request, _("Page '{0}' has been converted into an ordinary page."). format(page.get_admin_display_title())) for fn in hooks.get_hooks('after_convert_alias_page'): result = fn(request, page) if hasattr(result, 'status_code'): return result if next_url: return redirect(next_url) return redirect('wagtailadmin_pages:edit', page.id) return TemplateResponse(request, 'wagtailadmin/pages/confirm_convert_alias.html', { 'page': page, 'next': next_url, })
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.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 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, comment_notifications=False) 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() if getattr(settings, "WAGTAIL_WORKFLOW_ENABLED", True): # Retrieve current workflow state if set, default to last workflow state self.workflow_state = ( self.page.current_workflow_state or self.page.workflow_states.order_by("created_at").last()) else: self.workflow_state = None if 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 bulk_action_choices(context, app_label, model_name): bulk_actions_list = list( bulk_action_registry.get_bulk_actions_for_model(app_label, model_name) ) bulk_actions_list.sort(key=lambda x: x.action_priority) bulk_action_more_list = [] if len(bulk_actions_list) > 4: bulk_action_more_list = bulk_actions_list[4:] bulk_actions_list = bulk_actions_list[:4] next_url = get_valid_next_url_from_request(context["request"]) if not next_url: next_url = context["request"].path bulk_action_buttons = [ PageListingButton( action.display_name, reverse( "wagtail_bulk_action", args=[app_label, model_name, action.action_type] ) + "?" + urlencode({"next": next_url}), attrs={"aria-label": action.aria_label}, priority=action.action_priority, classes=action.classes | {"bulk-action-btn"}, ) for action in bulk_actions_list ] if bulk_action_more_list: more_button = ButtonWithDropdown( label=_("More"), attrs={"title": _("View more bulk actions")}, classes={"bulk-actions-more", "dropup"}, button_classes={"button", "button-small"}, buttons_data=[ { "label": action.display_name, "url": reverse( "wagtail_bulk_action", args=[app_label, model_name, action.action_type], ) + "?" + urlencode({"next": next_url}), "attrs": {"aria-label": action.aria_label}, "priority": action.action_priority, "classes": {"bulk-action-btn"}, } for action in bulk_action_more_list ], ) more_button.is_parent = True bulk_action_buttons.append(more_button) return {"buttons": bulk_action_buttons}
def delete_related_variants(request, page): if (not isinstance(page, models.PersonalisablePageMixin) or not page.personalisation_metadata.is_canonical): return # Get a list of related personalisation metadata for all the related # variants. variants_metadata = page.personalisation_metadata.variants_metadata.select_related( "variant") next_url = get_valid_next_url_from_request(request) if request.method == "POST": parent_id = page.get_parent().id with transaction.atomic(): # To ensure variants are deleted for all descendants, start with # the deepest ones, and explicitly delete variants and metadata # for all of them, including the page itself. Otherwise protected # foreign key constraints are violated. Only consider canonical # pages. for metadata in PersonalisablePageMetadata.objects.filter( canonical_page__in=page.get_descendants(inclusive=True), variant=F("canonical_page"), ).order_by("-canonical_page__depth"): for variant_metadata in metadata.variants_metadata.select_related( "variant"): # Call delete() on objects to trigger any signals or hooks. variant_metadata.variant.delete() metadata.delete() metadata.canonical_page.delete() msg = _("Page '{0}' and its variants deleted.") messages.success(request, msg.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/wagtail_personalisation/confirm_delete.html", { "page": page, "descendant_count": page.get_descendant_count(), "next": next_url, "variants": Page.objects.filter( pk__in=variants_metadata.values_list("variant_id")), }, )
def ion_unpublish(request, page_id): page = get_object_or_404(Page, id=page_id).specific # TODO permission handling postponed # user_perms = UserPagePermissionsProxy(request.user) # if not user_perms.for_page(page).can_unpublish(): # raise PermissionDenied next_url = get_valid_next_url_from_request(request) if request.method == 'POST': include_descendants = True for fn in hooks.get_hooks('before_unpublish_page'): result = fn(request, page) if hasattr(result, 'status_code'): return result page.unpublish(user=request.user) if include_descendants: live_descendant_pages = page.get_descendants().live().specific() for live_descendant_page in live_descendant_pages: # if user_perms.for_page(live_descendant_page).can_unpublish(): live_descendant_page.unpublish() for fn in hooks.get_hooks('after_unpublish_page'): result = fn(request, page) if hasattr(result, 'status_code'): return result messages.success(request, _("Page '{0}' unpublished.").format( page.get_admin_display_title()), buttons=[ messages.button( reverse('wagtailadmin_pages:edit', args=(page.id, )), _('Edit')) ]) if next_url: return redirect(next_url) return redirect('wagtailadmin_explore', page.get_parent().id) return TemplateResponse( request, 'wagtailadmin/pages/confirm_unpublish.html', { 'page': page, 'next': next_url, # 'live_descendant_count': page.get_descendants().live().count(), })
def __init__(self, request, model): self.request = request next_url = get_valid_next_url_from_request(request) if not next_url: next_url = request.path self.next_url = next_url self.num_parent_objects = self.num_child_objects = 0 if model in self.models: self.model = model else: raise Exception( "model {} is not among the specified list of models".format( model.__class__.__name__))
def unpublish(request, page_id): page = get_object_or_404(Page, id=page_id).specific user_perms = UserPagePermissionsProxy(request.user) if not user_perms.for_page(page).can_unpublish(): raise PermissionDenied next_url = get_valid_next_url_from_request(request) if request.method == "POST": include_descendants = request.POST.get("include_descendants", False) for fn in hooks.get_hooks("before_unpublish_page"): result = fn(request, page) if hasattr(result, "status_code"): return result action = UnpublishPageAction(page, user=request.user, include_descendants=include_descendants) action.execute(skip_permission_checks=True) for fn in hooks.get_hooks("after_unpublish_page"): result = fn(request, page) if hasattr(result, "status_code"): return result messages.success( request, _("Page '{0}' unpublished.").format( page.get_admin_display_title()), buttons=[ messages.button( reverse("wagtailadmin_pages:edit", args=(page.id, )), _("Edit")) ], ) if next_url: return redirect(next_url) return redirect("wagtailadmin_explore", page.get_parent().id) return TemplateResponse( request, "wagtailadmin/pages/confirm_unpublish.html", { "page": page, "next": next_url, "live_descendant_count": page.get_descendants().live().count(), }, )
def bulk_action_choices(context, app_label, model_name): bulk_actions_list = list( bulk_action_registry.get_bulk_actions_for_model(app_label, model_name)) bulk_actions_list.sort(key=lambda x: x.action_priority) bulk_action_more_list = [] if len(bulk_actions_list) > 4: bulk_action_more_list = bulk_actions_list[4:] bulk_actions_list = bulk_actions_list[:4] next_url = get_valid_next_url_from_request(context['request']) if not next_url: next_url = context['request'].path bulk_action_buttons = [ PageListingButton( action.display_name, reverse('wagtail_bulk_action', args=[app_label, model_name, action.action_type]) + '?' + urlencode({'next': next_url}), attrs={'aria-label': action.aria_label}, priority=action.action_priority, classes=action.classes | {'bulk-action-btn'}, ) for action in bulk_actions_list ] if bulk_action_more_list: more_button = ButtonWithDropdown( label=_("More"), attrs={'title': _("View more bulk actions")}, classes={'bulk-actions-more', 'dropup'}, button_classes={'button', 'button-small'}, buttons_data=[{ 'label': action.display_name, 'url': reverse('wagtail_bulk_action', args=[app_label, model_name, action.action_type]) + '?' + urlencode({'next': next_url}), 'attrs': { 'aria-label': action.aria_label }, 'priority': action.action_priority, 'classes': {'bulk-action-btn'}, } for action in bulk_action_more_list]) more_button.is_parent = True bulk_action_buttons.append(more_button) return {'buttons': bulk_action_buttons}
def ion_publish_with_children(request, page_id): page = get_object_or_404(Page, id=page_id) page_perms = page.permissions_for_user(request.user) if not page_perms.can_publish(): raise PermissionDenied revision = page.get_latest_revision() unpublished_descendant_pages = page.get_descendants().not_live() has_unpublished_alias_pages = unpublished_descendant_pages.filter( alias_of__isnull=False).exists() if has_unpublished_alias_pages: messages.error( request, "The page has unpublished alias subpages. Please publish the original page(s) first." ) next_url = get_valid_next_url_from_request(request) if request.method == 'POST': if has_unpublished_alias_pages: return redirect(next_url) if next_url else redirect( 'wagtailadmin_explore', page.get_parent().id) revision.publish() for unpublished_descendant_page in unpublished_descendant_pages: unpublished_descendant_revision = unpublished_descendant_page.get_latest_revision( ) unpublished_descendant_revision.publish() messages.success(request, _("Publishing successful.")) if next_url: return redirect(next_url) return redirect('wagtailadmin_explore', page.get_parent().id) return TemplateResponse( request, 'wagtailadmin/pages/publish_with_children.html', { 'page': page, 'next': next_url, 'unpublished_descendants': unpublished_descendant_pages, 'unpublished_descendant_count': unpublished_descendant_pages.count(), 'has_unpublished_alias_pages': has_unpublished_alias_pages, })
def upload_pofile(request, translation_id): translation = get_object_or_404(Translation, id=translation_id) instance = translation.get_target_instance() if not user_can_edit_instance(request.user, instance): raise PermissionDenied do_import = True with tempfile.NamedTemporaryFile() as f: # Note: polib.pofile accepts either a filename or contents. We cannot pass the # contents directly into polib.pofile or users could upload a file containing # a filename and this will be read by polib! f.write(request.FILES["file"].read()) f.flush() try: po = polib.pofile(f.name) except (OSError, UnicodeDecodeError): # Annoyingly, POLib uses OSError for parser exceptions... messages.error(request, _("Please upload a valid PO file.")) do_import = False if do_import: translation_id = po.metadata["X-WagtailLocalize-TranslationID"] if translation_id != str(translation.uuid): messages.error( request, _( "Cannot import PO file that was created for a different translation." ), ) do_import = False if do_import: translation.import_po(po, user=request.user, tool_name="PO File") messages.success(request, _("Successfully imported translations from PO File.")) # Work out where to redirect to next_url = get_valid_next_url_from_request(request) if not next_url: # Note: You should always provide a next URL when using this view! next_url = reverse("wagtailadmin_home") return redirect(next_url)
def stop_translation(request, translation_id): translation = get_object_or_404(Translation, id=translation_id) instance = translation.get_target_instance() if not user_can_edit_instance(request.user, instance): raise PermissionDenied translation.enabled = False translation.save(update_fields=['enabled']) next_url = get_valid_next_url_from_request(request) if not next_url: # Note: You should always provide a next URL when using this view! next_url = reverse('wagtailadmin_home') messages.success(request, _("Translation has been stopped.")) return redirect(next_url)
def revisions_unschedule(request, page_id, revision_id): page = get_object_or_404(Page, id=page_id).specific user_perms = UserPagePermissionsProxy(request.user) if not user_perms.for_page(page).can_unschedule(): raise PermissionDenied revision = get_object_or_404(page.revisions, id=revision_id) next_url = get_valid_next_url_from_request(request) subtitle = _('revision {0} of "{1}"').format( revision.id, page.get_admin_display_title()) if request.method == "POST": revision.approved_go_live_at = None revision.save(user=request.user, update_fields=["approved_go_live_at"]) messages.success( request, _('Version {0} of "{1}" unscheduled.').format( revision.id, page.get_admin_display_title()), buttons=[ messages.button( reverse("wagtailadmin_pages:edit", args=(page.id, )), _("Edit")) ], ) if next_url: return redirect(next_url) return redirect("wagtailadmin_pages:history", page.id) return TemplateResponse( request, "wagtailadmin/pages/revisions/confirm_unschedule.html", { "page": page, "revision": revision, "next": next_url, "subtitle": subtitle }, )
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.page = self.page_class(owner=self.request.user) 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() self.next_url = get_valid_next_url_from_request(self.request) return super().dispatch(request)
def convert_alias(request, page_id): page = get_object_or_404(Page, id=page_id, alias_of_id__isnull=False).specific if not page.permissions_for_user(request.user).can_edit(): raise PermissionDenied with transaction.atomic(): for fn in hooks.get_hooks("before_convert_alias_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": action = ConvertAliasPageAction(page, user=request.user) action.execute(skip_permission_checks=True) messages.success( request, _("Page '{0}' has been converted into an ordinary page."). format(page.get_admin_display_title()), ) for fn in hooks.get_hooks("after_convert_alias_page"): result = fn(request, page) if hasattr(result, "status_code"): return result if next_url: return redirect(next_url) return redirect("wagtailadmin_pages:edit", page.id) return TemplateResponse( request, "wagtailadmin/pages/confirm_convert_alias.html", { "page": page, "next": next_url, }, )
def edit_translatable_alias_page(request, page): return render( request, "wagtail_localize/admin/edit_translatable_alias.html", { "page": page, "page_for_status": page, "content_type": page.cached_content_type, "next": get_valid_next_url_from_request(request), "locale": page.locale, "translations": [ { "locale": translation.locale, "url": reverse("wagtailadmin_pages:edit", args=[translation.id]), } for translation in page.get_translations() .only("id", "locale") .select_related("locale") ], }, )
def delete(request, image_id): image = get_object_or_404(get_image_model(), id=image_id) if not permission_policy.user_has_permission_for_instance( request.user, 'delete', image): raise PermissionDenied next_url = get_valid_next_url_from_request(request) if request.method == 'POST': image.delete() messages.success(request, _("Image '{0}' deleted.").format(image.title)) return redirect(next_url) if next_url else redirect( 'wagtailimages:index') return TemplateResponse(request, "wagtailimages/images/confirm_delete.html", { 'image': image, 'next': next_url, })
def edit_translatable_alias_page(request, page): return render( request, 'wagtail_localize/admin/edit_translatable_alias.html', { 'page': page, 'page_for_status': page, 'content_type': page.cached_content_type, 'next': get_valid_next_url_from_request(request), 'locale': page.locale, 'translations': [{ 'locale': translation.locale, 'url': reverse('wagtailadmin_pages:edit', args=[translation.id]), } for translation in page.get_translations().only( 'id', 'locale').select_related('locale')], })
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: if wmt_settings.TRANSLATE_SLUGS: slug = build_localized_fieldname('slug', code) else: slug = 'slug' 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 copy(request, app_label, model_name, id): # Validate snippet has been registered and title_field is set. meta = snippet_copy_registry.get(app_label, model_name) if meta is None: raise Exception("This snippet isn't registered as copyable") try: model = apps.get_model(app_label, model_name) except LookupError: raise Http404 permission = get_permission_name('change', model) if not request.user.has_perm(permission): return permission_denied(request) snippet = get_object_or_404(model, id=id) # Create the form form = meta['copy_form_class'](request.POST or None, snippet=snippet, title_field_name=meta['title_field_name']) next_url = get_valid_next_url_from_request(request) for fn in hooks.get_hooks('before_copy_snippet'): result = fn(request, snippet) if hasattr(result, 'status_code'): return result # Check if user is submitting if request.method == 'POST': if form.is_valid(): # Copy the snippet new_snippet = form.copy() # Give a success message back to the user messages.success( request, _(f"{snippet.get_snippet_verbose_name()} '{snippet}' has been copied." ).format(snippet)) for fn in hooks.get_hooks('after_copy_snippet'): result = fn(request, snippet, new_snippet) if hasattr(result, 'status_code'): return result if next_url: return redirect(next_url) if 'wagtail.contrib.modeladmin' in settings.INSTALLED_APPS: url_helper = AdminURLHelper(new_snippet) return redirect( url_helper.get_action_url('edit', quote(new_snippet.pk))) return redirect('wagtailsnippets:edit', app_label, model_name, new_snippet.id) return render( request, 'wagtailsnippetscopy/copy.html', { 'snippet': snippet, 'app_label': app_label, 'model_name': model_name, 'form': form, 'next': next_url, })
def edit(request, document_id): Document = get_document_model() DocumentForm = get_document_form(Document) doc = get_object_or_404(Document, id=document_id) if not permission_policy.user_has_permission_for_instance( request.user, "change", doc): raise PermissionDenied next_url = get_valid_next_url_from_request(request) if request.method == "POST": original_file = doc.file form = DocumentForm(request.POST, request.FILES, instance=doc, user=request.user) if form.is_valid(): if "file" in form.changed_data: doc = form.save(commit=False) doc.file_size = doc.file.size # Set new document file hash doc.file.seek(0) doc._set_file_hash(doc.file.read()) doc.file.seek(0) doc.save() form.save_m2m() # If providing a new document file, delete the old one. # NB Doing this via original_file.delete() clears the file field, # which definitely isn't what we want... original_file.storage.delete(original_file.name) else: doc = form.save() # Reindex the document to make sure all tags are indexed search_index.insert_or_update_object(doc) edit_url = reverse("wagtaildocs:edit", args=(doc.id, )) redirect_url = "wagtaildocs:index" if next_url: edit_url = f"{edit_url}?{urlencode({'next': next_url})}" redirect_url = next_url messages.success( request, _("Document '{0}' updated").format(doc.title), buttons=[messages.button(edit_url, _("Edit"))], ) return redirect(redirect_url) else: messages.error(request, _("The document could not be saved due to errors.")) else: form = DocumentForm(instance=doc, user=request.user) try: local_path = doc.file.path except NotImplementedError: # Document is hosted externally (eg, S3) local_path = None if local_path: # Give error if document file doesn't exist if not os.path.isfile(local_path): messages.error( request, _("The file could not be found. Please change the source or delete the document" ), buttons=[ messages.button( reverse("wagtaildocs:delete", args=(doc.id, )), _("Delete")) ], ) return TemplateResponse( request, "wagtaildocs/documents/edit.html", { "document": doc, "filesize": doc.get_file_size(), "form": form, "user_can_delete": permission_policy.user_has_permission_for_instance( request.user, "delete", doc), "next": next_url, }, )
def edit(request, image_id): Image = get_image_model() ImageForm = get_image_form(Image) image = get_object_or_404(Image, id=image_id) if not permission_policy.user_has_permission_for_instance( request.user, "change", image): raise PermissionDenied next_url = get_valid_next_url_from_request(request) if request.method == "POST": original_file = image.file form = ImageForm(request.POST, request.FILES, instance=image, user=request.user) if form.is_valid(): if "file" in form.changed_data: # Set new image file size image.file_size = image.file.size # Set new image file hash image.file.seek(0) image._set_file_hash(image.file.read()) image.file.seek(0) form.save() if "file" in form.changed_data: # if providing a new image file, delete the old one and all renditions. # NB Doing this via original_file.delete() clears the file field, # which definitely isn't what we want... original_file.storage.delete(original_file.name) image.renditions.all().delete() # Reindex the image to make sure all tags are indexed search_index.insert_or_update_object(image) edit_url = reverse("wagtailimages:edit", args=(image.id, )) redirect_url = "wagtailimages:index" if next_url: edit_url = f"{edit_url}?{urlencode({'next': next_url})}" redirect_url = next_url messages.success( request, _("Image '{0}' updated.").format(image.title), buttons=[messages.button(edit_url, _("Edit again"))], ) return redirect(redirect_url) else: messages.error(request, _("The image could not be saved due to errors.")) else: form = ImageForm(instance=image, user=request.user) # Check if we should enable the frontend url generator try: reverse("wagtailimages_serve", args=("foo", "1", "bar")) url_generator_enabled = True except NoReverseMatch: url_generator_enabled = False if image.is_stored_locally(): # Give error if image file doesn't exist if not os.path.isfile(image.file.path): messages.error( request, _("The source image file could not be found. Please change the source or delete the image." ).format(image.title), buttons=[ messages.button( reverse("wagtailimages:delete", args=(image.id, )), _("Delete")) ], ) try: filesize = image.get_file_size() except SourceImageIOError: filesize = None return TemplateResponse( request, "wagtailimages/images/edit.html", { "image": image, "form": form, "url_generator_enabled": url_generator_enabled, "filesize": filesize, "user_can_delete": permission_policy.user_has_permission_for_instance( request.user, "delete", image), "next": next_url, }, )
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 wagtail_site_name = getattr(settings, "WAGTAIL_SITE_NAME", "wagtail") 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) pages_to_delete = {page} # The `construct_translated_pages_to_cascade_actions` hook returns translation and # alias pages when the action is set to "delete" if getattr(settings, "WAGTAIL_I18N_ENABLED", False): for fn in hooks.get_hooks( "construct_translated_pages_to_cascade_actions"): fn_pages = fn([page], "delete") if fn_pages and isinstance(fn_pages, dict): for additional_pages in fn_pages.values(): pages_to_delete.update(additional_pages) pages_to_delete = list(pages_to_delete) if request.method == "POST": continue_deleting = True if (request.POST.get("confirm_site_name") and request.POST.get("confirm_site_name") != wagtail_site_name): messages.error( request, f"Please type '{wagtail_site_name}' to confirm.") continue_deleting = False if continue_deleting: parent_id = page.get_parent().id # Delete the source page. action = DeletePageAction(page, user=request.user) # Permission checks are done above, so skip them in execute. action.execute(skip_permission_checks=True) # Delete translation and alias pages if they have the same parent page. if getattr(settings, "WAGTAIL_I18N_ENABLED", False): parent_page_translations = page.get_parent( ).get_translations() for page_or_alias in pages_to_delete: if page_or_alias.get_parent( ) in parent_page_translations: action = DeletePageAction(page_or_alias, user=request.user) # Permission checks are done above, so skip them in execute. action.execute(skip_permission_checks=True) 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) descendant_count = page.get_descendant_count() return TemplateResponse( request, "wagtailadmin/pages/confirm_delete.html", { "page": page, "descendant_count": descendant_count, "next": next_url, # if the number of pages ( child pages + current page) exceeds this limit, then confirm before delete. "confirm_before_delete": (descendant_count + 1) >= getattr( settings, "WAGTAILADMIN_UNSAFE_PAGE_DELETION_LIMIT", 10), "wagtail_site_name": wagtail_site_name, # note that while pages_to_delete may contain a mix of translated pages # and aliases, we count the "translations" only, as aliases are similar # to symlinks, so they should just follow the source "translation_count": len([ translation.id for translation in pages_to_delete if not translation.alias_of_id and translation.id != page.id ]), "translation_descendant_count": sum([ translation.get_descendants().filter( alias_of__isnull=True).count() for translation in pages_to_delete ]), }, )
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)
def get_success_url(self): return get_valid_next_url_from_request(self.request)
def machine_translate(request, translation_id): translation = get_object_or_404(Translation, id=translation_id) instance = translation.get_target_instance() if not user_can_edit_instance(request.user, instance): raise PermissionDenied translator = get_machine_translator() if translator is None: raise Http404 if not translator.can_translate(translation.source.locale, translation.target_locale): raise Http404 # Get segments segments = defaultdict(list) for string_segment in translation.source.stringsegment_set.all( ).select_related("context", "string"): segment = StringSegmentValue( string_segment.context.path, string_segment.string.as_value()).with_order(string_segment.order) if string_segment.attrs: segment.attrs = json.loads(string_segment.attrs) # Don't translate if there already is a translation if StringTranslation.objects.filter( translation_of_id=string_segment.string_id, locale=translation.target_locale, context_id=string_segment.context_id, ).exists(): continue segments[segment.string].append( (string_segment.string_id, string_segment.context_id)) if segments: translations = translator.translate(translation.source.locale, translation.target_locale, segments.keys()) with transaction.atomic(): for string, contexts in segments.items(): for string_id, context_id in contexts: StringTranslation.objects.get_or_create( translation_of_id=string_id, locale=translation.target_locale, context_id=context_id, defaults={ 'data': translations[string].data, 'translation_type': StringTranslation.TRANSLATION_TYPE_MACHINE, 'tool_name': translator.display_name, 'last_translated_by': request.user, 'has_error': False, 'field_error': "", }) messages.success( request, _("Successfully translated with {}.").format( translator.display_name)) else: messages.warning(request, _("There isn't anything left to translate.")) # Work out where to redirect to next_url = get_valid_next_url_from_request(request) if not next_url: # Note: You should always provide a next URL when using this view! next_url = reverse('wagtailadmin_home') return redirect(next_url)