minor cleanup
[redakcja.git] / apps / fileupload / views.py
1 # -*- coding: utf-8 -*-
2 import json
3 import os
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
13
14
15 # Use sorl.thumbnail if available.
16 try:
17     from sorl.thumbnail import default
18 except ImportError:
19     def thumbnail(relpath):
20         return settings.MEDIA_URL + relpath
21     default = None
22 else:
23     def thumbnail(relpath):
24         try:
25             return default.backend.get_thumbnail(relpath, "x50").url
26         except IOError:
27             # That's not an image. No thumb.
28             return None
29
30
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)
36
37
38 class UploadViewMixin(object):
39     def get_safe_path(self, filename=""):
40         """Finds absolute filesystem path of the browsed dir of file.
41
42         Makes sure it's inside MEDIA_ROOT.
43
44         """
45         path = os.path.abspath(os.path.join(settings.MEDIA_ROOT, self.get_directory(), filename))
46         if not path.startswith(os.path.abspath(settings.MEDIA_ROOT)):
47             raise Http404
48         if filename:
49             if not path.startswith(self.get_safe_path()):
50                 raise Http404
51         return force_unicode(path)
52
53
54 class UploadView(UploadViewMixin, FormView):
55     template_name = "fileupload/picture_form.html"
56     form_class = UploadForm
57
58     def get_object(self, request, *args, **kwargs):
59         """Get any data for later use."""
60         return None
61
62     def get_directory(self):
63         """Directory relative to MEDIA_ROOT. Must end with a slash."""
64         return self.kwargs['path']
65
66     def breadcrumbs(self):
67         """List of tuples (name, url) or just (name,) for breadcrumbs.
68
69         Probably only the last item (representing currently browsed dir)
70         should lack url.
71
72         """
73         directory = self.get_directory()
74         now_path = os.path.dirname(self.request.get_full_path())
75         directory = os.path.dirname(directory)
76         if directory:
77             crumbs = [
78                 (os.path.basename(directory),)
79             ]
80             directory = os.path.dirname(directory)
81             now_path = (os.path.dirname(now_path))
82             while directory:
83                 crumbs.insert(0, (os.path.basename(directory), now_path+'/'))
84                 directory = os.path.dirname(directory)
85                 now_path = os.path.dirname(now_path)
86             crumbs.insert(0, ('media', now_path))
87         else:
88             crumbs = [('media',)]
89         return crumbs
90
91     def get_url(self, filename):
92         """Finds URL of a file in browsed dir."""
93         return settings.MEDIA_URL + self.get_directory() + quote(filename.encode('utf-8'))
94
95     @method_decorator(vary_on_headers('Accept'))
96     def dispatch(self, request, *args, **kwargs):
97         self.object = self.get_object(request, *args, **kwargs)
98         return super(UploadView, self).dispatch(request, *args, **kwargs)
99
100     def get(self, request, *args, **kwargs):
101         if request.is_ajax():
102             files = []
103             path = self.get_safe_path()
104             if os.path.isdir(path):
105                 for f in sorted(os.listdir(path)):
106                     file_info = {
107                         "name": f,
108                     }
109                     if os.path.isdir(os.path.join(path, f)):
110                         file_info.update({
111                             "url": "%s%s/" % (request.get_full_path(), f),
112                         })
113                     else:
114                         thumbnail_url = thumbnail(self.get_directory() + f)
115                         file_info.update({
116                             "url": self.get_url(f),
117                             'thumbnail_url': thumbnail_url,
118                             'delete_url': "%s?file=%s" % (
119                                 request.get_full_path(),
120                                 quote(f.encode('utf-8'))),
121                             'delete_type': "DELETE"
122                         })
123                     files.append(file_info)
124             return JSONResponse(files)
125         else:
126             return super(UploadView, self).get(request, *args, **kwargs)
127
128     def form_valid(self, form):
129         flist = self.request.FILES.getlist('files')
130         path = self.get_safe_path()
131         if not os.path.isdir(path):
132             os.makedirs(path)
133         data = []
134         for f in flist:
135             with open(self.get_safe_path(f.name), 'w') as destination:
136                 for chunk in f.chunks():
137                     destination.write(chunk)
138             data.append({
139                 'name': f.name,
140                 'url': self.get_url(f.name),
141                 'thumbnail_url': thumbnail(self.get_directory() + f.name),
142                 'delete_url': "%s?file=%s" % (
143                     self.request.get_full_path(),
144                     quote(f.name.encode('utf-8'))),
145                 'delete_type': "DELETE",
146             })
147         response = JSONResponse(data)
148         response['Content-Disposition'] = 'inline; filename=files.json'
149         return response
150
151     def delete(self, request, *args, **kwargs):
152         os.unlink(self.get_safe_path(request.GET.get('file')))
153         response = JSONResponse(True)
154         response['Content-Disposition'] = 'inline; filename=files.json'
155         return response
156
157
158 class PackageView(UploadViewMixin, RedirectView):
159     # usage of RedirectView here is really really ugly
160     permanent = False
161
162     def dispatch(self, request, *args, **kwargs):
163         self.object = self.get_object(request, *args, **kwargs)
164         path = self.get_safe_path()
165         with ZipFile(os.path.join(path, 'package.zip'), 'w') as zip_file:
166             for f in os.listdir(path):
167                 if f == 'package.zip':
168                     continue
169                 zip_file.write(os.path.join(path, f), arcname=f)
170         return super(PackageView, self).dispatch(request, *args, **kwargs)