1 # -*- coding: utf-8 -*-
4 from zipfile import ZipFile
5 from urllib import quote
6 from django.conf import settings
7 from django.http import HttpResponse, Http404
8 from django.utils.decorators import method_decorator
9 from django.utils.encoding import force_unicode
10 from django.views.decorators.vary import vary_on_headers
11 from django.views.generic import FormView, RedirectView
12 from unidecode import unidecode
14 from .forms import UploadForm
17 # Use sorl.thumbnail if available.
19 from sorl.thumbnail import default
21 def thumbnail(relpath):
22 return settings.MEDIA_URL + relpath
25 def thumbnail(relpath):
27 return default.backend.get_thumbnail(relpath, "x50").url
29 # That's not an image. No thumb.
33 class JSONResponse(HttpResponse):
34 """JSON response class."""
35 def __init__(self, obj=None, mimetype="application/json", *args, **kwargs):
36 content = json.dumps(obj)
37 super(JSONResponse, self).__init__(content, mimetype, *args, **kwargs)
40 class UploadViewMixin(object):
41 def get_safe_path(self, filename=""):
42 """Finds absolute filesystem path of the browsed dir of file.
44 Makes sure it's inside MEDIA_ROOT.
47 path = os.path.abspath(os.path.join(settings.MEDIA_ROOT, self.get_directory(), filename))
48 if not path.startswith(os.path.abspath(settings.MEDIA_ROOT)):
51 if not path.startswith(self.get_safe_path()):
53 return force_unicode(path)
56 class UploadView(UploadViewMixin, FormView):
57 template_name = "fileupload/picture_form.html"
58 form_class = UploadForm
60 def get_object(self, request, *args, **kwargs):
61 """Get any data for later use."""
64 def get_directory(self):
65 """Directory relative to MEDIA_ROOT. Must end with a slash."""
66 return self.kwargs['path']
68 def breadcrumbs(self):
69 """List of tuples (name, url) or just (name,) for breadcrumbs.
71 Probably only the last item (representing currently browsed dir)
75 directory = self.get_directory()
76 now_path = os.path.dirname(self.request.get_full_path())
77 directory = os.path.dirname(directory)
80 (os.path.basename(directory),)
82 directory = os.path.dirname(directory)
83 now_path = (os.path.dirname(now_path))
85 crumbs.insert(0, (os.path.basename(directory), now_path+'/'))
86 directory = os.path.dirname(directory)
87 now_path = os.path.dirname(now_path)
88 crumbs.insert(0, ('media', now_path))
93 def get_url(self, filename):
94 """Finds URL of a file in browsed dir."""
95 return settings.MEDIA_URL + self.get_directory() + quote(filename.encode('utf-8'))
97 @method_decorator(vary_on_headers('Accept'))
98 def dispatch(self, request, *args, **kwargs):
99 self.object = self.get_object(request, *args, **kwargs)
100 return super(UploadView, self).dispatch(request, *args, **kwargs)
102 def get(self, request, *args, **kwargs):
103 if request.is_ajax():
105 path = self.get_safe_path()
106 if os.path.isdir(path):
107 for f in sorted(os.listdir(path)):
111 if os.path.isdir(os.path.join(path, f)):
113 "url": "%s%s/" % (request.get_full_path(), f),
116 thumbnail_url = thumbnail(self.get_directory() + f)
118 "url": self.get_url(f),
119 'thumbnail_url': thumbnail_url,
120 'delete_url': "%s?file=%s" % (
121 request.get_full_path(),
122 quote(f.encode('utf-8'))),
123 'delete_type': "DELETE"
125 files.append(file_info)
126 return JSONResponse(files)
128 return super(UploadView, self).get(request, *args, **kwargs)
130 def form_valid(self, form):
131 flist = self.request.FILES.getlist('files')
132 path = self.get_safe_path()
133 if not os.path.isdir(path):
137 f.name = unidecode(f.name)
138 with open(self.get_safe_path(f.name), 'w') as destination:
139 for chunk in f.chunks():
140 destination.write(chunk)
143 'url': self.get_url(f.name),
144 'thumbnail_url': thumbnail(self.get_directory() + f.name),
145 'delete_url': "%s?file=%s" % (
146 self.request.get_full_path(),
147 quote(f.name.encode('utf-8'))),
148 'delete_type': "DELETE",
150 response = JSONResponse(data)
151 response['Content-Disposition'] = 'inline; filename=files.json'
154 def delete(self, request, *args, **kwargs):
155 os.unlink(self.get_safe_path(request.GET.get('file')))
156 response = JSONResponse(True)
157 response['Content-Disposition'] = 'inline; filename=files.json'
161 class PackageView(UploadViewMixin, RedirectView):
162 # usage of RedirectView here is really really ugly
165 def dispatch(self, request, *args, **kwargs):
166 self.object = self.get_object(request, *args, **kwargs)
167 path = self.get_safe_path()
168 with ZipFile(os.path.join(path, 'package.zip'), 'w') as zip_file:
169 for f in os.listdir(path):
170 if f == 'package.zip':
172 zip_file.write(os.path.join(path, f), arcname=f)
173 return super(PackageView, self).dispatch(request, *args, **kwargs)