import imghdr from wsgiref.util import FileWrapper import requests from django.conf import settings from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin from django.core.exceptions import PermissionDenied from django.http import HttpResponse, StreamingHttpResponse from django.http.request import HttpRequest from django.http.response import HttpResponseRedirect from django.shortcuts import get_object_or_404 from django.shortcuts import render from django.utils.decorators import method_decorator from django.views.decorators.cache import cache_control from django.views.decorators.csrf import ensure_csrf_cookie from graphene_django.views import GraphQLView from graphql import get_operation_ast, parse from sentry_sdk.api import start_transaction from wagtail.admin.views.pages import listing from wagtail.images import get_image_model from wagtail.images.exceptions import InvalidFilterSpecError from wagtail.images.models import Image from wagtail.images.models import SourceImageIOError from wagtail.images.utils import verify_signature from wagtail.images.views.serve import ServeView from core.logger import get_logger logger = get_logger(__name__) # For sentry perfomance monitoring # taken from https://jerrynsh.com/how-to-monitor-python-graphql-api-with-sentry/ class SentryGraphQLView(GraphQLView): def execute_graphql_request( self, request: HttpRequest, data, query, variables, operation_name, show_graphiql, ): """ adapted to use the new GraphQL 3.0 syntax, still need to get the operation type, but the code to do this changed significantly, so the above link only explains the 'what' and 'why', but no longer the 'how' """ document = parse(query) operation_type = get_operation_ast(document, operation_name).operation with start_transaction(op=str(operation_type), name=operation_name): return super().execute_graphql_request( request, data, query, variables, operation_name, show_graphiql ) class PrivateGraphQLView(LoginRequiredMixin, SentryGraphQLView): pass @ensure_csrf_cookie def home(request): if settings.SERVE_VIA_WEBPACK: try: res = requests.get( "http://localhost:8080{}".format(request.get_full_path()) ) headers = res.headers content_type = headers.get("content-type", "text/html") return HttpResponse(res.text, content_type=content_type) except Exception as e: print("Can not connect to dev server at http://localhost:8080:", e) return render(request, "index.html", {}) def override_wagtailadmin_explore_default_ordering(request, parent_page_id): """ Wrap Wagtail's explore view to change the default ordering """ if request.method == "GET" and "ordering" not in request.GET: # Display reordering handles by default for children of all Page types. return HttpResponseRedirect(request.path_info + "?ordering=ord") return listing.IndexView.as_view()(request, parent_page_id) class CustomImageServeView(ServeView): ''' Taken from wagtail.images.views.serve.ServeView the only change is that we use original for the filter_spec_for_signature''' model = get_image_model() action = "serve" key = None @method_decorator(cache_control(max_age=3600, public=True)) def get(self, request, signature, image_id, filter_spec, filename=None): # This variable is the only change to the wagtail implementation filter_spec_for_signature = 'original' if not verify_signature( signature.encode(), image_id, filter_spec_for_signature, 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)