Fixes #3866: Text counter with footnotes.
[redakcja.git] / src / fileupload / views.py
1 import json
2 import os
3 from urllib import quote
4 from django.conf import settings
5 from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404
6 from django.utils.decorators import method_decorator
7 from django.utils.encoding import smart_bytes
8 from django.views.decorators.vary import vary_on_headers
9 from django.views.generic import FormView, View
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, ValueError):
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
35 class UploadView(FormView):
36     template_name = "fileupload/picture_form.html"
37     form_class = UploadForm
38
39     def get_object(self, request, *args, **kwargs):
40         """Get any data for later use."""
41         return None
42
43     def get_directory(self):
44         """Directory relative to MEDIA_ROOT. Must end with a slash."""
45         return self.kwargs['path']
46
47     def breadcrumbs(self):
48         """List of tuples (name, url) or just (name,) for breadcrumbs.
49
50         Probably only the last item (representing currently browsed dir)
51         should lack url.
52
53         """
54         directory = self.get_directory()
55         now_path = os.path.dirname(self.request.get_full_path())
56         directory = os.path.dirname(directory)
57         if directory:
58             crumbs = [
59                 (os.path.basename(directory),)
60             ]
61             directory = os.path.dirname(directory)
62             now_path = (os.path.dirname(now_path))
63             while directory:
64                 crumbs.insert(0, (os.path.basename(directory), now_path+'/'))
65                 directory = os.path.dirname(directory)
66                 now_path = os.path.dirname(now_path)
67             crumbs.insert(0, ('media', now_path))
68         else:
69             crumbs = [('media',)]
70         return crumbs
71
72     def get_safe_path(self, filename=""):
73         """Finds absolute filesystem path of the browsed dir of file.
74         
75         Makes sure it's inside MEDIA_ROOT.
76         
77         """
78         filename = smart_bytes(filename)
79         path = os.path.abspath(os.path.join(
80                 settings.MEDIA_ROOT,
81                 smart_bytes(self.get_directory()),
82                 filename))
83         if not path.startswith(settings.MEDIA_ROOT):
84             raise Http404
85         if filename:
86             if not path.startswith(self.get_safe_path()):
87                 raise Http404
88         return path
89
90     def get_url(self, filename):
91         """Finds URL of a file in browsed dir."""
92         return settings.MEDIA_URL + self.get_directory() + quote(filename.encode('utf-8'))
93
94     @method_decorator(vary_on_headers('Accept'))
95     def dispatch(self, request, *args, **kwargs):
96         self.object = self.get_object(request, *args, **kwargs)
97         return super(UploadView, self).dispatch(request, *args, **kwargs)
98
99     def get(self, request, *args, **kwargs):
100         if request.is_ajax():
101             files = []
102             path = self.get_safe_path()
103             if os.path.isdir(path):
104                 for f in sorted(os.listdir(path)):
105                     file_info = {
106                         "name": f,
107                     }
108                     if os.path.isdir(os.path.join(path, f)):
109                         file_info.update({
110                             "url": "%s%s/" % (request.get_full_path(), f),
111                         })
112                     else:
113                         file_info.update({
114                             "url": self.get_url(f),
115                             'thumbnail_url': thumbnail(self.get_directory() + f),
116                             'delete_url': "%s?file=%s" % (
117                                 request.get_full_path(),
118                                 quote(f.encode('utf-8'))),
119                             'delete_type': "DELETE"
120                         })
121                         thumbnail_url = thumbnail(self.get_directory() + f),
122                     files.append(file_info)
123             return JSONResponse(files)
124         else:
125             return super(UploadView, self).get(request, *args, **kwargs)
126
127     def form_valid(self, form):
128         flist = self.request.FILES.getlist('files')
129         path = self.get_safe_path()
130         if not os.path.isdir(path):
131             os.makedirs(path)
132         data = []
133         for f in flist:
134             with open(self.get_safe_path(f.name), 'w') as destination:
135                 for chunk in f.chunks():
136                     destination.write(chunk)
137             data.append({
138                 'name': f.name, 
139                 'url': self.get_url(f.name),
140                 'thumbnail_url': thumbnail(self.get_directory() + f.name),
141                         'delete_url': "%s?file=%s" % (
142                             self.request.get_full_path(),
143                             quote(f.name.encode('utf-8'))),
144                 'delete_type': "DELETE"
145             })
146         response = JSONResponse(data)
147         response['Content-Disposition'] = 'inline; filename=files.json'
148         return response
149
150     def delete(self, request, *args, **kwargs):
151         os.unlink(self.get_safe_path(request.GET.get('file')))
152         response = JSONResponse(True)
153         response['Content-Disposition'] = 'inline; filename=files.json'
154         return response