X-Git-Url: https://git.mdrn.pl/redakcja.git/blobdiff_plain/43116c58e5c56f94ef358a5a17fb13a252e02531..98e406af0cc6b5d9b94441ee3f6b67a64d4eb591:/apps/fileupload/views.py diff --git a/apps/fileupload/views.py b/apps/fileupload/views.py index 217a78b5..c7b93187 100644 --- a/apps/fileupload/views.py +++ b/apps/fileupload/views.py @@ -1,9 +1,38 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# import json import os -from .forms import UploadForm -from django.views.generic import FormView, View -from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404 +from urllib import quote from django.conf import settings +from django.http import HttpResponse, Http404 +from django.utils.decorators import method_decorator +from django.views.decorators.vary import vary_on_headers +from django.views.generic import FormView +from unidecode import unidecode + +from .forms import UploadForm + + +# Use sorl.thumbnail if available. +try: + from sorl.thumbnail import default +except ImportError: + def thumbnail(relpath): + return settings.MEDIA_URL + relpath +else: + def thumbnail(relpath): + try: + thumb = default.backend.get_thumbnail(relpath, "x50") + if not thumb.exists(): + # That's not an image. No thumb. + return None + return thumb.url + except IOError: + # That's not an image. No thumb. + return None class JSONResponse(HttpResponse): @@ -12,25 +41,68 @@ class JSONResponse(HttpResponse): content = json.dumps(obj) super(JSONResponse, self).__init__(content, mimetype, *args, **kwargs) -class UploadView(FormView): - template_name = "fileupload/picture_form.html" - form_class = UploadForm +class UploadViewMixin(object): def get_safe_path(self, filename=""): + """Finds absolute filesystem path of the browsed dir of file. + + Makes sure it's inside MEDIA_ROOT. + + """ path = os.path.abspath(os.path.join( settings.MEDIA_ROOT, self.get_directory(), filename)) - if not path.startswith(settings.MEDIA_ROOT): + if not path.startswith(os.path.abspath(settings.MEDIA_ROOT)): raise Http404 if filename: if not path.startswith(self.get_safe_path()): raise Http404 return path - def get_url(self, filename=""): - return settings.MEDIA_URL + self.get_directory() + filename +class UploadView(UploadViewMixin, FormView): + template_name = "fileupload/picture_form.html" + form_class = UploadForm + + def get_object(self, request, *args, **kwargs): + """Get any data for later use.""" + return None + + def get_directory(self): + """Directory relative to MEDIA_ROOT. Must end with a slash.""" + return self.kwargs['path'] + + def breadcrumbs(self): + """List of tuples (name, url) or just (name,) for breadcrumbs. + + Probably only the last item (representing currently browsed dir) + should lack url. + + """ + directory = self.get_directory() + now_path = os.path.dirname(self.request.get_full_path()) + directory = os.path.dirname(directory) + if directory: + crumbs = [ + (os.path.basename(directory),) + ] + directory = os.path.dirname(directory) + now_path = (os.path.dirname(now_path)) + while directory: + crumbs.insert(0, (os.path.basename(directory), now_path + '/')) + directory = os.path.dirname(directory) + now_path = os.path.dirname(now_path) + crumbs.insert(0, ('media', now_path)) + else: + crumbs = [('media',)] + return crumbs + + def get_url(self, filename): + """Finds URL of a file in browsed dir.""" + return settings.MEDIA_URL + self.get_directory() + quote(filename.encode('utf-8')) + + @method_decorator(vary_on_headers('Accept')) def dispatch(self, request, *args, **kwargs): self.object = self.get_object(request, *args, **kwargs) return super(UploadView, self).dispatch(request, *args, **kwargs) @@ -38,37 +110,50 @@ class UploadView(FormView): def get(self, request, *args, **kwargs): if request.is_ajax(): files = [] - path = self.get_safe_path() if os.path.isdir(path): - for f in os.listdir(path): - files.append({ + for f in sorted(os.listdir(path)): + file_info = { "name": f, - "url": self.get_url(f), - 'thumbnail_url': self.get_url(f), # FIXME: thumb! - 'delete_url': "%s?file=%s" % (request.get_full_path(), f), # FIXME: encode - 'delete_type': "DELETE" - }) + } + if os.path.isdir(os.path.join(path, f)): + file_info.update({ + "url": "%s%s/" % (request.get_full_path(), f), + }) + else: + file_info.update({ + "url": self.get_url(f), + 'thumbnail_url': thumbnail(self.get_directory() + f), + 'delete_url': "%s?file=%s" % ( + request.get_full_path(), + quote(f.encode('utf-8'))), + 'delete_type': "DELETE" + }) + files.append(file_info) return JSONResponse(files) else: return super(UploadView, self).get(request, *args, **kwargs) def form_valid(self, form): - f = self.request.FILES.get('file') + flist = self.request.FILES.getlist('files') path = self.get_safe_path() - if not os.path.isdir(path): os.makedirs(path) - with open(self.get_safe_path(f.name), 'w') as destination: - for chunk in f.chunks(): - destination.write(chunk) - data = [{ - 'name': f.name, - 'url': self.get_url(f.name), - 'thumbnail_url': self.get_url(f.name), # FIXME: thumb! - 'delete_url': "%s?file=%s" % (self.request.get_full_path(), f.name), # FIXME: encode - 'delete_type': "DELETE" - }] + data = [] + for f in flist: + f.name = unidecode(f.name) + with open(self.get_safe_path(f.name), 'w') as destination: + for chunk in f.chunks(): + destination.write(chunk) + data.append({ + 'name': f.name, + 'url': self.get_url(f.name), + 'thumbnail_url': thumbnail(self.get_directory() + f.name), + 'delete_url': "%s?file=%s" % ( + self.request.get_full_path(), + quote(f.name.encode('utf-8'))), + 'delete_type': "DELETE" + }) response = JSONResponse(data) response['Content-Disposition'] = 'inline; filename=files.json' return response