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