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 .forms import UploadForm
15 # Use sorl.thumbnail if available.
17 from sorl.thumbnail import default
19 def thumbnail(relpath):
20 return settings.MEDIA_URL + relpath
23 def thumbnail(relpath):
25 return default.backend.get_thumbnail(relpath, "x50").url
27 # That's not an image. No thumb.
31 class JSONResponse(HttpResponse):
32 """JSON response class."""
33 def __init__(self, obj=None, mimetype="application/json", *args, **kwargs):
34 content = json.dumps(obj)
35 super(JSONResponse, self).__init__(content, mimetype, *args, **kwargs)
38 class UploadViewMixin(object):
39 def get_safe_path(self, filename=""):
40 """Finds absolute filesystem path of the browsed dir of file.
42 Makes sure it's inside MEDIA_ROOT.
45 path = os.path.abspath(os.path.join(settings.MEDIA_ROOT, self.get_directory(), filename))
46 # WTF how would that be possible?
47 if not path.startswith(os.path.abspath(settings.MEDIA_ROOT)):
50 if not path.startswith(self.get_safe_path()):
52 return force_unicode(path)
55 class UploadView(UploadViewMixin, FormView):
56 template_name = "fileupload/picture_form.html"
57 form_class = UploadForm
59 def get_object(self, request, *args, **kwargs):
60 """Get any data for later use."""
63 def get_directory(self):
64 """Directory relative to MEDIA_ROOT. Must end with a slash."""
65 return self.kwargs['path']
67 def breadcrumbs(self):
68 """List of tuples (name, url) or just (name,) for breadcrumbs.
70 Probably only the last item (representing currently browsed dir)
74 directory = self.get_directory()
75 now_path = os.path.dirname(self.request.get_full_path())
76 directory = os.path.dirname(directory)
79 (os.path.basename(directory),)
81 directory = os.path.dirname(directory)
82 now_path = (os.path.dirname(now_path))
84 crumbs.insert(0, (os.path.basename(directory), now_path+'/'))
85 directory = os.path.dirname(directory)
86 now_path = os.path.dirname(now_path)
87 crumbs.insert(0, ('media', now_path))
92 def get_url(self, filename):
93 """Finds URL of a file in browsed dir."""
94 return settings.MEDIA_URL + self.get_directory() + quote(filename.encode('utf-8'))
96 @method_decorator(vary_on_headers('Accept'))
97 def dispatch(self, request, *args, **kwargs):
98 self.object = self.get_object(request, *args, **kwargs)
99 return super(UploadView, self).dispatch(request, *args, **kwargs)
101 def get(self, request, *args, **kwargs):
102 if request.is_ajax():
104 path = self.get_safe_path()
105 if os.path.isdir(path):
106 for f in sorted(os.listdir(path)):
110 if os.path.isdir(os.path.join(path, f)):
112 "url": "%s%s/" % (request.get_full_path(), f),
115 thumbnail_url = thumbnail(self.get_directory() + f)
117 "url": self.get_url(f),
118 'thumbnail_url': thumbnail_url,
119 'delete_url': "%s?file=%s" % (
120 request.get_full_path(),
121 quote(f.encode('utf-8'))),
122 'delete_type': "DELETE"
124 files.append(file_info)
125 return JSONResponse(files)
127 return super(UploadView, self).get(request, *args, **kwargs)
129 def form_valid(self, form):
130 flist = self.request.FILES.getlist('files')
131 path = self.get_safe_path()
132 if not os.path.isdir(path):
136 with open(self.get_safe_path(f.name), 'w') as destination:
137 for chunk in f.chunks():
138 destination.write(chunk)
141 'url': self.get_url(f.name),
142 'thumbnail_url': thumbnail(self.get_directory() + f.name),
143 'delete_url': "%s?file=%s" % (
144 self.request.get_full_path(),
145 quote(f.name.encode('utf-8'))),
146 'delete_type': "DELETE",
148 response = JSONResponse(data)
149 response['Content-Disposition'] = 'inline; filename=files.json'
152 def delete(self, request, *args, **kwargs):
153 os.unlink(self.get_safe_path(request.GET.get('file')))
154 response = JSONResponse(True)
155 response['Content-Disposition'] = 'inline; filename=files.json'
159 class PackageView(UploadViewMixin, RedirectView):
160 # usage of RedirectView here is really really ugly
163 def dispatch(self, request, *args, **kwargs):
164 self.object = self.get_object(request, *args, **kwargs)
165 path = self.get_safe_path()
166 with ZipFile(os.path.join(path, 'package.zip'), 'w') as zip_file:
167 for f in os.listdir(path):
168 if f == 'package.zip':
170 zip_file.write(os.path.join(path, f), arcname=f)
171 return super(PackageView, self).dispatch(request, *args, **kwargs)