387 lines
13 KiB
Python
387 lines
13 KiB
Python
import os
|
|
from tempfile import SpooledTemporaryFile
|
|
|
|
from django.conf import settings
|
|
from django.core.exceptions import PermissionDenied
|
|
from django.http import FileResponse, HttpResponse, JsonResponse
|
|
from django.shortcuts import get_object_or_404
|
|
from django.urls import reverse
|
|
from django.urls.exceptions import NoReverseMatch
|
|
from django.utils.functional import cached_property
|
|
from django.utils.translation import gettext as _
|
|
from django.utils.translation import gettext_lazy, ngettext
|
|
from django.views import View
|
|
|
|
from wagtail.admin import messages
|
|
from wagtail.admin.auth import PermissionPolicyChecker
|
|
from wagtail.admin.filters import BaseMediaFilterSet
|
|
from wagtail.admin.utils import get_valid_next_url_from_request, set_query_params
|
|
from wagtail.admin.views import generic
|
|
from wagtail.images import get_image_model
|
|
from wagtail.images.exceptions import InvalidFilterSpecError
|
|
from wagtail.images.forms import URLGeneratorForm, get_image_form
|
|
from wagtail.images.models import Filter, SourceImageIOError
|
|
from wagtail.images.permissions import permission_policy
|
|
from wagtail.images.utils import generate_signature
|
|
from wagtail.models import Site
|
|
|
|
permission_checker = PermissionPolicyChecker(permission_policy)
|
|
|
|
Image = get_image_model()
|
|
|
|
USAGE_PAGE_SIZE = getattr(settings, "WAGTAILIMAGES_USAGE_PAGE_SIZE", 20)
|
|
|
|
|
|
class ImagesFilterSet(BaseMediaFilterSet):
|
|
permission_policy = permission_policy
|
|
|
|
class Meta:
|
|
model = Image
|
|
fields = []
|
|
|
|
|
|
class IndexView(generic.IndexView):
|
|
ORDERING_OPTIONS = {
|
|
"-created_at": gettext_lazy("Newest"),
|
|
"created_at": gettext_lazy("Oldest"),
|
|
"title": gettext_lazy("Title: (A -> Z)"),
|
|
"-title": gettext_lazy("Title: (Z -> A)"),
|
|
"file_size": gettext_lazy("File size: (low to high)"),
|
|
"-file_size": gettext_lazy("File size: (high to low)"),
|
|
}
|
|
default_ordering = "-created_at"
|
|
context_object_name = "images"
|
|
permission_policy = permission_policy
|
|
any_permission_required = ["add", "change", "delete"]
|
|
model = Image
|
|
filterset_class = ImagesFilterSet
|
|
show_other_searches = True
|
|
header_icon = "image"
|
|
page_title = gettext_lazy("Images")
|
|
add_item_label = gettext_lazy("Add an image")
|
|
index_url_name = "wagtailimages:index"
|
|
index_results_url_name = "wagtailimages:index_results"
|
|
add_url_name = "wagtailimages:add_multiple"
|
|
edit_url_name = "wagtailimages:edit"
|
|
template_name = "wagtailimages/images/index.html"
|
|
results_template_name = "wagtailimages/images/index_results.html"
|
|
columns = []
|
|
|
|
def get_paginate_by(self, queryset):
|
|
return getattr(settings, "WAGTAILIMAGES_INDEX_PAGE_SIZE", 30)
|
|
|
|
def get_valid_orderings(self):
|
|
return self.ORDERING_OPTIONS
|
|
|
|
def get_base_queryset(self):
|
|
# Get images (filtered by user permission)
|
|
images = (
|
|
permission_policy.instances_user_has_any_permission_for(
|
|
self.request.user, ["change", "delete"]
|
|
)
|
|
.select_related("collection")
|
|
.prefetch_renditions("max-165x165")
|
|
)
|
|
return images
|
|
|
|
@cached_property
|
|
def current_collection(self):
|
|
# Upon validation, the cleaned data is a Collection instance
|
|
return self.filters and self.filters.form.cleaned_data.get("collection_id")
|
|
|
|
def get_add_url(self):
|
|
# Pass the collection filter to prefill the add form's collection field
|
|
return set_query_params(
|
|
super().get_add_url(),
|
|
{"collection_id": self.current_collection and self.current_collection.pk},
|
|
)
|
|
|
|
def get_filterset_kwargs(self):
|
|
kwargs = super().get_filterset_kwargs()
|
|
kwargs["is_searching"] = self.is_searching
|
|
return kwargs
|
|
|
|
def get_next_url(self):
|
|
next_url = self.index_url
|
|
request_query_string = self.request.META.get("QUERY_STRING")
|
|
if request_query_string:
|
|
next_url += "?" + request_query_string
|
|
return next_url
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
|
|
context.update(
|
|
{
|
|
"next": self.get_next_url(),
|
|
"current_collection": self.current_collection,
|
|
"current_ordering": self.ordering,
|
|
"ORDERING_OPTIONS": self.ORDERING_OPTIONS,
|
|
}
|
|
)
|
|
|
|
return context
|
|
|
|
|
|
class EditView(generic.EditView):
|
|
permission_policy = permission_policy
|
|
pk_url_kwarg = "image_id"
|
|
error_message = gettext_lazy("The image could not be saved due to errors.")
|
|
template_name = "wagtailimages/images/edit.html"
|
|
index_url_name = "wagtailimages:index"
|
|
edit_url_name = "wagtailimages:edit"
|
|
delete_url_name = "wagtailimages:delete"
|
|
url_generator_url_name = "wagtailimages:url_generator"
|
|
header_icon = "image"
|
|
context_object_name = "image"
|
|
|
|
@cached_property
|
|
def model(self):
|
|
return get_image_model()
|
|
|
|
def get_form_class(self):
|
|
return get_image_form(self.model)
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super().get_form_kwargs()
|
|
kwargs["user"] = self.request.user
|
|
return kwargs
|
|
|
|
def get_object(self, queryset=None):
|
|
obj = super().get_object(queryset)
|
|
if not permission_policy.user_has_permission_for_instance(
|
|
self.request.user, "change", obj
|
|
):
|
|
raise PermissionDenied
|
|
return obj
|
|
|
|
def get_success_message(self):
|
|
return _("Image '%(image_title)s' updated.") % {
|
|
"image_title": self.object.title
|
|
}
|
|
|
|
@cached_property
|
|
def next_url(self):
|
|
return get_valid_next_url_from_request(self.request)
|
|
|
|
def get_success_url(self):
|
|
return self.next_url or super().get_success_url()
|
|
|
|
def render_to_response(self, context, **response_kwargs):
|
|
if self.object.is_stored_locally():
|
|
# Give error if image file doesn't exist
|
|
if not os.path.isfile(self.object.file.path):
|
|
messages.error(
|
|
self.request,
|
|
_(
|
|
"The source image file could not be found. Please change the source or delete the image."
|
|
)
|
|
% {"image_title": self.object.title},
|
|
buttons=[messages.button(self.get_delete_url(), _("Delete"))],
|
|
)
|
|
|
|
return super().render_to_response(context, **response_kwargs)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context["next"] = self.next_url
|
|
context["usage_count_val"] = self.object.get_usage().count()
|
|
|
|
try:
|
|
context["filesize"] = self.object.get_file_size()
|
|
except SourceImageIOError:
|
|
context["filesize"] = None
|
|
|
|
try:
|
|
reverse("wagtailimages_serve", args=("foo", "1", "bar"))
|
|
context["url_generator_url"] = reverse(
|
|
self.url_generator_url_name, args=(self.object.id,)
|
|
)
|
|
except NoReverseMatch:
|
|
context["url_generator_url"] = None
|
|
|
|
return context
|
|
|
|
|
|
class URLGeneratorView(generic.InspectView):
|
|
any_permission_required = ["change"]
|
|
model = get_image_model()
|
|
pk_url_kwarg = "image_id"
|
|
header_icon = "image"
|
|
page_title = gettext_lazy("Generate URL")
|
|
template_name = "wagtailimages/images/url_generator.html"
|
|
index_url_name = "wagtailimages:index"
|
|
edit_url_name = "wagtailimages:edit"
|
|
|
|
def get_page_subtitle(self):
|
|
return self.object.title
|
|
|
|
def get_fields(self):
|
|
return []
|
|
|
|
def get(self, request, image_id, *args, **kwargs):
|
|
self.object = get_object_or_404(self.model, id=image_id)
|
|
|
|
if not permission_policy.user_has_permission_for_instance(
|
|
request.user, "change", self.object
|
|
):
|
|
raise PermissionDenied
|
|
|
|
self.form = URLGeneratorForm(
|
|
initial={
|
|
"filter_method": "original",
|
|
"width": self.object.width,
|
|
"height": self.object.height,
|
|
}
|
|
)
|
|
|
|
return self.render_to_response(self.get_context_data())
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context["form"] = self.form
|
|
return context
|
|
|
|
|
|
class GenerateURLView(View):
|
|
def get(self, request, image_id, filter_spec):
|
|
# Get the image
|
|
Image = get_image_model()
|
|
try:
|
|
image = Image.objects.get(id=image_id)
|
|
except Image.DoesNotExist:
|
|
return JsonResponse({"error": "Cannot find image."}, status=404)
|
|
|
|
# Check if this user has edit permission on this image
|
|
if not permission_policy.user_has_permission_for_instance(
|
|
request.user, "change", image
|
|
):
|
|
return JsonResponse(
|
|
{
|
|
"error": "You do not have permission to generate a URL for this image."
|
|
},
|
|
status=403,
|
|
)
|
|
|
|
# Parse the filter spec to make sure it's valid
|
|
try:
|
|
Filter(spec=filter_spec).operations
|
|
except InvalidFilterSpecError:
|
|
return JsonResponse({"error": "Invalid filter spec."}, status=400)
|
|
|
|
# Generate url
|
|
signature = generate_signature(image_id, filter_spec)
|
|
url = reverse("wagtailimages_serve", args=(signature, image_id, filter_spec))
|
|
|
|
# Get site root url
|
|
try:
|
|
site_root_url = Site.objects.get(is_default_site=True).root_url
|
|
except Site.DoesNotExist:
|
|
site_root_url = Site.objects.first().root_url
|
|
|
|
# Generate preview url
|
|
preview_url = reverse("wagtailimages:preview", args=(image_id, filter_spec))
|
|
|
|
return JsonResponse(
|
|
{"url": site_root_url + url, "preview_url": preview_url}, status=200
|
|
)
|
|
|
|
|
|
def preview(request, image_id, filter_spec):
|
|
image = get_object_or_404(get_image_model(), id=image_id)
|
|
|
|
try:
|
|
# Temporary image needs to be an instance that Willow can run optimizers on
|
|
temp_image = SpooledTemporaryFile(max_size=settings.FILE_UPLOAD_MAX_MEMORY_SIZE)
|
|
image = Filter(spec=filter_spec).run(image, temp_image)
|
|
temp_image.seek(0)
|
|
response = FileResponse(temp_image)
|
|
response["Content-Type"] = "image/" + image.format_name
|
|
return response
|
|
except InvalidFilterSpecError:
|
|
return HttpResponse(
|
|
"Invalid filter spec: " + filter_spec, content_type="text/plain", status=400
|
|
)
|
|
|
|
|
|
class DeleteView(generic.DeleteView):
|
|
model = get_image_model()
|
|
pk_url_kwarg = "image_id"
|
|
permission_policy = permission_policy
|
|
permission_required = "delete"
|
|
header_icon = "image"
|
|
template_name = "wagtailimages/images/confirm_delete.html"
|
|
usage_url_name = "wagtailimages:image_usage"
|
|
delete_url_name = "wagtailimages:delete"
|
|
index_url_name = "wagtailimages:index"
|
|
page_title = gettext_lazy("Delete image")
|
|
|
|
def user_has_permission(self, permission):
|
|
return self.permission_policy.user_has_permission_for_instance(
|
|
self.request.user, permission, self.object
|
|
)
|
|
|
|
@property
|
|
def confirmation_message(self):
|
|
# This message will only appear in the singular, but we specify a plural
|
|
# so it can share the translation string with confirm_bulk_delete.html
|
|
return ngettext(
|
|
"Are you sure you want to delete this image?",
|
|
"Are you sure you want to delete these images?",
|
|
1,
|
|
)
|
|
|
|
def get_success_message(self):
|
|
return _("Image '%(image_title)s' deleted.") % {
|
|
"image_title": self.object.title
|
|
}
|
|
|
|
|
|
class CreateView(generic.CreateView):
|
|
permission_policy = permission_policy
|
|
index_url_name = "wagtailimages:index"
|
|
add_url_name = "wagtailimages:add"
|
|
edit_url_name = "wagtailimages:edit"
|
|
error_message = gettext_lazy("The image could not be created due to errors.")
|
|
template_name = "wagtailimages/images/add.html"
|
|
header_icon = "image"
|
|
|
|
@cached_property
|
|
def model(self):
|
|
return get_image_model()
|
|
|
|
def get_form_class(self):
|
|
return get_image_form(self.model)
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super().get_form_kwargs()
|
|
kwargs["user"] = self.request.user
|
|
return kwargs
|
|
|
|
def get_initial_form_instance(self):
|
|
return self.model(uploaded_by_user=self.request.user)
|
|
|
|
def get_success_message(self, instance):
|
|
return _("Image '%(image_title)s' added.") % {"image_title": instance.title}
|
|
|
|
|
|
class UsageView(generic.UsageView):
|
|
model = get_image_model()
|
|
paginate_by = USAGE_PAGE_SIZE
|
|
pk_url_kwarg = "image_id"
|
|
permission_policy = permission_policy
|
|
permission_required = "change"
|
|
header_icon = "image"
|
|
index_url_name = "wagtailimages:index"
|
|
edit_url_name = "wagtailimages:edit"
|
|
|
|
def get_base_object_queryset(self):
|
|
return super().get_base_object_queryset().select_related("uploaded_by_user")
|
|
|
|
def user_has_permission(self, permission):
|
|
return self.permission_policy.user_has_permission_for_instance(
|
|
self.request.user, permission, self.object
|
|
)
|
|
|
|
def get_page_subtitle(self):
|
|
return self.object.title
|