89cc480fed776ab6ae3629611cad23adef72ec46
[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         # WTF how would that be possible?
47         if not path.startswith(os.path.abspath(settings.MEDIA_ROOT)):
48             raise Http404
49         if filename:
50             if not path.startswith(self.get_safe_path()):
51                 raise Http404
52         return force_unicode(path)
53
54
55 class UploadView(UploadViewMixin, FormView):
56     template_name = "fileupload/picture_form.html"
57     form_class = UploadForm
58
59     def get_object(self, request, *args, **kwargs):
60         """Get any data for later use."""
61         return None
62
63     def get_directory(self):
64         """Directory relative to MEDIA_ROOT. Must end with a slash."""
65         return self.kwargs['path']
66
67     def breadcrumbs(self):
68         """List of tuples (name, url) or just (name,) for breadcrumbs.
69
70         Probably only the last item (representing currently browsed dir)
71         should lack url.
72
73         """
74         directory = self.get_directory()
75         now_path = os.path.dirname(self.request.get_full_path())
76         directory = os.path.dirname(directory)
77         if directory:
78             crumbs = [
79                 (os.path.basename(directory),)
80             ]
81             directory = os.path.dirname(directory)
82             now_path = (os.path.dirname(now_path))
83             while directory:
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))
88         else:
89             crumbs = [('media',)]
90         return crumbs
91
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'))
95
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)
100
101     def get(self, request, *args, **kwargs):
102         if request.is_ajax():
103             files = []
104             path = self.get_safe_path()
105             if os.path.isdir(path):
106                 for f in sorted(os.listdir(path)):
107                     file_info = {
108                         "name": f,
109                     }
110                     if os.path.isdir(os.path.join(path, f)):
111                         file_info.update({
112                             "url": "%s%s/" % (request.get_full_path(), f),
113                         })
114                     else:
115                         thumbnail_url = thumbnail(self.get_directory() + f)
116                         file_info.update({
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"
123                         })
124                     files.append(file_info)
125             return JSONResponse(files)
126         else:
127             return super(UploadView, self).get(request, *args, **kwargs)
128
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):
133             os.makedirs(path)
134         data = []
135         for f in flist:
136             with open(self.get_safe_path(f.name), 'w') as destination:
137                 for chunk in f.chunks():
138                     destination.write(chunk)
139             data.append({
140                 'name': f.name, 
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",
147             })
148         response = JSONResponse(data)
149         response['Content-Disposition'] = 'inline; filename=files.json'
150         return response
151
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'
156         return response
157
158
159 class PackageView(UploadViewMixin, RedirectView):
160     def dispatch(self, request, *args, **kwargs):
161         self.object = self.get_object(request, *args, **kwargs)
162         path = self.get_safe_path()
163         with ZipFile(os.path.join(path, 'package.zip'), 'w') as zip_file:
164             for f in os.listdir(path):
165                 if f == 'package.zip':
166                     continue
167                 zip_file.write(os.path.join(path, f), arcname=f)
168         return super(PackageView, self).dispatch(request, *args, **kwargs)