def list(request, app_label, model_name): model = get_snippet_model_from_url_params(app_label, model_name) permissions = [ get_permission_name(action, model) for action in ['add', 'change', 'delete'] ] if not any([request.user.has_perm(perm) for perm in permissions]): return permission_denied(request) items = model.objects.all() # Preserve the snippet's model-level ordering if specified, but fall back on PK if not # (to ensure pagination is consistent) if not items.ordered: items = items.order_by('pk') # Search is_searchable = class_is_indexed(model) is_searching = False search_query = None if is_searchable and 'q' in request.GET: search_form = SearchForm(request.GET, placeholder=_("Search %(snippet_type_name)s") % { 'snippet_type_name': model._meta.verbose_name_plural }) if search_form.is_valid(): search_query = search_form.cleaned_data['q'] search_backend = get_search_backend() items = search_backend.search(search_query, items) is_searching = True else: search_form = SearchForm(placeholder=_("Search %(snippet_type_name)s") % { 'snippet_type_name': model._meta.verbose_name_plural }) paginator = Paginator(items, per_page=20) paginated_items = paginator.get_page(request.GET.get('p')) # Template if request.is_ajax(): template = 'wagtailsnippets/snippets/results.html' else: template = 'wagtailsnippets/snippets/type_index.html' return render(request, template, { 'model_opts': model._meta, 'items': paginated_items, 'can_add_snippet': request.user.has_perm(get_permission_name('add', model)), 'can_delete_snippets': request.user.has_perm(get_permission_name('delete', model)), 'is_searchable': is_searchable, 'search_form': search_form, 'is_searching': is_searching, 'query_string': search_query, })
def delete(request, app_label, model_name, pk, template='wagtailsnippets/snippets/confirm_delete.html', redirect_to=_redirect_to): model = get_snippet_model_from_url_params(app_label, model_name) permission = get_permission_name('delete', model) if not request.user.has_perm(permission): return permission_denied(request) instance = get_object_or_404(model, pk=unquote(pk)) if request.method == 'POST': instance.delete() messages.success( request, _("{snippet_type} '{instance}' deleted.").format( snippet_type=capfirst(model._meta.verbose_name_plural), instance=instance ) ) return redirect_to(app_label, model_name) return render(request, template, { 'model_opts': model._meta, 'instance': instance, })
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): return permission_denied(request) instance = get_object_or_404(model, pk=unquote(pk)) edit_handler = get_snippet_edit_handler(model) form_class = edit_handler.get_form_class() if request.method == 'POST': form = form_class(request.POST, request.FILES, instance=instance) if form.is_valid(): form.save() messages.success(request, _("{snippet_type} '{instance}' updated.").format( snippet_type=capfirst( model._meta.verbose_name_plural), instance=instance), buttons=[ messages.button( reverse('wagtailsnippets:edit', args=(app_label, model_name, quote(instance.pk))), _('Edit')) ]) return redirect('wagtailsnippets:list', app_label, model_name) else: messages.validation_error( request, _("The snippet could not be saved due to errors."), form) edit_handler = edit_handler.bind_to_instance(instance=instance, form=form, request=request) else: form = form_class(instance=instance) edit_handler = edit_handler.bind_to_instance(instance=instance, form=form, request=request) return render( request, 'wagtailsnippets/snippets/edit.html', { 'model_opts': model._meta, 'instance': instance, 'edit_handler': edit_handler, 'form': form, })
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): return permission_denied(request) instance = get_object_or_404(model, pk=unquote(pk)) edit_handler = get_snippet_edit_handler(model) form_class = edit_handler.get_form_class() if request.method == 'POST': form = form_class(request.POST, request.FILES, instance=instance) if form.is_valid(): form.save() messages.success( request, _("{snippet_type} '{instance}' updated.").format( snippet_type=capfirst(model._meta.verbose_name_plural), instance=instance ), buttons=[ messages.button(reverse( 'wagtailsnippets:edit', args=(app_label, model_name, quote(instance.pk)) ), _('Edit')) ] ) return redirect('wagtailsnippets:list', app_label, model_name) else: messages.validation_error( request, _("The snippet could not be saved due to errors."), form ) edit_handler = edit_handler.bind_to_instance(instance=instance, form=form, request=request) else: form = form_class(instance=instance) edit_handler = edit_handler.bind_to_instance(instance=instance, form=form, request=request) return render(request, 'wagtailsnippets/snippets/edit.html', { 'model_opts': model._meta, 'instance': instance, 'edit_handler': edit_handler, 'form': form, })
def create(request, app_label, model_name, template='wagtailsnippets/snippets/create.html', redirect_to=_redirect_to): model = get_snippet_model_from_url_params(app_label, model_name) permission = get_permission_name('add', model) if not request.user.has_perm(permission): return permission_denied(request) instance = model() edit_handler = get_snippet_edit_handler(model) form_class = edit_handler.get_form_class() if request.method == 'POST': form = form_class(request.POST, request.FILES, instance=instance) if form.is_valid(): form.save() messages.success(request, _("{snippet_type} '{instance}' created.").format( snippet_type=capfirst( model._meta.verbose_name), instance=instance), buttons=[ messages.button( reverse('wagtailsnippets:edit', args=(app_label, model_name, quote(instance.pk))), _('Edit')) ]) return redirect_to(app_label, model_name) else: messages.error( request, _("The snippet could not be created due to errors.")) edit_handler = edit_handler.bind_to_instance(instance=instance, form=form) else: form = form_class(instance=instance) edit_handler = edit_handler.bind_to_instance(instance=instance, form=form) return render(request, template, { 'model_opts': model._meta, 'edit_handler': edit_handler, 'form': form, })
def create(request, app_label, model_name, template='wagtailsnippets/snippets/create.html', redirect_to=_redirect_to): model = get_snippet_model_from_url_params(app_label, model_name) permission = get_permission_name('add', model) if not request.user.has_perm(permission): return permission_denied(request) instance = model() edit_handler = get_snippet_edit_handler(model) form_class = edit_handler.get_form_class() if request.method == 'POST': form = form_class(request.POST, request.FILES, instance=instance) if form.is_valid(): form.save() messages.success( request, _("{snippet_type} '{instance}' created.").format( snippet_type=capfirst(model._meta.verbose_name), instance=instance ), buttons=[ messages.button(reverse( 'wagtailsnippets:edit', args=(app_label, model_name, quote(instance.pk)) ), _('Edit')) ] ) return redirect_to(app_label, model_name) else: messages.error(request, _("The snippet could not be created due to errors.")) edit_handler = edit_handler.bind_to_instance(instance=instance, form=form) else: form = form_class(instance=instance) edit_handler = edit_handler.bind_to_instance(instance=instance, form=form) return render(request, template, { 'model_opts': model._meta, 'edit_handler': edit_handler, 'form': form, })
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): return permission_denied(request) if pk: instances = [get_object_or_404(model, pk=unquote(pk))] else: ids = request.GET.getlist('id') instances = model.objects.filter(pk__in=ids) count = len(instances) if request.method == 'POST': for instance in instances: instance.delete() if count == 1: message_content = _("{snippet_type} '{instance}' deleted.").format( snippet_type=capfirst(model._meta.verbose_name_plural), instance=instance) else: message_content = _("{count} {snippet_type} deleted.").format( snippet_type=capfirst(model._meta.verbose_name_plural), count=count) messages.success(request, message_content) return redirect('wagtailsnippets:list', app_label, model_name) return render( 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 register_snippet_listing_buttons(snippet, user, next_url=None): model = type(snippet) if user_can_edit_snippet_type(user, model): yield SnippetListingButton( _('Edit'), reverse('wagtailsnippets:edit', args=[model._meta.app_label, model._meta.model_name, quote(snippet.pk)]), attrs={'aria-label': _("Edit '%(title)s'") % {'title': str(snippet)}}, priority=10 ) if user.has_perm(get_permission_name('delete', model)): yield SnippetListingButton( _('Delete'), reverse('wagtailsnippets:delete', args=[model._meta.app_label, model._meta.model_name, quote(snippet.pk)]), attrs={'aria-label': _("Delete '%(title)s'") % {'title': str(snippet)}}, priority=20 )
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): return permission_denied(request) if pk: instances = [get_object_or_404(model, pk=unquote(pk))] else: ids = request.GET.getlist('id') instances = model.objects.filter(pk__in=ids) count = len(instances) if request.method == 'POST': for instance in instances: instance.delete() if count == 1: message_content = _("{snippet_type} '{instance}' deleted.").format( snippet_type=capfirst(model._meta.verbose_name_plural), instance=instance ) else: message_content = _("{count} {snippet_type} deleted.").format( snippet_type=capfirst(model._meta.verbose_name_plural), count=count ) messages.success(request, message_content) return redirect('wagtailsnippets:list', app_label, model_name) return render(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 delete(request, app_label, model_name, pk): model = get_snippet_model_from_url_params(app_label, model_name) permission = get_permission_name('delete', model) if not request.user.has_perm(permission): return permission_denied(request) instance = get_object_or_404(model, pk=unquote(pk)) if request.method == 'POST': instance.delete() messages.success( request, _("{snippet_type} '{instance}' deleted.").format( snippet_type=capfirst(model._meta.verbose_name_plural), instance=instance)) return redirect('wagtailsnippets:list', app_label, model_name) return render(request, 'wagtailsnippets/snippets/confirm_delete.html', { 'model_opts': model._meta, 'instance': instance, })
def list(request, app_label, model_name): model = get_snippet_model_from_url_params(app_label, model_name) permissions = [ get_permission_name(action, model) for action in ['add', 'change', 'delete'] ] if not any([request.user.has_perm(perm) for perm in permissions]): return permission_denied(request) items = model.objects.all() # Preserve the snippet's model-level ordering if specified, but fall back on PK if not # (to ensure pagination is consistent) if not items.ordered: items = items.order_by('pk') # Search is_searchable = class_is_indexed(model) is_searching = False search_query = None if is_searchable and 'q' in request.GET: search_form = SearchForm( request.GET, placeholder=_("Search %(snippet_type_name)s") % {'snippet_type_name': model._meta.verbose_name_plural}) if search_form.is_valid(): search_query = search_form.cleaned_data['q'] search_backend = get_search_backend() items = search_backend.search(search_query, items) is_searching = True else: search_form = SearchForm( placeholder=_("Search %(snippet_type_name)s") % {'snippet_type_name': model._meta.verbose_name_plural}) paginator = Paginator(items, per_page=20) paginated_items = paginator.get_page(request.GET.get('p')) # Template if request.is_ajax(): template = 'wagtailsnippets/snippets/results.html' else: template = 'wagtailsnippets/snippets/type_index.html' return TemplateResponse( request, template, { 'model_opts': model._meta, 'items': paginated_items, 'can_add_snippet': request.user.has_perm(get_permission_name('add', model)), 'can_delete_snippets': request.user.has_perm(get_permission_name('delete', model)), 'is_searchable': is_searchable, 'search_form': search_form, 'is_searching': is_searching, 'query_string': search_query, })
def edit_translation(request, translation, instance): if isinstance(instance, Page): # Page # Note: Edit permission is already checked by the edit page view page_perms = instance.permissions_for_user(request.user) is_live = instance.live is_locked = instance.locked if instance.live_revision: last_published_at = instance.live_revision.created_at last_published_by = instance.live_revision.user else: last_published_at = instance.last_published_at last_published_by = None if instance.live: live_url = instance.full_url else: live_url = None can_publish = page_perms.can_publish() can_unpublish = page_perms.can_unpublish() can_lock = page_perms.can_lock() can_unlock = page_perms.can_unlock() can_delete = page_perms.can_delete() else: # Snippet # Note: Edit permission is already checked by the edit snippet view is_live = True is_locked = False last_published_at = None last_published_by = None live_url = None can_publish = True can_unpublish = False can_lock = False can_unlock = False can_delete = request.user.has_perm( get_permission_name("delete", instance.__class__) ) source_instance = translation.source.get_source_instance() if request.method == "POST": if request.POST.get("action") == "publish": if isinstance(instance, Page): if not page_perms.can_publish(): raise PermissionDenied try: translation.save_target(user=request.user, publish=True) except ValidationError: messages.error( request, _( "New validation errors were found when publishing '{object}' in {locale}. Please fix them or click publish again to ignore these translations for now." ).format( object=str(instance), locale=translation.target_locale.get_display_name(), ), ) else: # Refresh instance to title in success message is up to date instance.refresh_from_db() string_segments = translation.source.stringsegment_set.all().order_by( "order" ) string_translations = string_segments.get_translations( translation.target_locale ) # Using annotate_translation as this ignores errors by default (so both errors and missing segments treated the same) if ( string_segments.annotate_translation(translation.target_locale) .filter(translation__isnull=True) .exists() ): # One or more strings had an error messages.warning( request, _( "Published '{object}' in {locale} with missing translations - see below." ).format( object=str(instance), locale=translation.target_locale.get_display_name(), ), ) else: messages.success( request, _("Published '{object}' in {locale}.").format( object=str(instance), locale=translation.target_locale.get_display_name(), ), ) return redirect(request.path) string_segments = translation.source.stringsegment_set.all().order_by("order") string_translations = string_segments.get_translations(translation.target_locale) overridable_segments = translation.source.overridablesegment_set.all().order_by( "order" ) segment_overrides = overridable_segments.get_overrides(translation.target_locale) related_object_segments = ( translation.source.relatedobjectsegment_set.all().order_by("order") ) tab_helper = TabHelper(source_instance) breadcrumb = [] title_segment_id = None if isinstance(instance, Page): # find the closest common ancestor of the pages that this user has direct explore permission # (i.e. add/edit/publish/lock) over; this will be the root of the breadcrumb cca = get_explorable_root_page(request.user) if cca: breadcrumb = [ { "id": page.id, "isRoot": page.is_root(), "title": page.title, "exploreUrl": reverse("wagtailadmin_explore_root") if page.is_root() else reverse("wagtailadmin_explore", args=[page.id]), } for page in instance.get_ancestors(inclusive=False).descendant_of( cca, inclusive=True ) ] # Set to the ID of a string segment that represents the title. # If this segment has a translation, the title will be replaced with that translation. try: title_segment_id = string_segments.get(context__path="title").id except StringSegment.DoesNotExist: pass machine_translator = None translator = get_machine_translator() if translator and translator.can_translate( translation.source.locale, translation.target_locale ): machine_translator = { "name": translator.display_name, "url": reverse("wagtail_localize:machine_translate", args=[translation.id]), } segments = [] for segment in string_segments: try: location_info = get_segment_location_info( source_instance, tab_helper, segment.context.path, segment.context.get_field_path(source_instance), ) except FieldHasNoEditPanelError: continue segments.append( { "type": "string", "id": segment.id, "contentPath": segment.context.path, "source": segment.string.data, "location": location_info, "editUrl": reverse( "wagtail_localize:edit_string_translation", kwargs={ "translation_id": translation.id, "string_segment_id": segment.id, }, ), "order": segment.order, } ) for segment in overridable_segments: try: location_info = get_segment_location_info( source_instance, tab_helper, segment.context.path, segment.context.get_field_path(source_instance), widget=True, ) except FieldHasNoEditPanelError: continue segments.append( { "type": "synchronised_value", "id": segment.id, "contentPath": segment.context.path, "location": location_info, "value": segment.data, "editUrl": reverse( "wagtail_localize:edit_override", kwargs={ "translation_id": translation.id, "overridable_segment_id": segment.id, }, ), "order": segment.order, } ) def get_source_object_info(segment): instance = segment.get_source_instance() if isinstance(instance, Page): return { "title": str(instance), "isLive": instance.live, "liveUrl": instance.full_url, "editUrl": reverse("wagtailadmin_pages:edit", args=[instance.id]), "createTranslationRequestUrl": reverse( "wagtail_localize:submit_page_translation", args=[instance.id] ), } else: return { "title": str(instance), "isLive": True, "editUrl": reverse( "wagtailsnippets:edit", args=[ instance._meta.app_label, instance._meta.model_name, quote(instance.id), ], ), "createTranslationRequestUrl": reverse( "wagtail_localize:submit_snippet_translation", args=[ instance._meta.app_label, instance._meta.model_name, quote(instance.id), ], ), } def get_dest_object_info(segment): instance = segment.object.get_instance_or_none(translation.target_locale) if not instance: return if isinstance(instance, Page): return { "title": str(instance), "isLive": instance.live, "liveUrl": instance.full_url, "editUrl": reverse("wagtailadmin_pages:edit", args=[instance.id]), } else: return { "title": str(instance), "isLive": True, "editUrl": reverse( "wagtailsnippets:edit", args=[ instance._meta.app_label, instance._meta.model_name, quote(instance.id), ], ), } def get_translation_progress(segment, locale): try: translation = Translation.objects.get( source__object_id=segment.object_id, target_locale=locale, enabled=True ) except Translation.DoesNotExist: return None total_segments, translated_segments = translation.get_progress() return { "totalSegments": total_segments, "translatedSegments": translated_segments, } for segment in related_object_segments: try: location_info = get_segment_location_info( source_instance, tab_helper, segment.context.path, segment.context.get_field_path(source_instance), ) except FieldHasNoEditPanelError: continue segments.append( { "type": "related_object", "id": segment.id, "contentPath": segment.context.path, "location": location_info, "order": segment.order, "source": get_source_object_info(segment), "dest": get_dest_object_info(segment), "translationProgress": get_translation_progress( segment, translation.target_locale ), } ) # Order segments by how they appear in the content panels # segment['location']['order'] is the content panel ordering # segment['order'] is the model field ordering # User's expect segments to follow the panel ordering as that's the ordering # that is used in the page editor of the source page. However, segments that # come from the same streamfield/inline panel are given the same value for # panel ordering, so we need to order by model field ordering as well (all # segments have a unique value for model field ordering) segments.sort(key=lambda segment: (segment["location"]["order"], segment["order"])) # Display a warning to the user if the schema of the source model has been updated since the source was last updated if translation.source.schema_out_of_date(): messages.warning( request, _( "The data model for '{model_name}' has been changed since the last translation sync. " "If any new fields have been added recently, these may not be visible until the next translation sync." ).format(model_name=capfirst(source_instance._meta.verbose_name)), ) return render( request, "wagtail_localize/admin/edit_translation.html", { "translation": translation, # These props are passed directly to the TranslationEditor react component "props": json.dumps( { "adminBaseUrl": reverse("wagtailadmin_home"), "object": { "title": str(instance), "titleSegmentId": title_segment_id, "isLive": is_live, "isLocked": is_locked, "lastPublishedDate": last_published_at.strftime(DATE_FORMAT) if last_published_at is not None else None, "lastPublishedBy": UserSerializer(last_published_by).data if last_published_by is not None else None, "liveUrl": live_url, }, "breadcrumb": breadcrumb, "tabs": tab_helper.tabs_with_slugs, "sourceLocale": { "code": translation.source.locale.language_code, "displayName": translation.source.locale.get_display_name(), }, "locale": { "code": translation.target_locale.language_code, "displayName": translation.target_locale.get_display_name(), }, "translations": [ { "title": str(translated_instance), "locale": { "code": translated_instance.locale.language_code, "displayName": translated_instance.locale.get_display_name(), }, "editUrl": reverse( "wagtailadmin_pages:edit", args=[translated_instance.id] ) if isinstance(translated_instance, Page) else reverse( "wagtailsnippets:edit", args=[ translated_instance._meta.app_label, translated_instance._meta.model_name, quote(translated_instance.id), ], ), } for translated_instance in instance.get_translations().select_related( "locale" ) ], "perms": { "canPublish": can_publish, "canUnpublish": can_unpublish, "canLock": can_lock, "canUnlock": can_unlock, "canDelete": can_delete, }, "links": { "downloadPofile": reverse( "wagtail_localize:download_pofile", args=[translation.id] ), "uploadPofile": reverse( "wagtail_localize:upload_pofile", args=[translation.id] ), "unpublishUrl": reverse( "wagtailadmin_pages:unpublish", args=[instance.id] ) if isinstance(instance, Page) else None, "lockUrl": reverse( "wagtailadmin_pages:lock", args=[instance.id] ) if isinstance(instance, Page) else None, "unlockUrl": reverse( "wagtailadmin_pages:unlock", args=[instance.id] ) if isinstance(instance, Page) else None, "deleteUrl": reverse( "wagtailadmin_pages:delete", args=[instance.id] ) if isinstance(instance, Page) else reverse( "wagtailsnippets:delete", args=[ instance._meta.app_label, instance._meta.model_name, quote(instance.pk), ], ), "stopTranslationUrl": reverse( "wagtail_localize:stop_translation", args=[translation.id] ), }, "previewModes": [ { "mode": mode, "label": label, "url": reverse( "wagtail_localize:preview_translation", args=[translation.id], ) if mode == instance.default_preview_mode else reverse( "wagtail_localize:preview_translation", args=[translation.id, mode], ), } for mode, label in ( instance.preview_modes if isinstance(instance, Page) else [] ) ], "machineTranslator": machine_translator, "segments": segments, # We serialize the translation data using Django REST Framework. # This gives us a consistent representation with the APIs so we # can dynamically update translations in the view. "initialStringTranslations": StringTranslationSerializer( string_translations, many=True, context={"translation_source": translation.source}, ).data, "initialOverrides": SegmentOverrideSerializer( segment_overrides, many=True, context={"translation_source": translation.source}, ).data, }, cls=DjangoJSONEncoder, ), }, )
def edit(request, poll_pk): from ..models import Poll model = Poll poll = get_object_or_404(Poll, pk=poll_pk) permission = get_permission_name('change', model) if not request.user.has_perm(permission): return permission_denied(request) instance = poll for fn in hooks.get_hooks('before_edit_snippet'): result = fn(request, instance) if hasattr(result, 'status_code'): return result edit_handler = get_poll_edit_handler(Poll) 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(): form.save() messages.success( request, _("%(snippet_type)s '%(instance)s' updated.") % { 'snippet_type': capfirst(model._meta.verbose_name), 'instance': instance }, buttons=[ messages.button( reverse('wagtailpolls_edit', args=(poll, )), _('Edit')) ]) for fn in hooks.get_hooks('after_edit_snippet'): result = fn(request, instance) if hasattr(result, 'status_code'): return result return redirect('wagtailpolls_index') 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) return TemplateResponse( request, 'wagtailpolls/edit.html', { 'modal_opts': model._meta, 'instance': instance, 'poll': instance, 'edit_handler': edit_handler, 'form': form })
def is_shown(self, context): if context["view"] == "edit" and context["instance"].live: publish_permission = get_permission_name("publish", context["model"]) return context["request"].user.has_perm(publish_permission) return False
def is_shown(self, context): publish_permission = get_permission_name("publish", context["model"]) return context["request"].user.has_perm(publish_permission)
def is_shown(self, context): delete_permission = get_permission_name("delete", context["model"]) return context["view"] == "edit" and context["request"].user.has_perm( delete_permission)
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 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 list(request, app_label, model_name): model = get_snippet_model_from_url_params(app_label, model_name) permissions = [ get_permission_name(action, model) for action in ['add', 'change', 'delete'] ] if not any([request.user.has_perm(perm) for perm in permissions]): raise PermissionDenied items = model.objects.all() enable_locale_filter = getattr(settings, 'WAGTAIL_I18N_ENABLED', False) and issubclass( model, TranslatableMixin) if enable_locale_filter: if 'locale' in request.GET: try: locale = Locale.objects.get( language_code=request.GET['locale']) except Locale.DoesNotExist: # Redirect to snippet without locale return redirect('wagtailsnippets:list', app_label, model_name) else: # Default to active locale (this will take into account the user's chosen admin language) locale = Locale.get_active() items = items.filter(locale=locale) else: locale = None # Preserve the snippet's model-level ordering if specified, but fall back on PK if not # (to ensure pagination is consistent) if not items.ordered: items = items.order_by('pk') # Search is_searchable = class_is_indexed(model) is_searching = False search_query = None if is_searchable and 'q' in request.GET: search_form = SearchForm( request.GET, placeholder=_("Search %(snippet_type_name)s") % {'snippet_type_name': model._meta.verbose_name_plural}) if search_form.is_valid(): search_query = search_form.cleaned_data['q'] search_backend = get_search_backend() items = search_backend.search(search_query, items) is_searching = True else: search_form = SearchForm( placeholder=_("Search %(snippet_type_name)s") % {'snippet_type_name': model._meta.verbose_name_plural}) paginator = Paginator(items, per_page=20) paginated_items = paginator.get_page(request.GET.get('p')) # Template if request.is_ajax(): template = 'wagtailsnippets/snippets/results.html' else: template = 'wagtailsnippets/snippets/type_index.html' context = { 'model_opts': model._meta, 'items': paginated_items, 'can_add_snippet': request.user.has_perm(get_permission_name('add', model)), 'can_delete_snippets': request.user.has_perm(get_permission_name('delete', model)), 'is_searchable': is_searchable, 'search_form': search_form, 'is_searching': is_searching, 'query_string': search_query, 'locale': None, 'translations': [], } if enable_locale_filter: context.update({ 'locale': locale, 'translations': [{ 'locale': locale, 'url': reverse('wagtailsnippets:list', args=[app_label, model_name]) + '?locale=' + locale.language_code } for locale in Locale.objects.all().exclude(id=locale.id)], }) return TemplateResponse(request, template, 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(): form.save() 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) context = { 'model_opts': model._meta, 'instance': instance, 'edit_handler': edit_handler, 'form': form, 'action_menu': SnippetActionMenu(request, view='edit', instance=instance), 'locale': None, 'translations': [], } 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 get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) items = self.model.objects.all() enable_locale_filter = getattr(settings, 'WAGTAIL_I18N_ENABLED', False) and issubclass( self.model, TranslatableMixin) if enable_locale_filter: if 'locale' in self.request.GET: try: locale = Locale.objects.get( language_code=self.request.GET['locale']) except Locale.DoesNotExist: # Redirect to snippet without locale return redirect('wagtailsnippets:list', self.app_label, self.model_name) else: # Default to active locale (this will take into account the user's chosen admin language) locale = Locale.get_active() items = items.filter(locale=locale) else: locale = None # Preserve the snippet's model-level ordering if specified, but fall back on PK if not # (to ensure pagination is consistent) if not items.ordered: items = items.order_by('pk') # Search is_searchable = class_is_indexed(self.model) is_searching = False search_query = None if is_searchable and 'q' in self.request.GET: search_form = SearchForm( self.request.GET, placeholder=_("Search %(snippet_type_name)s") % {'snippet_type_name': self.model._meta.verbose_name_plural}) if search_form.is_valid(): search_query = search_form.cleaned_data['q'] search_backend = get_search_backend() items = search_backend.search(search_query, items) is_searching = True else: search_form = SearchForm( placeholder=_("Search %(snippet_type_name)s") % {'snippet_type_name': self.model._meta.verbose_name_plural}) paginator = Paginator(items, per_page=20) paginated_items = paginator.get_page(self.request.GET.get('p')) context.update({ 'model_opts': self.model._meta, 'items': paginated_items, 'can_add_snippet': self.request.user.has_perm(get_permission_name('add', self.model)), 'can_delete_snippets': self.request.user.has_perm( get_permission_name('delete', self.model)), 'is_searchable': is_searchable, 'search_form': search_form, 'is_searching': is_searching, 'query_string': search_query, 'locale': None, 'translations': [], }) if enable_locale_filter: context.update({ 'locale': locale, 'translations': [{ 'locale': locale, 'url': reverse('wagtailsnippets:list', args=[self.app_label, self.model_name]) + '?locale=' + locale.language_code } for locale in Locale.objects.all().exclude(id=locale.id)], }) return 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): return permission_denied(request) 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(): form.save() 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) return TemplateResponse( request, 'wagtailsnippets/snippets/edit.html', { 'model_opts': model._meta, 'instance': instance, 'edit_handler': edit_handler, 'form': form, 'action_menu': SnippetActionMenu(request, view='edit', instance=instance), })
def edit_translation(request, translation, instance): if isinstance(instance, Page): # Page # Note: Edit permission is already checked by the edit page view page_perms = instance.permissions_for_user(request.user) is_live = instance.live is_locked = instance.locked if instance.live_revision: last_published_at = instance.live_revision.created_at last_published_by = instance.live_revision.user else: last_published_at = instance.last_published_at last_published_by = None if instance.live: live_url = instance.full_url else: live_url = None can_publish = page_perms.can_publish() can_unpublish = page_perms.can_unpublish() can_lock = page_perms.can_lock() can_unlock = page_perms.can_unlock() can_delete = page_perms.can_delete() else: # Snippet # Note: Edit permission is already checked by the edit snippet view is_live = True is_locked = False last_published_at = None last_published_by = None live_url = None can_publish = True can_unpublish = False can_lock = False can_unlock = False can_delete = request.user.has_perm( get_permission_name('delete', instance.__class__)) source_instance = translation.source.get_source_instance() if request.method == 'POST': if request.POST.get('action') == 'publish': if isinstance(instance, Page): if not page_perms.can_publish(): raise PermissionDenied try: translation.save_target(user=request.user, publish=True) except ValidationError: messages.error( request, _("New validation errors were found when publishing '{object}' in {locale}. Please fix them or click publish again to ignore these translations for now." ).format( object=str(instance), locale=translation.target_locale.get_display_name())) else: # Refresh instance to title in success message is up to date instance.refresh_from_db() string_segments = translation.source.stringsegment_set.all( ).order_by('order') string_translations = string_segments.get_translations( translation.target_locale) # Using annotate_translation as this ignores errors by default (so both errors and missing segments treated the same) if string_segments.annotate_translation( translation.target_locale).filter( translation__isnull=True).exists(): # One or more strings had an error messages.warning( request, _("Published '{object}' in {locale} with missing translations - see below." ).format(object=str(instance), locale=translation.target_locale. get_display_name())) else: messages.success( request, _("Published '{object}' in {locale}.").format( object=str(instance), locale=translation.target_locale.get_display_name( ))) return redirect(request.path) string_segments = translation.source.stringsegment_set.all().order_by( 'order') string_translations = string_segments.get_translations( translation.target_locale) overridable_segments = translation.source.overridablesegment_set.all( ).order_by('order') segment_overrides = overridable_segments.get_overrides( translation.target_locale) related_object_segments = translation.source.relatedobjectsegment_set.all( ).order_by('order') tab_helper = TabHelper(source_instance) breadcrumb = [] title_segment_id = None if isinstance(instance, Page): # find the closest common ancestor of the pages that this user has direct explore permission # (i.e. add/edit/publish/lock) over; this will be the root of the breadcrumb cca = get_explorable_root_page(request.user) if cca: breadcrumb = [{ 'id': page.id, 'isRoot': page.is_root(), 'title': page.title, 'exploreUrl': reverse('wagtailadmin_explore_root') if page.is_root() else reverse('wagtailadmin_explore', args=[page.id]), } for page in instance.get_ancestors( inclusive=False).descendant_of(cca, inclusive=True)] # Set to the ID of a string segment that represents the title. # If this segment has a translation, the title will be replaced with that translation. try: title_segment_id = string_segments.get(context__path='title').id except StringSegment.DoesNotExist: pass machine_translator = None translator = get_machine_translator() if translator and translator.can_translate(translation.source.locale, translation.target_locale): machine_translator = { 'name': translator.display_name, 'url': reverse('wagtail_localize:machine_translate', args=[translation.id]), } string_segment_data = [{ 'type': 'string', 'id': segment.id, 'contentPath': segment.context.path, 'source': segment.string.data, 'location': get_segment_location_info(source_instance, tab_helper, segment.context.path), 'editUrl': reverse('wagtail_localize:edit_string_translation', kwargs={ 'translation_id': translation.id, 'string_segment_id': segment.id }), 'order': segment.order, } for segment in string_segments] syncronised_value_segment_data = [{ 'type': 'synchronised_value', 'id': segment.id, 'contentPath': segment.context.path, 'location': get_segment_location_info(source_instance, tab_helper, segment.context.path, widget=True), 'value': segment.data, 'editUrl': reverse('wagtail_localize:edit_override', kwargs={ 'translation_id': translation.id, 'overridable_segment_id': segment.id }), 'order': segment.order, } for segment in overridable_segments] def get_source_object_info(segment): instance = segment.get_source_instance() if isinstance(instance, Page): return { 'title': str(instance), 'isLive': instance.live, 'liveUrl': instance.full_url, 'editUrl': reverse('wagtailadmin_pages:edit', args=[instance.id]), 'createTranslationRequestUrl': reverse('wagtail_localize:submit_page_translation', args=[instance.id]), } else: return { 'title': str(instance), 'isLive': True, 'editUrl': reverse('wagtailsnippets:edit', args=[ instance._meta.app_label, instance._meta.model_name, quote(instance.id) ]), 'createTranslationRequestUrl': reverse('wagtail_localize:submit_snippet_translation', args=[ instance._meta.app_label, instance._meta.model_name, quote(instance.id) ]), } def get_dest_object_info(segment): instance = segment.object.get_instance_or_none( translation.target_locale) if not instance: return if isinstance(instance, Page): return { 'title': str(instance), 'isLive': instance.live, 'liveUrl': instance.full_url, 'editUrl': reverse('wagtailadmin_pages:edit', args=[instance.id]), } else: return { 'title': str(instance), 'isLive': True, 'editUrl': reverse('wagtailsnippets:edit', args=[ instance._meta.app_label, instance._meta.model_name, quote(instance.id) ]), } def get_translation_progress(segment, locale): try: translation = Translation.objects.get( source__object_id=segment.object_id, target_locale=locale, enabled=True) except Translation.DoesNotExist: return None total_segments, translated_segments = translation.get_progress() return { 'totalSegments': total_segments, 'translatedSegments': translated_segments, } related_object_segment_data = [{ 'type': 'related_object', 'id': segment.id, 'contentPath': segment.context.path, 'location': get_segment_location_info(source_instance, tab_helper, segment.context.path), 'order': segment.order, 'source': get_source_object_info(segment), 'dest': get_dest_object_info(segment), 'translationProgress': get_translation_progress(segment, translation.target_locale), } for segment in related_object_segments] segments = string_segment_data + syncronised_value_segment_data + related_object_segment_data segments.sort(key=lambda segment: segment['order']) return render( request, 'wagtail_localize/admin/edit_translation.html', { # These props are passed directly to the TranslationEditor react component 'props': json.dumps( { 'object': { 'title': str(instance), 'titleSegmentId': title_segment_id, 'isLive': is_live, 'isLocked': is_locked, 'lastPublishedDate': last_published_at.strftime(DATE_FORMAT) if last_published_at is not None else None, 'lastPublishedBy': UserSerializer(last_published_by).data if last_published_by is not None else None, 'liveUrl': live_url, }, 'breadcrumb': breadcrumb, 'tabs': tab_helper.tabs_with_slugs, 'sourceLocale': { 'code': translation.source.locale.language_code, 'displayName': translation.source.locale.get_display_name(), }, 'locale': { 'code': translation.target_locale.language_code, 'displayName': translation.target_locale.get_display_name(), }, 'translations': [{ 'title': str(translated_instance), 'locale': { 'code': translated_instance.locale.language_code, 'displayName': translated_instance.locale.get_display_name(), }, 'editUrl': reverse('wagtailadmin_pages:edit', args=[translated_instance.id]) if isinstance( translated_instance, Page) else reverse('wagtailsnippets:edit', args=[ translated_instance._meta.app_label, translated_instance._meta.model_name, quote(translated_instance.id) ]), } for translated_instance in instance.get_translations(). select_related('locale')], 'perms': { 'canPublish': can_publish, 'canUnpublish': can_unpublish, 'canLock': can_lock, 'canUnlock': can_unlock, 'canDelete': can_delete, }, 'links': { 'downloadPofile': reverse('wagtail_localize:download_pofile', args=[translation.id]), 'uploadPofile': reverse('wagtail_localize:upload_pofile', args=[translation.id]), 'unpublishUrl': reverse('wagtailadmin_pages:unpublish', args=[instance.id]) if isinstance( instance, Page) else None, 'lockUrl': reverse('wagtailadmin_pages:lock', args=[instance.id]) if isinstance(instance, Page) else None, 'unlockUrl': reverse('wagtailadmin_pages:unlock', args=[instance.id]) if isinstance( instance, Page) else None, 'deleteUrl': reverse('wagtailadmin_pages:delete', args=[instance.id]) if isinstance( instance, Page) else reverse( 'wagtailsnippets:delete', args=[ instance._meta.app_label, instance._meta.model_name, quote(instance.pk) ]), 'stopTranslationUrl': reverse('wagtail_localize:stop_translation', args=[translation.id]), }, 'previewModes': [{ 'mode': mode, 'label': label, 'url': reverse('wagtail_localize:preview_translation', args=[translation.id]) if mode == instance.default_preview_mode else reverse( 'wagtail_localize:preview_translation', args=[translation.id, mode]), } for mode, label in (instance.preview_modes if isinstance( instance, Page) else [])], 'machineTranslator': machine_translator, 'segments': segments, # We serialize the translation data using Django REST Framework. # This gives us a consistent representation with the APIs so we # can dynamically update translations in the view. 'initialStringTranslations': StringTranslationSerializer(string_translations, many=True, context={ 'translation_source': translation.source }).data, 'initialOverrides': SegmentOverrideSerializer(segment_overrides, many=True, context={ 'translation_source': translation.source }).data, }, cls=DjangoJSONEncoder) })
def is_shown(self, context): delete_permission = get_permission_name('delete', context['model']) return (context['view'] == 'edit' and context['request'].user.has_perm(delete_permission))
def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) items = self.model.objects.all() enable_locale_filter = getattr(settings, "WAGTAIL_I18N_ENABLED", False) and issubclass( self.model, TranslatableMixin) if enable_locale_filter: if "locale" in self.request.GET: try: locale = Locale.objects.get( language_code=self.request.GET["locale"]) except Locale.DoesNotExist: # Redirect to snippet without locale return redirect("wagtailsnippets:list", self.app_label, self.model_name) else: # Default to active locale (this will take into account the user's chosen admin language) locale = Locale.get_active() items = items.filter(locale=locale) else: locale = None # Preserve the snippet's model-level ordering if specified, but fall back on PK if not # (to ensure pagination is consistent) if not items.ordered: items = items.order_by("pk") # Search is_searchable = class_is_indexed(self.model) is_searching = False search_query = None if is_searchable and "q" in self.request.GET: search_form = SearchForm( self.request.GET, placeholder=_("Search %(snippet_type_name)s") % {"snippet_type_name": self.model._meta.verbose_name_plural}, ) if search_form.is_valid(): search_query = search_form.cleaned_data["q"] search_backend = get_search_backend() items = search_backend.search(search_query, items) is_searching = True else: search_form = SearchForm( placeholder=_("Search %(snippet_type_name)s") % {"snippet_type_name": self.model._meta.verbose_name_plural}) paginator = Paginator(items, per_page=20) paginated_items = paginator.get_page(self.request.GET.get("p")) context.update({ "model_opts": self.model._meta, "items": paginated_items, "can_add_snippet": self.request.user.has_perm(get_permission_name("add", self.model)), "can_delete_snippets": self.request.user.has_perm( get_permission_name("delete", self.model)), "is_searchable": is_searchable, "search_form": search_form, "is_searching": is_searching, "query_string": search_query, "locale": None, "translations": [], }) if enable_locale_filter: context.update({ "locale": locale, "translations": [{ "locale": locale, "url": reverse( "wagtailsnippets:list", args=[self.app_label, self.model_name], ) + "?locale=" + locale.language_code, } for locale in Locale.objects.all().exclude(id=locale.id)], }) return 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 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(): form.save() 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 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': for instance in instances: 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 check_perm(self, snippet): if getattr(self, "can_delete_items", None) is None: # since snippets permissions are not enforced per object, makes sense to just check once per model request self.can_delete_items = self.request.user.has_perm( get_permission_name("delete", self.model)) return self.can_delete_items