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(): with transaction.atomic(): user = form.save() log(user, 'wagtail.create') 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 TemplateResponse(request, 'wagtailusers/users/create.html', { 'form': form, })
def delete(request, user_id): user = get_object_or_404(User, pk=user_id) if not user_can_delete_user(request.user, user): raise PermissionDenied for fn in hooks.get_hooks("before_delete_user"): result = fn(request, user) if hasattr(result, "status_code"): return result if request.method == "POST": with transaction.atomic(): log(user, "wagtail.delete") 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 TemplateResponse( request, "wagtailusers/users/confirm_delete.html", { "user": user, }, )
def add(request): if request.method == "POST": form = RedirectForm(request.POST, request.FILES) if form.is_valid(): with transaction.atomic(): theredirect = form.save() log(instance=theredirect, action="wagtail.edit") messages.success( request, _("Redirect '{0}' added.").format(theredirect.title), buttons=[ messages.button( reverse("wagtailredirects:edit", args=(theredirect.id, )), _("Edit"), ) ], ) return redirect("wagtailredirects:index") else: messages.error( request, _("The redirect could not be created due to errors.")) else: form = RedirectForm() return TemplateResponse( request, "wagtailredirects/add.html", { "form": form, }, )
def post(self, request, *args, **kwargs): try: msg = _("%(model_name)s '%(instance)s' deleted.") % { "model_name": self.verbose_name, "instance": self.instance, } with transaction.atomic(): log(instance=self.instance, action="wagtail.delete") self.delete_instance() messages.success(request, msg) return redirect(self.index_url) except models.ProtectedError: linked_objects = [] fields = self.model._meta.fields_map.values() fields = (obj for obj in fields if not isinstance(obj.field, ManyToManyField)) for rel in fields: if rel.on_delete == models.PROTECT: if isinstance(rel, OneToOneRel): try: obj = getattr(self.instance, rel.get_accessor_name()) except ObjectDoesNotExist: pass else: linked_objects.append(obj) else: qs = getattr(self.instance, rel.get_accessor_name()) for obj in qs.all(): linked_objects.append(obj) context = self.get_context_data(protected_error=True, linked_objects=linked_objects) return self.render_to_response(context)
def _unpublish_page(self, page, set_expired, commit, user, log_action): """ Unpublish the page by setting ``live`` to ``False``. Does nothing if ``live`` is already ``False`` :param log_action: flag for logging the action. Pass False to skip logging. Can be passed an action string. Defaults to 'wagtail.unpublish' """ if page.live: page.live = False page.has_unpublished_changes = True page.live_revision = None if set_expired: page.expired = True if commit: # using clean=False to bypass validation page.save(clean=False) page_unpublished.send(sender=page.specific_class, instance=page.specific) if log_action: log( instance=page, action=log_action if isinstance(log_action, str) else "wagtail.unpublish", user=user, ) logger.info('Page unpublished: "%s" id=%d', page.title, page.id) page.revisions.update(approved_go_live_at=None) # Unpublish aliases for alias in page.aliases.all(): alias.unpublish()
def log_deletion(self, page): log( instance=page, action="wagtail.delete", user=self.user, deleted=True, )
def _convert_alias(self, page, log_action, user): page.alias_of_id = None page.save(update_fields=["alias_of_id"], clean=False) # Create an initial revision revision = page.save_revision(user=user, changed=False, clean=False) if page.live: page.live_revision = revision page.save(update_fields=["live_revision"], clean=False) # Log if log_action: log( instance=page, action=log_action, revision=revision, user=user, data={ "page": { "id": page.id, "title": page.get_admin_display_title() }, }, ) return page
def delete(self, request, *args, **kwargs): self.object = self.get_object() success_url = self.get_success_url() with transaction.atomic(): log(instance=self.object, action='wagtail.delete') self.object.delete() messages.success(request, self.get_success_message()) return HttpResponseRedirect(success_url)
def edit(request, app_name, model_name, site_pk): model = get_model_from_url_params(app_name, model_name) if not user_can_edit_setting_type(request.user, model): raise PermissionDenied site = get_object_or_404(Site, pk=site_pk) setting_type_name = model._meta.verbose_name instance = model.for_site(site) edit_handler = get_setting_edit_handler(model) edit_handler = edit_handler.bind_to(instance=instance, request=request) form_class = edit_handler.get_form_class() if request.method == "POST": form = form_class(request.POST, request.FILES, instance=instance) if form.is_valid(): with transaction.atomic(): form.save() log(instance, "wagtail.edit") messages.success( request, _("%(setting_type)s updated.") % { "setting_type": capfirst(setting_type_name), "instance": instance }, ) return redirect("wagtailsettings:edit", app_name, model_name, site.pk) else: messages.validation_error( request, _("The setting could not be saved due to errors."), form) else: form = form_class(instance=instance) edit_handler = edit_handler.bind_to(form=form) # Show a site switcher form if there are multiple sites site_switcher = None if Site.objects.count() > 1: site_switcher = SiteSwitchForm(site, model) return TemplateResponse( request, "wagtailsettings/edit.html", { "opts": model._meta, "setting_type_name": setting_type_name, "instance": instance, "edit_handler": edit_handler, "form": form, "site": site, "site_switcher": site_switcher, "tabbed": isinstance(edit_handler, TabbedInterface), }, )
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(): with transaction.atomic(): user = form.save() log(user, "wagtail.edit") if user == request.user and "password1" in form.changed_data: # User is changing their own password; need to update their session hash update_session_auth_hash(request, user) 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 TemplateResponse( request, "wagtailusers/users/edit.html", { "user": user, "form": form, "can_delete": can_delete, }, )
def delete(request, query_id): query = get_object_or_404(Query, id=query_id) if request.method == 'POST': editors_picks = query.editors_picks.all() with transaction.atomic(): for search_pick in editors_picks: log(search_pick, 'wagtail.delete') editors_picks.delete() messages.success(request, _("Editor's picks deleted.")) return redirect('wagtailsearchpromotions:index') return TemplateResponse(request, 'wagtailsearchpromotions/confirm_delete.html', { 'query': query, })
def form_valid(self, form): self.form = form with transaction.atomic(): self.object = self.save_instance() log(instance=self.object, action='wagtail.edit') success_message = self.get_success_message() if success_message is not None: messages.success(self.request, success_message, buttons=[ messages.button( reverse(self.edit_url_name, args=(self.object.id, )), _('Edit')) ]) return redirect(self.get_success_url())
def add(request): if request.method == 'POST': # Get query query_form = search_forms.QueryForm(request.POST) if query_form.is_valid(): query = Query.get(query_form['query_string'].value()) # Save search picks searchpicks_formset = forms.SearchPromotionsFormSet(request.POST, instance=query) if save_searchpicks(query, query, searchpicks_formset): for search_pick in searchpicks_formset.new_objects: log(search_pick, 'wagtail.create') messages.success( request, _("Editor's picks for '{0}' created.").format(query), buttons=[ messages.button( reverse('wagtailsearchpromotions:edit', args=(query.id, )), _('Edit')) ]) return redirect('wagtailsearchpromotions:index') else: if len(searchpicks_formset.non_form_errors()): # formset level error (e.g. no forms submitted) messages.error( request, " ".join(error for error in searchpicks_formset.non_form_errors())) else: # specific errors will be displayed within form fields messages.error( request, _("Recommendations have not been created due to errors" )) else: searchpicks_formset = forms.SearchPromotionsFormSet() else: query_form = search_forms.QueryForm() searchpicks_formset = forms.SearchPromotionsFormSet() return TemplateResponse( request, 'wagtailsearchpromotions/add.html', { 'query_form': query_form, 'searchpicks_formset': searchpicks_formset, 'form_media': query_form.media + searchpicks_formset.media, })
def log_scheduling_action(self): log( instance=self.page, action="wagtail.publish.schedule", user=self.user, data={ "revision": { "id": self.revision.id, "created": self.revision.created_at.strftime("%d %b %Y %H:%M"), "go_live_at": self.page.go_live_at.strftime("%d %b %Y %H:%M"), "has_live_version": self.page.live, } }, revision=self.revision, content_changed=self.changed, )
def edit(request, redirect_id): theredirect = get_object_or_404(models.Redirect, id=redirect_id) if not permission_policy.user_has_permission_for_instance( request.user, "change", theredirect): raise PermissionDenied if request.method == "POST": form = RedirectForm(request.POST, request.FILES, instance=theredirect) if form.is_valid(): with transaction.atomic(): form.save() log(instance=theredirect, action="wagtail.edit") messages.success( request, _("Redirect '{0}' updated.").format(theredirect.title), buttons=[ messages.button( reverse("wagtailredirects:edit", args=(theredirect.id, )), _("Edit"), ) ], ) return redirect("wagtailredirects:index") else: messages.error(request, _("The redirect could not be saved due to errors.")) else: form = RedirectForm(instance=theredirect) return TemplateResponse( request, "wagtailredirects/edit.html", { "redirect": theredirect, "form": form, "user_can_delete": permission_policy.user_has_permission(request.user, "delete"), }, )
def delete(request, redirect_id): theredirect = get_object_or_404(models.Redirect, id=redirect_id) if not permission_policy.user_has_permission_for_instance( request.user, 'delete', theredirect): raise PermissionDenied if request.method == 'POST': with transaction.atomic(): log(instance=theredirect, action='wagtail.delete') theredirect.delete() messages.success( request, _("Redirect '{0}' deleted.").format(theredirect.title)) return redirect('wagtailredirects:index') return TemplateResponse(request, "wagtailredirects/confirm_delete.html", { 'redirect': theredirect, })
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(): with transaction.atomic(): user = form.save() log(user, "wagtail.create") 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 TemplateResponse( request, "wagtailusers/users/create.html", { "form": form, }, )
def create_redirects_from_dataset(dataset, config): errors = [] successes = 0 total = 0 for row in dataset: total += 1 from_link = row[config["from_index"]] to_link = row[config["to_index"]] data = { "old_path": from_link, "redirect_link": to_link, "is_permanent": config["permanent"], } if config["site"]: data["site"] = config["site"].pk form = RedirectForm(data) if not form.is_valid(): error = to_readable_errors(form.errors.as_text()) errors.append([from_link, to_link, error]) continue with transaction.atomic(): redirect = form.save() log(instance=redirect, action="wagtail.create") successes += 1 return { "errors": errors, "errors_count": len(errors), "successes": successes, "total": total, }
def edit(request, redirect_id): theredirect = get_object_or_404(models.Redirect, id=redirect_id) if not permission_policy.user_has_permission_for_instance( request.user, 'change', theredirect): raise PermissionDenied if request.method == 'POST': form = RedirectForm(request.POST, request.FILES, instance=theredirect) if form.is_valid(): with transaction.atomic(): form.save() log(instance=theredirect, action='wagtail.edit') messages.success( request, _("Redirect '{0}' updated.").format(theredirect.title), buttons=[ messages.button( reverse('wagtailredirects:edit', args=(theredirect.id, )), _('Edit')) ]) return redirect('wagtailredirects:index') else: messages.error(request, _("The redirect could not be saved due to errors.")) else: form = RedirectForm(instance=theredirect) return TemplateResponse( request, "wagtailredirects/edit.html", { 'redirect': theredirect, 'form': form, 'user_can_delete': permission_policy.user_has_permission(request.user, 'delete'), })
def save_searchpicks(query, new_query, searchpicks_formset): # Save if searchpicks_formset.is_valid(): # Set sort_order for i, form in enumerate(searchpicks_formset.ordered_forms): form.instance.sort_order = i # Make sure the form is marked as changed so it gets saved with the new order form.has_changed = lambda: True # log deleted items before saving, otherwise we lose their IDs items_for_deletion = [ form.instance for form in searchpicks_formset.deleted_forms if form.instance.pk ] with transaction.atomic(): for search_pick in items_for_deletion: log(search_pick, 'wagtail.delete') searchpicks_formset.save() for search_pick in searchpicks_formset.new_objects: log(search_pick, 'wagtail.create') # If query was changed, move all search picks to the new query if query != new_query: searchpicks_formset.get_queryset().update(query=new_query) # log all items in the formset as having changed for search_pick, changed_fields in searchpicks_formset.changed_objects: log(search_pick, 'wagtail.edit') else: # only log objects with actual changes for search_pick, changed_fields in searchpicks_formset.changed_objects: if changed_fields: log(search_pick, 'wagtail.edit') return True else: return False
def create(request, app_label, model_name): model = get_snippet_model_from_url_params(app_label, model_name) permission = get_permission_name("add", model) if not request.user.has_perm(permission): raise PermissionDenied for fn in hooks.get_hooks("before_create_snippet"): result = fn(request, model) if hasattr(result, "status_code"): return result instance = model() # Set locale of the new instance if issubclass(model, TranslatableMixin): selected_locale = request.GET.get("locale") if selected_locale: instance.locale = get_object_or_404(Locale, language_code=selected_locale) else: instance.locale = Locale.get_default() # Make edit handler edit_handler = get_snippet_edit_handler(model) edit_handler = edit_handler.bind_to(request=request) form_class = edit_handler.get_form_class() if request.method == "POST": form = form_class(request.POST, request.FILES, instance=instance) if form.is_valid(): with transaction.atomic(): form.save() log(instance=instance, action="wagtail.create") messages.success( request, _("%(snippet_type)s '%(instance)s' created.") % { "snippet_type": capfirst(model._meta.verbose_name), "instance": instance, }, buttons=[ messages.button( reverse( "wagtailsnippets:edit", args=(app_label, model_name, quote(instance.pk)), ), _("Edit"), ) ], ) for fn in hooks.get_hooks("after_create_snippet"): result = fn(request, instance) if hasattr(result, "status_code"): return result urlquery = "" if (isinstance(instance, TranslatableMixin) and instance.locale is not Locale.get_default()): urlquery = "?locale=" + instance.locale.language_code return redirect( reverse("wagtailsnippets:list", args=[app_label, model_name]) + urlquery) else: messages.validation_error( request, _("The snippet could not be created due to errors."), form) else: form = form_class(instance=instance) edit_handler = edit_handler.bind_to(instance=instance, form=form) context = { "model_opts": model._meta, "edit_handler": edit_handler, "form": form, "action_menu": SnippetActionMenu(request, view="create", model=model), "locale": None, "translations": [], } if getattr(settings, "WAGTAIL_I18N_ENABLED", False) and issubclass( model, TranslatableMixin): context.update({ "locale": instance.locale, "translations": [{ "locale": locale, "url": reverse("wagtailsnippets:add", args=[app_label, model_name]) + "?locale=" + locale.language_code, } for locale in Locale.objects.all().exclude(id=instance.locale.id) ], }) return TemplateResponse(request, "wagtailsnippets/snippets/create.html", context)
def create(request, app_label, model_name): model = get_snippet_model_from_url_params(app_label, model_name) permission = get_permission_name('add', model) if not request.user.has_perm(permission): raise PermissionDenied for fn in hooks.get_hooks('before_create_snippet'): result = fn(request, model) if hasattr(result, 'status_code'): return result instance = model() # Set locale of the new instance if issubclass(model, TranslatableMixin): selected_locale = request.GET.get('locale') if selected_locale: instance.locale = get_object_or_404(Locale, language_code=selected_locale) else: instance.locale = Locale.get_default() # Make edit handler edit_handler = get_snippet_edit_handler(model) edit_handler = edit_handler.bind_to(request=request) form_class = edit_handler.get_form_class() if request.method == 'POST': form = form_class(request.POST, request.FILES, instance=instance) if form.is_valid(): with transaction.atomic(): form.save() log(instance=instance, action='wagtail.create') messages.success( request, _("%(snippet_type)s '%(instance)s' created.") % { 'snippet_type': capfirst(model._meta.verbose_name), 'instance': instance }, buttons=[ messages.button( reverse('wagtailsnippets:edit', args=(app_label, model_name, quote(instance.pk))), _('Edit')) ]) for fn in hooks.get_hooks('after_create_snippet'): result = fn(request, instance) if hasattr(result, 'status_code'): return result urlquery = '' if isinstance(instance, TranslatableMixin ) and instance.locale is not Locale.get_default(): urlquery = '?locale=' + instance.locale.language_code return redirect( reverse('wagtailsnippets:list', args=[app_label, model_name]) + urlquery) else: messages.validation_error( request, _("The snippet could not be created due to errors."), form) else: form = form_class(instance=instance) edit_handler = edit_handler.bind_to(instance=instance, form=form) context = { 'model_opts': model._meta, 'edit_handler': edit_handler, 'form': form, 'action_menu': SnippetActionMenu(request, view='create', model=model), 'locale': None, 'translations': [], } if getattr(settings, 'WAGTAIL_I18N_ENABLED', False) and issubclass( model, TranslatableMixin): context.update({ 'locale': instance.locale, 'translations': [{ 'locale': locale, 'url': reverse('wagtailsnippets:add', args=[app_label, model_name]) + '?locale=' + locale.language_code } for locale in Locale.objects.all().exclude(id=instance.locale.id) ], }) return TemplateResponse(request, 'wagtailsnippets/snippets/create.html', context)
def edit(request, app_label, model_name, pk): model = get_snippet_model_from_url_params(app_label, model_name) permission = get_permission_name('change', model) if not request.user.has_perm(permission): raise PermissionDenied instance = get_object_or_404(model, pk=unquote(pk)) for fn in hooks.get_hooks('before_edit_snippet'): result = fn(request, instance) if hasattr(result, 'status_code'): return result edit_handler = get_snippet_edit_handler(model) edit_handler = edit_handler.bind_to(instance=instance, request=request) form_class = edit_handler.get_form_class() if request.method == 'POST': form = form_class(request.POST, request.FILES, instance=instance) if form.is_valid(): with transaction.atomic(): form.save() log(instance=instance, action='wagtail.edit') messages.success( request, _("%(snippet_type)s '%(instance)s' updated.") % { 'snippet_type': capfirst(model._meta.verbose_name), 'instance': instance }, buttons=[ messages.button( reverse('wagtailsnippets:edit', args=(app_label, model_name, quote(instance.pk))), _('Edit')) ]) for fn in hooks.get_hooks('after_edit_snippet'): result = fn(request, instance) if hasattr(result, 'status_code'): return result return redirect('wagtailsnippets:list', app_label, model_name) else: messages.validation_error( request, _("The snippet could not be saved due to errors."), form) else: form = form_class(instance=instance) edit_handler = edit_handler.bind_to(form=form) latest_log_entry = log_registry.get_logs_for_instance(instance).first() context = { 'model_opts': model._meta, 'instance': instance, 'edit_handler': edit_handler, 'form': form, 'action_menu': SnippetActionMenu(request, view='edit', instance=instance), 'locale': None, 'translations': [], 'latest_log_entry': latest_log_entry, } if getattr(settings, 'WAGTAIL_I18N_ENABLED', False) and issubclass( model, TranslatableMixin): context.update({ 'locale': instance.locale, 'translations': [{ 'locale': translation.locale, 'url': reverse('wagtailsnippets:edit', args=[app_label, model_name, quote(translation.pk)]) } for translation in instance.get_translations().select_related( 'locale')], }) return TemplateResponse(request, 'wagtailsnippets/snippets/edit.html', context)
def _move_page(self, page, target, pos=None): from wagtail.core.models import Page # Determine old and new parents parent_before = page.get_parent() if pos in ("first-child", "last-child", "sorted-child"): parent_after = target else: parent_after = target.get_parent() # Determine old and new url_paths # Fetching new object to avoid affecting `page` old_page = Page.objects.get(id=page.id) old_url_path = old_page.url_path new_url_path = old_page.set_url_path(parent=parent_after) url_path_changed = old_url_path != new_url_path # Emit pre_page_move signal pre_page_move.send( sender=page.specific_class or page.__class__, instance=page, parent_page_before=parent_before, parent_page_after=parent_after, url_path_before=old_url_path, url_path_after=new_url_path, ) # Only commit when all descendants are properly updated with transaction.atomic(): # Allow treebeard to update `path` values MP_MoveHandler(page, target, pos).process() # Treebeard's move method doesn't actually update the in-memory instance, # so we need to work with a freshly loaded one now new_page = Page.objects.get(id=page.id) new_page.url_path = new_url_path new_page.save() # Update descendant paths if url_path has changed if url_path_changed: new_page._update_descendant_url_paths(old_url_path, new_url_path) # Emit post_page_move signal post_page_move.send( sender=page.specific_class or page.__class__, instance=new_page, parent_page_before=parent_before, parent_page_after=parent_after, url_path_before=old_url_path, url_path_after=new_url_path, ) # Log log( instance=page, action='wagtail.move' if url_path_changed else 'wagtail.reorder', user=self.user, data={ "source": { "id": parent_before.id, "title": parent_before.specific_deferred.get_admin_display_title(), }, "destination": { "id": parent_after.id, "title": parent_after.specific_deferred.get_admin_display_title(), }, }, ) logger.info('Page moved: "%s" id=%d path=%s', page.title, page.id, new_url_path)
def account(request): # Fetch the user and profile objects once and pass into each panel # We need to use the same instances for all forms so they don't overwrite each other user = request.user profile = UserProfile.get_for_user(user) # Panels panels = [ NameEmailSettingsPanel(request, user, profile), AvatarSettingsPanel(request, user, profile), NotificationsSettingsPanel(request, user, profile), LocaleSettingsPanel(request, user, profile), ChangePasswordPanel(request, user, profile), ] for fn in hooks.get_hooks("register_account_settings_panel"): panel = fn(request, user, profile) if panel and panel.is_active(): panels.append(panel) panels = [panel for panel in panels if panel.is_active()] # Get tabs and order them tabs = list({panel.tab for panel in panels}) tabs.sort(key=lambda tab: tab.order) # Get dict of tabs to ordered panels panels_by_tab = OrderedDict([(tab, []) for tab in tabs]) for panel in panels: panels_by_tab[panel.tab].append(panel) for tab, tab_panels in panels_by_tab.items(): tab_panels.sort(key=lambda panel: panel.order) panel_forms = [panel.get_form() for panel in panels] if request.method == "POST": if all(form.is_valid() or not form.is_bound for form in panel_forms): with transaction.atomic(): for form in panel_forms: if form.is_bound: form.save() log(user, "wagtail.edit") # Prevent a password change from logging this user out update_session_auth_hash(request, user) # Override the language when creating the success message # If the user has changed their language in this request, the message should # be in the new language, not the existing one with override(profile.get_preferred_language()): messages.success( request, _("Your account settings have been changed successfully!")) return redirect("wagtailadmin_account") media = Media() for form in panel_forms: media += form.media # Menu items menu_items = [] for fn in hooks.get_hooks("register_account_menu_item"): item = fn(request) if item: menu_items.append(item) return TemplateResponse( request, "wagtailadmin/account/account.html", { "panels_by_tab": panels_by_tab, "menu_items": menu_items, "media": media, }, )
def _create_alias(self, page, *, recursive, parent, update_slug, update_locale, user, log_action, reset_translation_key, _mpnode_attrs): specific_page = page.specific # FIXME: Switch to the same fields that are excluded from copy # We can't do this right now because we can't exclude fields from with_content_json # which we use for updating aliases exclude_fields = [ "id", "path", "depth", "numchild", "url_path", "path", "index_entries", "postgres_index_entries", ] update_attrs = { "alias_of": page, # Aliases don't have revisions so the draft title should always match the live title "draft_title": page.title, # Likewise, an alias page can't have unpublished changes if it's live "has_unpublished_changes": not page.live, } if update_slug: update_attrs["slug"] = update_slug if update_locale: update_attrs["locale"] = update_locale if user: update_attrs["owner"] = user # When we're not copying for translation, we should give the translation_key a new value if reset_translation_key: update_attrs["translation_key"] = uuid.uuid4() alias, child_object_map = _copy(specific_page, update_attrs=update_attrs, exclude_fields=exclude_fields) # Update any translatable child objects for (child_relation, old_pk), child_object in child_object_map.items(): if isinstance(child_object, TranslatableMixin): if update_locale: child_object.locale = update_locale # When we're not copying for translation, # we should give the translation_key a new value for each child object as well. if reset_translation_key: child_object.translation_key = uuid.uuid4() # Save the new page if _mpnode_attrs: # We've got a tree position already reserved. Perform a quick save. alias.path = _mpnode_attrs[0] alias.depth = _mpnode_attrs[1] alias.save(clean=False) else: if parent: alias = parent.add_child(instance=alias) else: alias = page.add_sibling(instance=alias) _mpnode_attrs = (alias.path, alias.depth) _copy_m2m_relations(specific_page, alias, exclude_fields=exclude_fields) # Log if log_action: source_parent = specific_page.get_parent() log( instance=alias, action=log_action, user=user, data={ "page": { "id": alias.id, "title": alias.get_admin_display_title() }, "source": { "id": source_parent.id, "title": source_parent.specific_deferred. get_admin_display_title(), } if source_parent else None, "destination": { "id": parent.id, "title": parent.specific_deferred.get_admin_display_title(), } if parent else None, }, ) if alias.live: # Log the publish log( instance=alias, action="wagtail.publish", user=user, ) logger.info('Page alias created: "%s" id=%d from=%d', alias.title, alias.id, page.id) # Copy child pages if recursive: from wagtail.core.models import Page numchild = 0 for child_page in page.get_children().specific(): newdepth = _mpnode_attrs[1] + 1 child_mpnode_attrs = ( Page._get_path(_mpnode_attrs[0], newdepth, numchild), newdepth, ) numchild += 1 self._create_alias( child_page, recursive=True, parent=alias, update_slug=None, update_locale=update_locale, user=user, log_action=log_action, reset_translation_key=reset_translation_key, _mpnode_attrs=child_mpnode_attrs, ) if numchild > 0: alias.numchild = numchild alias.save(clean=False, update_fields=["numchild"]) return alias
def _publish_page_revision(self, revision, page, user, changed, log_action, previous_revision): from wagtail.core.models import COMMENTS_RELATION_NAME, PageRevision if page.go_live_at and page.go_live_at > timezone.now(): page.has_unpublished_changes = True # Instead set the approved_go_live_at of this revision revision.approved_go_live_at = page.go_live_at revision.save() # And clear the the approved_go_live_at of any other revisions page.revisions.exclude(id=revision.id).update( approved_go_live_at=None) # if we are updating a currently live page skip the rest if page.live_revision: # Log scheduled publishing if log_action: self.log_scheduling_action() return # if we have a go_live in the future don't make the page live page.live = False else: page.live = True # at this point, the page has unpublished changes if and only if there are newer revisions than this one page.has_unpublished_changes = not revision.is_latest_revision() # If page goes live clear the approved_go_live_at of all revisions page.revisions.update(approved_go_live_at=None) page.expired = False # When a page is published it can't be expired # Set first_published_at, last_published_at and live_revision # if the page is being published now if page.live: now = timezone.now() page.last_published_at = now page.live_revision = revision if page.first_published_at is None: page.first_published_at = now if previous_revision: previous_revision_page = previous_revision.as_page_object() old_page_title = (previous_revision_page.title if page.title != previous_revision_page.title else None) else: try: previous = revision.get_previous() except PageRevision.DoesNotExist: previous = None old_page_title = (previous.page.title if previous and page.title != previous.page.title else None) else: # Unset live_revision if the page is going live in the future page.live_revision = None page.save() for comment in getattr(page, COMMENTS_RELATION_NAME).all().only("position"): comment.save(update_fields=["position"]) revision.submitted_for_moderation = False page.revisions.update(submitted_for_moderation=False) workflow_state = page.current_workflow_state if workflow_state and getattr( settings, "WAGTAIL_WORKFLOW_CANCEL_ON_PUBLISH", True): workflow_state.cancel(user=user) if page.live: page_published.send(sender=page.specific_class, instance=page.specific, revision=revision) # Update alias pages page.update_aliases(revision=revision, user=user, _content_json=revision.content_json) if log_action: data = None if previous_revision: data = { "revision": { "id": previous_revision.id, "created": previous_revision.created_at.strftime( "%d %b %Y %H:%M"), } } if old_page_title: data = data or {} data["title"] = { "old": old_page_title, "new": page.title, } log( instance=page, action="wagtail.rename", user=user, data=data, revision=revision, ) log( instance=page, action=log_action if isinstance(log_action, str) else "wagtail.publish", user=user, data=data, revision=revision, content_changed=changed, ) logger.info( 'Page published: "%s" id=%d revision_id=%d', page.title, page.id, revision.id, ) elif page.go_live_at: logger.info( 'Page scheduled for publish: "%s" id=%d revision_id=%d go_live_at=%s', page.title, page.id, revision.id, page.go_live_at.isoformat(), ) if log_action: self.log_scheduling_action()
def form_valid(self, form): response = super().form_valid(form) log(instance=self.instance, action="wagtail.edit") return response
def delete(request, app_label, model_name, pk=None): model = get_snippet_model_from_url_params(app_label, model_name) permission = get_permission_name("delete", model) if not request.user.has_perm(permission): raise PermissionDenied if pk: instances = [get_object_or_404(model, pk=unquote(pk))] else: ids = request.GET.getlist("id") instances = model.objects.filter(pk__in=ids) for fn in hooks.get_hooks("before_delete_snippet"): result = fn(request, instances) if hasattr(result, "status_code"): return result count = len(instances) if request.method == "POST": with transaction.atomic(): for instance in instances: log(instance=instance, action="wagtail.delete") instance.delete() if count == 1: message_content = _("%(snippet_type)s '%(instance)s' deleted.") % { "snippet_type": capfirst(model._meta.verbose_name), "instance": instance, } else: # This message is only used in plural form, but we'll define it with ngettext so that # languages with multiple plural forms can be handled correctly (or, at least, as # correctly as possible within the limitations of verbose_name_plural...) message_content = ngettext( "%(count)d %(snippet_type)s deleted.", "%(count)d %(snippet_type)s deleted.", count, ) % { "snippet_type": capfirst(model._meta.verbose_name_plural), "count": count, } messages.success(request, message_content) for fn in hooks.get_hooks("after_delete_snippet"): result = fn(request, instances) if hasattr(result, "status_code"): return result return redirect("wagtailsnippets:list", app_label, model_name) return TemplateResponse( request, "wagtailsnippets/snippets/confirm_delete.html", { "model_opts": model._meta, "count": count, "instances": instances, "submit_url": (reverse("wagtailsnippets:delete-multiple", args=(app_label, model_name)) + "?" + urlencode([("id", instance.pk) for instance in instances])), }, )
def edit(request, app_label, model_name, pk): model = get_snippet_model_from_url_params(app_label, model_name) permission = get_permission_name("change", model) if not request.user.has_perm(permission): raise PermissionDenied instance = get_object_or_404(model, pk=unquote(pk)) for fn in hooks.get_hooks("before_edit_snippet"): result = fn(request, instance) if hasattr(result, "status_code"): return result edit_handler = get_snippet_edit_handler(model) edit_handler = edit_handler.bind_to(instance=instance, request=request) form_class = edit_handler.get_form_class() if request.method == "POST": form = form_class(request.POST, request.FILES, instance=instance) if form.is_valid(): with transaction.atomic(): form.save() log(instance=instance, action="wagtail.edit") messages.success( request, _("%(snippet_type)s '%(instance)s' updated.") % { "snippet_type": capfirst(model._meta.verbose_name), "instance": instance, }, buttons=[ messages.button( reverse( "wagtailsnippets:edit", args=(app_label, model_name, quote(instance.pk)), ), _("Edit"), ) ], ) for fn in hooks.get_hooks("after_edit_snippet"): result = fn(request, instance) if hasattr(result, "status_code"): return result return redirect("wagtailsnippets:list", app_label, model_name) else: messages.validation_error( request, _("The snippet could not be saved due to errors."), form) else: form = form_class(instance=instance) edit_handler = edit_handler.bind_to(form=form) latest_log_entry = log_registry.get_logs_for_instance(instance).first() context = { "model_opts": model._meta, "instance": instance, "edit_handler": edit_handler, "form": form, "action_menu": SnippetActionMenu(request, view="edit", instance=instance), "locale": None, "translations": [], "latest_log_entry": latest_log_entry, } if getattr(settings, "WAGTAIL_I18N_ENABLED", False) and issubclass( model, TranslatableMixin): context.update({ "locale": instance.locale, "translations": [{ "locale": translation.locale, "url": reverse( "wagtailsnippets:edit", args=[app_label, model_name, quote(translation.pk)], ), } for translation in instance.get_translations().select_related( "locale")], }) return TemplateResponse(request, "wagtailsnippets/snippets/edit.html", context)