mirror of
https://github.com/zulip/zulip.git
synced 2025-11-14 19:06:09 +00:00
Django 2.2.x is the next LTS release after Django 1.11.x; I expect we'll be on it for a while, as Django 3.x won't have an LTS release series out for a while. Because of upstream API changes in Django, this commit includes several changes beyond requirements and: * urls: django.urls.resolvers.RegexURLPattern has been replaced by django.urls.resolvers.URLPattern; affects OpenAPI code and related features which re-parse Django's internals. https://code.djangoproject.com/ticket/28593 * test_runner: Change number to suffix. Django changed the name in this ticket: https://code.djangoproject.com/ticket/28578 * Delete now-unnecessary SameSite cookie code (it's now the default). * forms: urlsafe_base64_encode returns string in Django 2.2. https://docs.djangoproject.com/en/2.2/ref/utils/#django.utils.http.urlsafe_base64_encode * upload: Django's File.size property replaces _get_size(). https://docs.djangoproject.com/en/2.2/_modules/django/core/files/base/ * process_queue: Migrate to new autoreload API. * test_messages: Add an extra query caused by .refresh_from_db() losing the .select_related() on the Realm object. * session: Sync SessionHostDomainMiddleware with Django 2.2. There's a lot more we can do to take advantage of the new release; this is tracked in #11341. Many changes by Tim Abbott, Umair Waheed, and Mateusz Mandera squashed are squashed into this commit. Fixes #10835.
78 lines
3.6 KiB
Python
78 lines
3.6 KiB
Python
from django.http import HttpRequest, HttpResponse, HttpResponseForbidden, \
|
|
HttpResponseNotFound
|
|
from django.shortcuts import redirect
|
|
from django.utils.cache import patch_cache_control
|
|
from django.utils.translation import ugettext as _
|
|
|
|
from zerver.lib.response import json_success, json_error
|
|
from zerver.lib.upload import upload_message_image_from_request, get_local_file_path, \
|
|
get_signed_upload_url, check_upload_within_quota, INLINE_MIME_TYPES
|
|
from zerver.models import UserProfile, validate_attachment_request
|
|
from django.conf import settings
|
|
from django_sendfile import sendfile
|
|
from mimetypes import guess_type
|
|
|
|
def serve_s3(request: HttpRequest, url_path: str) -> HttpResponse:
|
|
uri = get_signed_upload_url(url_path)
|
|
return redirect(uri)
|
|
|
|
def serve_local(request: HttpRequest, path_id: str) -> HttpResponse:
|
|
local_path = get_local_file_path(path_id)
|
|
if local_path is None:
|
|
return HttpResponseNotFound('<p>File not found</p>')
|
|
|
|
# Here we determine whether a browser should treat the file like
|
|
# an attachment (and thus clicking a link to it should download)
|
|
# or like a link (and thus clicking a link to it should display it
|
|
# in a browser tab). This is controlled by the
|
|
# Content-Disposition header; `django-sendfile2` sends the
|
|
# attachment-style version of that header if and only if the
|
|
# attachment argument is passed to it. For attachments,
|
|
# django-sendfile2 sets the response['Content-disposition'] like
|
|
# this: `attachment; filename="zulip.txt"; filename*=UTF-8''zulip.txt`.
|
|
# The filename* parameter is omitted for ASCII filenames like this one.
|
|
#
|
|
# The "filename" field (used to name the file when downloaded) is
|
|
# unreliable because it doesn't have a well-defined encoding; the
|
|
# newer filename* field takes precedence, since it uses a
|
|
# consistent format (urlquoted). For more details on filename*
|
|
# and filename, see the below docs:
|
|
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
|
|
mimetype, encoding = guess_type(local_path)
|
|
attachment = mimetype not in INLINE_MIME_TYPES
|
|
|
|
response = sendfile(request, local_path, attachment=attachment,
|
|
mimetype=mimetype, encoding=encoding)
|
|
patch_cache_control(response, private=True, immutable=True)
|
|
return response
|
|
|
|
def serve_file_backend(request: HttpRequest, user_profile: UserProfile,
|
|
realm_id_str: str, filename: str) -> HttpResponse:
|
|
path_id = "%s/%s" % (realm_id_str, filename)
|
|
is_authorized = validate_attachment_request(user_profile, path_id)
|
|
|
|
if is_authorized is None:
|
|
return HttpResponseNotFound(_("<p>File not found.</p>"))
|
|
if not is_authorized:
|
|
return HttpResponseForbidden(_("<p>You are not authorized to view this file.</p>"))
|
|
if settings.LOCAL_UPLOADS_DIR is not None:
|
|
return serve_local(request, path_id)
|
|
|
|
return serve_s3(request, path_id)
|
|
|
|
def upload_file_backend(request: HttpRequest, user_profile: UserProfile) -> HttpResponse:
|
|
if len(request.FILES) == 0:
|
|
return json_error(_("You must specify a file to upload"))
|
|
if len(request.FILES) != 1:
|
|
return json_error(_("You may only upload one file at a time"))
|
|
|
|
user_file = list(request.FILES.values())[0]
|
|
file_size = user_file.size
|
|
if settings.MAX_FILE_UPLOAD_SIZE * 1024 * 1024 < file_size:
|
|
return json_error(_("Uploaded file is larger than the allowed limit of %s MB") % (
|
|
settings.MAX_FILE_UPLOAD_SIZE))
|
|
check_upload_within_quota(user_profile.realm, file_size)
|
|
|
|
uri = upload_message_image_from_request(request, user_file, user_profile)
|
|
return json_success({'uri': uri})
|