Initial commit
This commit is contained in:
0
env/lib/python3.10/site-packages/wagtail/images/views/__init__.py
vendored
Normal file
0
env/lib/python3.10/site-packages/wagtail/images/views/__init__.py
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/images/views/__pycache__/__init__.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/images/views/__pycache__/__init__.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/images/views/__pycache__/chooser.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/images/views/__pycache__/chooser.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/images/views/__pycache__/images.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/images/views/__pycache__/images.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/images/views/__pycache__/multiple.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/images/views/__pycache__/multiple.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/images/views/__pycache__/serve.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/images/views/__pycache__/serve.cpython-310.pyc
vendored
Normal file
Binary file not shown.
5
env/lib/python3.10/site-packages/wagtail/images/views/bulk_actions/__init__.py
vendored
Normal file
5
env/lib/python3.10/site-packages/wagtail/images/views/bulk_actions/__init__.py
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
from .add_tags import AddTagsBulkAction
|
||||
from .add_to_collection import AddToCollectionBulkAction
|
||||
from .delete import DeleteBulkAction
|
||||
|
||||
__all__ = ["AddToCollectionBulkAction", "AddTagsBulkAction", "DeleteBulkAction"]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
44
env/lib/python3.10/site-packages/wagtail/images/views/bulk_actions/add_tags.py
vendored
Normal file
44
env/lib/python3.10/site-packages/wagtail/images/views/bulk_actions/add_tags.py
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import ngettext
|
||||
|
||||
from wagtail.admin import widgets
|
||||
from wagtail.images.views.bulk_actions.image_bulk_action import ImageBulkAction
|
||||
|
||||
|
||||
class TagForm(forms.Form):
|
||||
tags = forms.Field(label=_("Tags"), widget=widgets.AdminTagWidget)
|
||||
|
||||
|
||||
class AddTagsBulkAction(ImageBulkAction):
|
||||
display_name = _("Tag")
|
||||
action_type = "add_tags"
|
||||
aria_label = _("Add tags to the selected images")
|
||||
template_name = "wagtailimages/bulk_actions/confirm_bulk_add_tags.html"
|
||||
action_priority = 20
|
||||
form_class = TagForm
|
||||
|
||||
def check_perm(self, image):
|
||||
return self.permission_policy.user_has_permission_for_instance(
|
||||
self.request.user, "change", image
|
||||
)
|
||||
|
||||
def get_execution_context(self):
|
||||
return {"tags": self.cleaned_form.cleaned_data["tags"].split(",")}
|
||||
|
||||
@classmethod
|
||||
def execute_action(cls, images, tags=[], **kwargs):
|
||||
num_parent_objects = 0
|
||||
if not tags:
|
||||
return
|
||||
for image in images:
|
||||
num_parent_objects += 1
|
||||
image.tags.add(*tags)
|
||||
return num_parent_objects, 0
|
||||
|
||||
def get_success_message(self, num_parent_objects, num_child_objects):
|
||||
return ngettext(
|
||||
"New tags have been added to %(num_parent_objects)d image",
|
||||
"New tags have been added to %(num_parent_objects)d images",
|
||||
num_parent_objects,
|
||||
) % {"num_parent_objects": num_parent_objects}
|
||||
57
env/lib/python3.10/site-packages/wagtail/images/views/bulk_actions/add_to_collection.py
vendored
Normal file
57
env/lib/python3.10/site-packages/wagtail/images/views/bulk_actions/add_to_collection.py
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import ngettext
|
||||
|
||||
from wagtail.images.views.bulk_actions.image_bulk_action import ImageBulkAction
|
||||
|
||||
|
||||
class CollectionForm(forms.Form):
|
||||
def __init__(self, *args, **kwargs):
|
||||
user = kwargs.pop("user", None)
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["collection"] = forms.ModelChoiceField(
|
||||
label=_("Collection"),
|
||||
queryset=ImageBulkAction.permission_policy.collections_user_has_permission_for(
|
||||
user, "add"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class AddToCollectionBulkAction(ImageBulkAction):
|
||||
display_name = _("Add to collection")
|
||||
action_type = "add_to_collection"
|
||||
aria_label = _("Add selected images to collection")
|
||||
template_name = "wagtailimages/bulk_actions/confirm_bulk_add_to_collection.html"
|
||||
action_priority = 30
|
||||
form_class = CollectionForm
|
||||
collection = None
|
||||
|
||||
def check_perm(self, image):
|
||||
return self.permission_policy.user_has_permission_for_instance(
|
||||
self.request.user, "change", image
|
||||
)
|
||||
|
||||
def get_form_kwargs(self):
|
||||
return {**super().get_form_kwargs(), "user": self.request.user}
|
||||
|
||||
def get_execution_context(self):
|
||||
return {"collection": self.cleaned_form.cleaned_data["collection"]}
|
||||
|
||||
@classmethod
|
||||
def execute_action(cls, images, collection=None, **kwargs):
|
||||
if collection is None:
|
||||
return
|
||||
num_parent_objects = (
|
||||
cls.get_default_model()
|
||||
.objects.filter(pk__in=[obj.pk for obj in images])
|
||||
.update(collection=collection)
|
||||
)
|
||||
return num_parent_objects, 0
|
||||
|
||||
def get_success_message(self, num_parent_objects, num_child_objects):
|
||||
collection = self.cleaned_form.cleaned_data["collection"]
|
||||
return ngettext(
|
||||
"%(num_parent_objects)d image has been added to %(collection)s",
|
||||
"%(num_parent_objects)d images have been added to %(collection)s",
|
||||
num_parent_objects,
|
||||
) % {"num_parent_objects": num_parent_objects, "collection": collection.name}
|
||||
33
env/lib/python3.10/site-packages/wagtail/images/views/bulk_actions/delete.py
vendored
Normal file
33
env/lib/python3.10/site-packages/wagtail/images/views/bulk_actions/delete.py
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import ngettext
|
||||
|
||||
from wagtail.images.views.bulk_actions.image_bulk_action import ImageBulkAction
|
||||
|
||||
|
||||
class DeleteBulkAction(ImageBulkAction):
|
||||
display_name = _("Delete")
|
||||
action_type = "delete"
|
||||
aria_label = _("Delete selected images")
|
||||
template_name = "wagtailimages/bulk_actions/confirm_bulk_delete.html"
|
||||
action_priority = 100
|
||||
classes = {"serious"}
|
||||
|
||||
def check_perm(self, document):
|
||||
return self.permission_policy.user_has_permission_for_instance(
|
||||
self.request.user, "delete", document
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def execute_action(cls, objects, **kwargs):
|
||||
num_parent_objects = len(objects)
|
||||
cls.get_default_model().objects.filter(
|
||||
pk__in=[obj.pk for obj in objects]
|
||||
).delete()
|
||||
return num_parent_objects, 0
|
||||
|
||||
def get_success_message(self, num_parent_objects, num_child_objects):
|
||||
return ngettext(
|
||||
"%(num_parent_objects)d image has been deleted",
|
||||
"%(num_parent_objects)d images have been deleted",
|
||||
num_parent_objects,
|
||||
) % {"num_parent_objects": num_parent_objects}
|
||||
34
env/lib/python3.10/site-packages/wagtail/images/views/bulk_actions/image_bulk_action.py
vendored
Normal file
34
env/lib/python3.10/site-packages/wagtail/images/views/bulk_actions/image_bulk_action.py
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
from wagtail.admin.views.bulk_action import BulkAction
|
||||
from wagtail.images import get_image_model
|
||||
from wagtail.images.permissions import permission_policy as images_permission_policy
|
||||
|
||||
|
||||
class ImageBulkAction(BulkAction):
|
||||
permission_policy = images_permission_policy
|
||||
models = [get_image_model()]
|
||||
|
||||
def get_all_objects_in_listing_query(self, parent_id):
|
||||
listing_objects = self.model.objects.all()
|
||||
if parent_id is not None:
|
||||
listing_objects = listing_objects.filter(collection_id=parent_id)
|
||||
|
||||
listing_objects = listing_objects.values_list("pk", flat=True)
|
||||
|
||||
if "q" in self.request.GET:
|
||||
query_string = self.request.GET.get("q", "")
|
||||
listing_objects = listing_objects.search(query_string).results()
|
||||
|
||||
return listing_objects
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["items_with_no_access"] = [
|
||||
{
|
||||
"item": image,
|
||||
"can_edit": self.permission_policy.user_has_permission_for_instance(
|
||||
self.request.user, "change", image
|
||||
),
|
||||
}
|
||||
for image in context["items_with_no_access"]
|
||||
]
|
||||
return context
|
||||
344
env/lib/python3.10/site-packages/wagtail/images/views/chooser.py
vendored
Normal file
344
env/lib/python3.10/site-packages/wagtail/images/views/chooser.py
vendored
Normal file
@@ -0,0 +1,344 @@
|
||||
from django.conf import settings
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.template.loader import render_to_string
|
||||
from django.urls import path, reverse
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.http import urlencode
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic.base import View
|
||||
|
||||
from wagtail.admin.auth import PermissionPolicyChecker
|
||||
from wagtail.admin.modal_workflow import render_modal_workflow
|
||||
from wagtail.admin.models import popular_tags_for_model
|
||||
from wagtail.admin.views.generic.chooser import (
|
||||
BaseChooseView,
|
||||
ChooseResultsViewMixin,
|
||||
ChooseViewMixin,
|
||||
ChosenMultipleViewMixin,
|
||||
ChosenResponseMixin,
|
||||
ChosenViewMixin,
|
||||
CreateViewMixin,
|
||||
CreationFormMixin,
|
||||
PreserveURLParametersMixin,
|
||||
)
|
||||
from wagtail.admin.viewsets.chooser import ChooserViewSet
|
||||
from wagtail.images import get_image_model
|
||||
from wagtail.images.formats import get_image_format
|
||||
from wagtail.images.forms import ImageInsertionForm, get_image_form
|
||||
from wagtail.images.permissions import permission_policy
|
||||
from wagtail.images.utils import find_image_duplicates
|
||||
|
||||
permission_checker = PermissionPolicyChecker(permission_policy)
|
||||
|
||||
|
||||
class ImageChosenResponseMixin(ChosenResponseMixin):
|
||||
def get_chosen_response_data(self, image, preview_image_filter="max-165x165"):
|
||||
"""
|
||||
Given an image, return the json data to pass back to the image chooser panel
|
||||
"""
|
||||
response_data = super().get_chosen_response_data(image)
|
||||
preview_image = image.get_rendition(preview_image_filter)
|
||||
response_data["preview"] = {
|
||||
"url": preview_image.url,
|
||||
"width": preview_image.width,
|
||||
"height": preview_image.height,
|
||||
}
|
||||
return response_data
|
||||
|
||||
|
||||
class ImageCreationFormMixin(CreationFormMixin):
|
||||
creation_tab_id = "upload"
|
||||
create_action_label = _("Upload")
|
||||
create_action_clicked_label = _("Uploading…")
|
||||
permission_policy = permission_policy
|
||||
|
||||
def get_creation_form_class(self):
|
||||
return get_image_form(self.model)
|
||||
|
||||
def get_creation_form_kwargs(self):
|
||||
kwargs = super().get_creation_form_kwargs()
|
||||
kwargs.update(
|
||||
{
|
||||
"user": self.request.user,
|
||||
"prefix": "image-chooser-upload",
|
||||
}
|
||||
)
|
||||
if self.request.method in ("POST", "PUT"):
|
||||
kwargs["instance"] = self.model(uploaded_by_user=self.request.user)
|
||||
|
||||
return kwargs
|
||||
|
||||
|
||||
class BaseImageChooseView(BaseChooseView):
|
||||
template_name = "wagtailimages/chooser/chooser.html"
|
||||
results_template_name = "wagtailimages/chooser/results.html"
|
||||
ordering = "-created_at"
|
||||
construct_queryset_hook_name = "construct_image_chooser_queryset"
|
||||
|
||||
@property
|
||||
def per_page(self):
|
||||
# Make per_page into a property so that we can read back WAGTAILIMAGES_CHOOSER_PAGE_SIZE
|
||||
# at runtime.
|
||||
return getattr(settings, "WAGTAILIMAGES_CHOOSER_PAGE_SIZE", 20)
|
||||
|
||||
def get_object_list(self):
|
||||
return (
|
||||
permission_policy.instances_user_has_any_permission_for(
|
||||
self.request.user, ["choose"]
|
||||
)
|
||||
.select_related("collection")
|
||||
.prefetch_renditions("max-165x165")
|
||||
)
|
||||
|
||||
def filter_object_list(self, objects):
|
||||
tag_name = self.request.GET.get("tag")
|
||||
if tag_name:
|
||||
objects = objects.filter(tags__name=tag_name)
|
||||
|
||||
return super().filter_object_list(objects)
|
||||
|
||||
def get_filter_form(self):
|
||||
FilterForm = self.get_filter_form_class()
|
||||
return FilterForm(self.request.GET, collections=self.collections)
|
||||
|
||||
@cached_property
|
||||
def collections(self):
|
||||
collections = self.permission_policy.collections_user_has_permission_for(
|
||||
self.request.user, "choose"
|
||||
)
|
||||
if len(collections) < 2:
|
||||
return None
|
||||
|
||||
return collections
|
||||
|
||||
def get(self, request):
|
||||
self.model = get_image_model()
|
||||
return super().get(request)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
chosen_url_name = (
|
||||
"wagtailimages_chooser:select_format"
|
||||
if self.request.GET.get("select_format")
|
||||
else "wagtailimages_chooser:chosen"
|
||||
)
|
||||
|
||||
for image in context["results"]:
|
||||
image.chosen_url = self.append_preserved_url_parameters(
|
||||
reverse(chosen_url_name, args=(image.id,))
|
||||
)
|
||||
|
||||
context["collections"] = self.collections
|
||||
return context
|
||||
|
||||
|
||||
class ImageChooseViewMixin(ChooseViewMixin):
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["popular_tags"] = popular_tags_for_model(self.model)
|
||||
return context
|
||||
|
||||
|
||||
class ImageChooseView(
|
||||
ImageChooseViewMixin, ImageCreationFormMixin, BaseImageChooseView
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class ImageChooseResultsView(
|
||||
ChooseResultsViewMixin, ImageCreationFormMixin, BaseImageChooseView
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class ImageChosenView(ChosenViewMixin, ImageChosenResponseMixin, View):
|
||||
def get(self, request, *args, pk, **kwargs):
|
||||
self.model = get_image_model()
|
||||
return super().get(request, *args, pk, **kwargs)
|
||||
|
||||
|
||||
class ImageChosenMultipleView(ChosenMultipleViewMixin, ImageChosenResponseMixin, View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.model = get_image_model()
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
|
||||
class SelectFormatResponseMixin(PreserveURLParametersMixin):
|
||||
def render_select_format_response(self, image, form):
|
||||
action_url = self.append_preserved_url_parameters(
|
||||
reverse("wagtailimages_chooser:select_format", args=(image.id,))
|
||||
)
|
||||
return render_modal_workflow(
|
||||
self.request,
|
||||
"wagtailimages/chooser/select_format.html",
|
||||
None,
|
||||
{"image": image, "form": form, "select_format_action_url": action_url},
|
||||
json_data={"step": "select_format"},
|
||||
)
|
||||
|
||||
|
||||
class ImageUploadViewMixin(SelectFormatResponseMixin, CreateViewMixin):
|
||||
def get(self, request):
|
||||
self.model = get_image_model()
|
||||
return super().get(request)
|
||||
|
||||
def post(self, request):
|
||||
self.model = get_image_model()
|
||||
self.form = self.get_creation_form()
|
||||
|
||||
if self.form.is_valid():
|
||||
image = self.save_form(self.form)
|
||||
|
||||
duplicates = find_image_duplicates(
|
||||
image=image,
|
||||
user=request.user,
|
||||
permission_policy=permission_policy,
|
||||
)
|
||||
existing_image = duplicates.first()
|
||||
if existing_image:
|
||||
return self.render_duplicate_found_response(
|
||||
request, image, existing_image
|
||||
)
|
||||
|
||||
if request.GET.get("select_format"):
|
||||
insertion_form = ImageInsertionForm(
|
||||
initial={"alt_text": image.default_alt_text},
|
||||
prefix="image-chooser-insertion",
|
||||
)
|
||||
return self.render_select_format_response(image, insertion_form)
|
||||
else:
|
||||
# not specifying a format; return the image details now
|
||||
return self.get_chosen_response(image)
|
||||
|
||||
else: # form is invalid
|
||||
return self.get_reshow_creation_form_response()
|
||||
|
||||
def render_duplicate_found_response(self, request, new_image, existing_image):
|
||||
next_step_url = (
|
||||
"wagtailimages_chooser:select_format"
|
||||
if request.GET.get("select_format")
|
||||
else "wagtailimages_chooser:chosen"
|
||||
)
|
||||
choose_new_image_url = self.append_preserved_url_parameters(
|
||||
reverse(next_step_url, args=(new_image.id,))
|
||||
)
|
||||
choose_existing_image_url = self.append_preserved_url_parameters(
|
||||
reverse(next_step_url, args=(existing_image.id,))
|
||||
)
|
||||
|
||||
cancel_duplicate_upload_action = (
|
||||
f"{reverse('wagtailimages:delete', args=(new_image.id,))}?"
|
||||
f"{urlencode({'next': choose_existing_image_url})}"
|
||||
)
|
||||
|
||||
duplicate_upload_html = render_to_string(
|
||||
"wagtailimages/chooser/confirm_duplicate_upload.html",
|
||||
{
|
||||
"new_image": new_image,
|
||||
"existing_image": existing_image,
|
||||
"confirm_duplicate_upload_action": choose_new_image_url,
|
||||
"cancel_duplicate_upload_action": cancel_duplicate_upload_action,
|
||||
},
|
||||
request,
|
||||
)
|
||||
return render_modal_workflow(
|
||||
request,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
json_data={
|
||||
"step": "duplicate_found",
|
||||
"htmlFragment": duplicate_upload_html,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class ImageUploadView(
|
||||
ImageUploadViewMixin, ImageCreationFormMixin, ImageChosenResponseMixin, View
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class ImageSelectFormatView(SelectFormatResponseMixin, ImageChosenResponseMixin, View):
|
||||
model = None
|
||||
|
||||
def get(self, request, image_id):
|
||||
image = get_object_or_404(self.model, id=image_id)
|
||||
initial = {"alt_text": image.default_alt_text}
|
||||
initial.update(request.GET.dict())
|
||||
# If you edit an existing image, and there is no alt text, ensure that
|
||||
# "image is decorative" is ticked when you open the form
|
||||
initial["image_is_decorative"] = initial["alt_text"] == ""
|
||||
form = ImageInsertionForm(initial=initial, prefix="image-chooser-insertion")
|
||||
return self.render_select_format_response(image, form)
|
||||
|
||||
def get_chosen_response_data(self, image):
|
||||
format = get_image_format(self.form.cleaned_data["format"])
|
||||
alt_text = self.form.cleaned_data["alt_text"]
|
||||
response_data = super().get_chosen_response_data(
|
||||
image, preview_image_filter=format.filter_spec
|
||||
)
|
||||
response_data.update(
|
||||
{
|
||||
"format": format.name,
|
||||
"alt": alt_text,
|
||||
"class": format.classname,
|
||||
"html": format.image_to_editor_html(image, alt_text),
|
||||
}
|
||||
)
|
||||
return response_data
|
||||
|
||||
def post(self, request, image_id):
|
||||
image = get_object_or_404(get_image_model(), id=image_id)
|
||||
|
||||
self.form = ImageInsertionForm(
|
||||
request.POST,
|
||||
initial={"alt_text": image.default_alt_text},
|
||||
prefix="image-chooser-insertion",
|
||||
)
|
||||
if self.form.is_valid():
|
||||
return self.get_chosen_response(image)
|
||||
else:
|
||||
return self.render_select_format_response(image, self.form)
|
||||
|
||||
|
||||
class ImageChooserViewSet(ChooserViewSet):
|
||||
choose_view_class = ImageChooseView
|
||||
choose_results_view_class = ImageChooseResultsView
|
||||
chosen_view_class = ImageChosenView
|
||||
chosen_multiple_view_class = ImageChosenMultipleView
|
||||
create_view_class = ImageUploadView
|
||||
select_format_view_class = ImageSelectFormatView
|
||||
permission_policy = permission_policy
|
||||
register_widget = False
|
||||
preserve_url_parameters = ChooserViewSet.preserve_url_parameters + ["select_format"]
|
||||
|
||||
icon = "image"
|
||||
choose_one_text = _("Choose an image")
|
||||
create_action_label = _("Upload")
|
||||
create_action_clicked_label = _("Uploading…")
|
||||
choose_another_text = _("Choose another image")
|
||||
edit_item_text = _("Edit this image")
|
||||
|
||||
@property
|
||||
def select_format_view(self):
|
||||
return self.select_format_view_class.as_view(
|
||||
model=self.model,
|
||||
preserve_url_parameters=self.preserve_url_parameters,
|
||||
)
|
||||
|
||||
def get_urlpatterns(self):
|
||||
return super().get_urlpatterns() + [
|
||||
path(
|
||||
"<int:image_id>/select_format/",
|
||||
self.select_format_view,
|
||||
name="select_format",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
viewset = ImageChooserViewSet(
|
||||
"wagtailimages_chooser",
|
||||
model=get_image_model(),
|
||||
url_prefix="images/chooser",
|
||||
)
|
||||
390
env/lib/python3.10/site-packages/wagtail/images/views/images.py
vendored
Normal file
390
env/lib/python3.10/site-packages/wagtail/images/views/images.py
vendored
Normal file
@@ -0,0 +1,390 @@
|
||||
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, redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.urls import reverse
|
||||
from django.urls.exceptions import NoReverseMatch
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.http import urlencode
|
||||
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"
|
||||
_show_breadcrumbs = True
|
||||
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
|
||||
|
||||
|
||||
@permission_checker.require("change")
|
||||
def edit(request, image_id):
|
||||
Image = get_image_model()
|
||||
ImageForm = get_image_form(Image)
|
||||
|
||||
image = get_object_or_404(Image, id=image_id)
|
||||
|
||||
if not permission_policy.user_has_permission_for_instance(
|
||||
request.user, "change", image
|
||||
):
|
||||
raise PermissionDenied
|
||||
|
||||
next_url = get_valid_next_url_from_request(request)
|
||||
|
||||
if request.method == "POST":
|
||||
form = ImageForm(request.POST, request.FILES, instance=image, user=request.user)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
|
||||
edit_url = reverse("wagtailimages:edit", args=(image.id,))
|
||||
redirect_url = "wagtailimages:index"
|
||||
if next_url:
|
||||
edit_url = f"{edit_url}?{urlencode({'next': next_url})}"
|
||||
redirect_url = next_url
|
||||
|
||||
messages.success(
|
||||
request,
|
||||
_("Image '%(image_title)s' updated.") % {"image_title": image.title},
|
||||
buttons=[messages.button(edit_url, _("Edit again"))],
|
||||
)
|
||||
return redirect(redirect_url)
|
||||
else:
|
||||
messages.error(request, _("The image could not be saved due to errors."))
|
||||
else:
|
||||
form = ImageForm(instance=image, user=request.user)
|
||||
|
||||
# Check if we should enable the frontend url generator
|
||||
try:
|
||||
reverse("wagtailimages_serve", args=("foo", "1", "bar"))
|
||||
url_generator_enabled = True
|
||||
except NoReverseMatch:
|
||||
url_generator_enabled = False
|
||||
|
||||
if image.is_stored_locally():
|
||||
# Give error if image file doesn't exist
|
||||
if not os.path.isfile(image.file.path):
|
||||
messages.error(
|
||||
request,
|
||||
_(
|
||||
"The source image file could not be found. Please change the source or delete the image."
|
||||
)
|
||||
% {"image_title": image.title},
|
||||
buttons=[
|
||||
messages.button(
|
||||
reverse("wagtailimages:delete", args=(image.id,)), _("Delete")
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
try:
|
||||
filesize = image.get_file_size()
|
||||
except SourceImageIOError:
|
||||
filesize = None
|
||||
|
||||
return TemplateResponse(
|
||||
request,
|
||||
"wagtailimages/images/edit.html",
|
||||
{
|
||||
"image": image,
|
||||
"form": form,
|
||||
"url_generator_enabled": url_generator_enabled,
|
||||
"filesize": filesize,
|
||||
"user_can_delete": permission_policy.user_has_permission_for_instance(
|
||||
request.user, "delete", image
|
||||
),
|
||||
"next": next_url,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class URLGeneratorView(generic.InspectView):
|
||||
any_permission_required = ["change"]
|
||||
model = get_image_model()
|
||||
pk_url_kwarg = "image_id"
|
||||
header_icon = "image"
|
||||
page_title = "Generating URL"
|
||||
template_name = "wagtailimages/images/url_generator.html"
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@permission_checker.require("add")
|
||||
def add(request):
|
||||
ImageModel = get_image_model()
|
||||
ImageForm = get_image_form(ImageModel)
|
||||
|
||||
if request.method == "POST":
|
||||
image = ImageModel(uploaded_by_user=request.user)
|
||||
form = ImageForm(request.POST, request.FILES, instance=image, user=request.user)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
|
||||
messages.success(
|
||||
request,
|
||||
_("Image '%(image_title)s' added.") % {"image_title": image.title},
|
||||
buttons=[
|
||||
messages.button(
|
||||
reverse("wagtailimages:edit", args=(image.id,)), _("Edit")
|
||||
)
|
||||
],
|
||||
)
|
||||
return redirect("wagtailimages:index")
|
||||
else:
|
||||
messages.error(request, _("The image could not be created due to errors."))
|
||||
else:
|
||||
form = ImageForm(user=request.user)
|
||||
|
||||
return TemplateResponse(
|
||||
request,
|
||||
"wagtailimages/images/add.html",
|
||||
{
|
||||
"form": form,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
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"
|
||||
|
||||
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
|
||||
163
env/lib/python3.10/site-packages/wagtail/images/views/multiple.py
vendored
Normal file
163
env/lib/python3.10/site-packages/wagtail/images/views/multiple.py
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
import os.path
|
||||
|
||||
from django.template.loader import render_to_string
|
||||
from django.urls import reverse
|
||||
|
||||
from wagtail.admin.views.generic.multiple_upload import AddView as BaseAddView
|
||||
from wagtail.admin.views.generic.multiple_upload import (
|
||||
CreateFromUploadView as BaseCreateFromUploadView,
|
||||
)
|
||||
from wagtail.admin.views.generic.multiple_upload import (
|
||||
DeleteUploadView as BaseDeleteUploadView,
|
||||
)
|
||||
from wagtail.admin.views.generic.multiple_upload import DeleteView as BaseDeleteView
|
||||
from wagtail.admin.views.generic.multiple_upload import EditView as BaseEditView
|
||||
from wagtail.images import get_image_model
|
||||
from wagtail.images.fields import get_allowed_image_extensions
|
||||
from wagtail.images.forms import get_image_form, get_image_multi_form
|
||||
from wagtail.images.permissions import ImagesPermissionPolicyGetter, permission_policy
|
||||
from wagtail.images.utils import find_image_duplicates
|
||||
|
||||
|
||||
class AddView(BaseAddView):
|
||||
permission_policy = ImagesPermissionPolicyGetter()
|
||||
template_name = "wagtailimages/multiple/add.html"
|
||||
|
||||
edit_object_url_name = "wagtailimages:edit_multiple"
|
||||
delete_object_url_name = "wagtailimages:delete_multiple"
|
||||
edit_object_form_prefix = "image"
|
||||
context_object_name = "image"
|
||||
context_object_id_name = "image_id"
|
||||
|
||||
edit_upload_url_name = "wagtailimages:create_multiple_from_uploaded_image"
|
||||
delete_upload_url_name = "wagtailimages:delete_upload_multiple"
|
||||
edit_upload_form_prefix = "uploaded-image"
|
||||
context_upload_name = "uploaded_image"
|
||||
context_upload_id_name = "uploaded_file_id"
|
||||
|
||||
def get_model(self):
|
||||
return get_image_model()
|
||||
|
||||
def get_upload_form_class(self):
|
||||
return get_image_form(self.model)
|
||||
|
||||
def get_edit_form_class(self):
|
||||
return get_image_multi_form(self.model)
|
||||
|
||||
def get_confirm_duplicate_upload_response(self, duplicates):
|
||||
return render_to_string(
|
||||
"wagtailimages/images/confirm_duplicate_upload.html",
|
||||
{
|
||||
"existing_image": duplicates[0],
|
||||
"delete_action": reverse(
|
||||
self.delete_object_url_name, args=(self.object.id,)
|
||||
),
|
||||
},
|
||||
request=self.request,
|
||||
)
|
||||
|
||||
def get_edit_object_response_data(self):
|
||||
data = super().get_edit_object_response_data()
|
||||
duplicates = find_image_duplicates(
|
||||
image=self.object,
|
||||
user=self.request.user,
|
||||
permission_policy=self.permission_policy,
|
||||
)
|
||||
if not duplicates:
|
||||
data.update(duplicate=False)
|
||||
else:
|
||||
data.update(
|
||||
duplicate=True,
|
||||
confirm_duplicate_upload=self.get_confirm_duplicate_upload_response(
|
||||
duplicates
|
||||
),
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
def save_object(self, form):
|
||||
image = form.save(commit=False)
|
||||
image.uploaded_by_user = self.request.user
|
||||
image.save()
|
||||
return image
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context.update(
|
||||
{
|
||||
"max_filesize": self.form.fields["file"].max_upload_size,
|
||||
"max_title_length": self.form.fields["title"].max_length,
|
||||
"allowed_extensions": get_allowed_image_extensions(),
|
||||
"error_max_file_size": self.form.fields["file"].error_messages[
|
||||
"file_too_large_unknown_size"
|
||||
],
|
||||
"error_accepted_file_types": self.form.fields["file"].error_messages[
|
||||
"invalid_image_extension"
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class EditView(BaseEditView):
|
||||
permission_policy = permission_policy
|
||||
pk_url_kwarg = "image_id"
|
||||
edit_object_form_prefix = "image"
|
||||
context_object_name = "image"
|
||||
context_object_id_name = "image_id"
|
||||
edit_object_url_name = "wagtailimages:edit_multiple"
|
||||
delete_object_url_name = "wagtailimages:delete_multiple"
|
||||
|
||||
def get_model(self):
|
||||
return get_image_model()
|
||||
|
||||
def get_edit_form_class(self):
|
||||
return get_image_multi_form(self.model)
|
||||
|
||||
|
||||
class DeleteView(BaseDeleteView):
|
||||
permission_policy = permission_policy
|
||||
pk_url_kwarg = "image_id"
|
||||
context_object_id_name = "image_id"
|
||||
|
||||
def get_model(self):
|
||||
return get_image_model()
|
||||
|
||||
|
||||
class CreateFromUploadedImageView(BaseCreateFromUploadView):
|
||||
edit_upload_url_name = "wagtailimages:create_multiple_from_uploaded_image"
|
||||
delete_upload_url_name = "wagtailimages:delete_upload_multiple"
|
||||
upload_pk_url_kwarg = "uploaded_file_id"
|
||||
edit_upload_form_prefix = "uploaded-image"
|
||||
context_object_id_name = "image_id"
|
||||
context_upload_name = "uploaded_image"
|
||||
|
||||
def get_model(self):
|
||||
return get_image_model()
|
||||
|
||||
def get_edit_form_class(self):
|
||||
return get_image_multi_form(self.model)
|
||||
|
||||
def save_object(self, form):
|
||||
# assign the file content from uploaded_image to the image object, to ensure it gets saved to
|
||||
# Image's storage
|
||||
|
||||
self.object.file.save(
|
||||
os.path.basename(self.upload.file.name), self.upload.file.file, save=False
|
||||
)
|
||||
self.object.uploaded_by_user = self.request.user
|
||||
|
||||
# form.save() would normally handle writing the image file metadata, but in this case the
|
||||
# file handling happens outside the form, so we need to do that manually
|
||||
self.object._set_image_file_metadata()
|
||||
|
||||
form.save()
|
||||
|
||||
|
||||
class DeleteUploadView(BaseDeleteUploadView):
|
||||
upload_pk_url_kwarg = "uploaded_file_id"
|
||||
|
||||
def get_model(self):
|
||||
return get_image_model()
|
||||
83
env/lib/python3.10/site-packages/wagtail/images/views/serve.py
vendored
Normal file
83
env/lib/python3.10/site-packages/wagtail/images/views/serve.py
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
|
||||
from django.http import FileResponse, HttpResponse
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.urls import reverse
|
||||
from django.utils.decorators import classonlymethod, method_decorator
|
||||
from django.views.decorators.cache import cache_control
|
||||
from django.views.generic import View
|
||||
|
||||
from wagtail.images import get_image_model
|
||||
from wagtail.images.exceptions import InvalidFilterSpecError
|
||||
from wagtail.images.models import SourceImageIOError
|
||||
from wagtail.images.utils import generate_signature, verify_signature
|
||||
from wagtail.utils.sendfile import sendfile
|
||||
|
||||
|
||||
def generate_image_url(image, filter_spec, viewname="wagtailimages_serve", key=None):
|
||||
signature = generate_signature(image.id, filter_spec, key)
|
||||
url = reverse(viewname, args=(signature, image.id, filter_spec))
|
||||
url += image.file.name[len("original_images/") :]
|
||||
return url
|
||||
|
||||
|
||||
class ServeView(View):
|
||||
model = get_image_model()
|
||||
action = "serve"
|
||||
key = None
|
||||
|
||||
@classonlymethod
|
||||
def as_view(cls, **initkwargs):
|
||||
if "action" in initkwargs:
|
||||
if initkwargs["action"] not in ["serve", "redirect"]:
|
||||
raise ImproperlyConfigured(
|
||||
"ServeView action must be either 'serve' or 'redirect'"
|
||||
)
|
||||
|
||||
return super().as_view(**initkwargs)
|
||||
|
||||
@method_decorator(cache_control(max_age=3600, public=True))
|
||||
def get(self, request, signature, image_id, filter_spec, filename=None):
|
||||
if not verify_signature(
|
||||
signature.encode(), image_id, filter_spec, key=self.key
|
||||
):
|
||||
raise PermissionDenied
|
||||
|
||||
image = get_object_or_404(self.model, id=image_id)
|
||||
|
||||
# Get/generate the rendition
|
||||
try:
|
||||
rendition = image.get_rendition(filter_spec)
|
||||
except SourceImageIOError:
|
||||
return HttpResponse(
|
||||
"Source image file not found", content_type="text/plain", status=410
|
||||
)
|
||||
except InvalidFilterSpecError:
|
||||
return HttpResponse(
|
||||
"Invalid filter spec: " + filter_spec,
|
||||
content_type="text/plain",
|
||||
status=400,
|
||||
)
|
||||
|
||||
return getattr(self, self.action)(rendition)
|
||||
|
||||
def serve(self, rendition):
|
||||
with rendition.get_willow_image() as willow_image:
|
||||
mime_type = willow_image.mime_type
|
||||
|
||||
# Serve the file
|
||||
rendition.file.open("rb")
|
||||
return FileResponse(rendition.file, content_type=mime_type)
|
||||
|
||||
def redirect(self, rendition):
|
||||
# Redirect to the file's public location
|
||||
return redirect(rendition.url)
|
||||
|
||||
|
||||
serve = ServeView.as_view()
|
||||
|
||||
|
||||
class SendFileView(ServeView):
|
||||
backend = None
|
||||
|
||||
def serve(self, rendition):
|
||||
return sendfile(self.request, rendition.file.path, backend=self.backend)
|
||||
Reference in New Issue
Block a user