filebrowser csrf issues fix
[redakcja.git] / apps / filebrowser / views.py
1 # coding: utf-8
2
3 # general imports
4 import re
5
6 # django imports
7 from django.shortcuts import render_to_response, HttpResponse
8 from django.template import RequestContext as Context
9 from django.http import HttpResponseRedirect
10 from django.contrib.admin.views.decorators import staff_member_required
11 from django.views.decorators.cache import never_cache
12 from django.utils.translation import ugettext as _
13 from django.conf import settings
14 from django import forms
15 from django.core.urlresolvers import reverse
16 from django.core.exceptions import ImproperlyConfigured
17 from django.dispatch import Signal
18 from django.views.decorators.csrf import csrf_exempt
19
20 from django.utils.encoding import smart_unicode, smart_str
21
22 # filebrowser imports
23 from filebrowser.fb_settings import *
24
25 from filebrowser.functions import (
26         _url_to_path, _path_to_url, _get_path, _get_file, _get_version_path,
27         _get_breadcrumbs, _get_filterdate, _get_settings_var, _handle_file_upload,
28         _get_file_type, _url_join, _convert_filename)
29
30 from filebrowser.templatetags.fb_tags import query_helper
31 from filebrowser.base import FileObject
32 from filebrowser.decorators import flash_login_required
33
34 # Precompile regular expressions
35 filter_re = []
36
37 for exp in EXCLUDE:
38     filter_re.append(re.compile(exp))
39 for k, v in VERSIONS.iteritems():
40     exp = (r'_%s.(%s)') % (k, '|'.join(EXTENSION_LIST))
41     filter_re.append(re.compile(exp))
42
43
44 def browse(request):
45     """
46     Browse Files/Directories.
47     """
48
49     # QUERY / PATH CHECK
50     query = request.GET
51     path = _get_path(query.get('dir', ''))
52     directory = _get_path('')
53
54     if path is None:
55         msg = _('Directory/File does not exist.')
56         request.user.message_set.create(message=msg)
57         if directory is None:
58             # The DIRECTORY does not exist, raise an error to prevent eternal redirecting.
59             raise ImproperlyConfigured(_("Error finding upload directory: %s. Maybe it does not exist?" % os.path.join(MEDIA_ROOT, DIRECTORY)))
60         redirect_url = reverse("fb_browse") + query_helper(query, "", "dir")
61         return HttpResponseRedirect(redirect_url)
62     abs_path = os.path.join(MEDIA_ROOT, DIRECTORY, path)
63
64     # INITIAL VARIABLES
65     results_var = {'results_total': 0, 'results_current': 0, 'delete_total': 0, 'images_total': 0, 'select_total': 0}
66     counter = {}
67     for k, v in EXTENSIONS.iteritems():
68         counter[k] = 0
69
70     dir_list = os.listdir(abs_path)
71     files = []
72     # print "LISTING FILES: ", dir_list
73     for file in dir_list:
74         # print repr(file)
75         # EXCLUDE FILES MATCHING VERSIONS_PREFIX OR ANY OF THE EXCLUDE PATTERNS
76         filtered = file.startswith('.')
77         for re_prefix in filter_re:
78             if re_prefix.search(file):
79                 filtered = True
80         if filtered:
81             continue
82         results_var['results_total'] += 1
83
84         # CREATE FILEOBJECT
85         fileobject = FileObject(os.path.join(smart_str(DIRECTORY), smart_str(path), smart_str(file)))
86
87         # FILTER / SEARCH
88         append = False
89         if fileobject.filetype == request.GET.get('filter_type', fileobject.filetype) and _get_filterdate(request.GET.get('filter_date', ''), fileobject.date):
90             append = True
91         if request.GET.get('q') and not re.compile(request.GET.get('q').lower(), re.M).search(file.lower()):
92             append = False
93
94         # APPEND FILE_LIST
95         if append:
96             files.append(fileobject)
97             results_var['results_current'] += 1
98             # COUNTER/RESULTS
99             if fileobject.filetype == 'Image':
100                 results_var['images_total'] += 1
101             if fileobject.filetype != 'Folder':
102                 results_var['delete_total'] += 1
103             elif fileobject.filetype == 'Folder' and fileobject.is_empty:
104                 results_var['delete_total'] += 1
105             if query.get('type') and query.get('type') in SELECT_FORMATS and fileobject.filetype in SELECT_FORMATS[query.get('type')]:
106                 results_var['select_total'] += 1
107             elif not query.get('type'):
108                 results_var['select_total'] += 1
109
110         # COUNTER/RESULTS
111         if fileobject.filetype:
112             counter[fileobject.filetype] += 1
113
114     # SORTING
115     files.sort(key=lambda e: getattr(e, request.GET.get('o', DEFAULT_ORDER)))
116     if request.GET.get('ot') == "desc":
117         files.reverse()
118
119     return render_to_response('filebrowser/index.html', {
120         'dir': path,
121         'files': files,
122         'results_var': results_var,
123         'counter': counter,
124         'query': query,
125         'title': _(u'FileBrowser'),
126         'settings_var': _get_settings_var(),
127         'breadcrumbs': _get_breadcrumbs(query, path, ''),
128     }, context_instance=Context(request))
129 browse = staff_member_required(never_cache(browse))
130
131
132 # mkdir signals
133 filebrowser_pre_createdir = Signal(providing_args=["path", "dirname"])
134 filebrowser_post_createdir = Signal(providing_args=["path", "dirname"])
135
136
137 def mkdir(request):
138     """
139     Make Directory.
140     """
141
142     from filebrowser.forms import MakeDirForm
143
144     # QUERY / PATH CHECK
145     query = request.GET
146     path = _get_path(query.get('dir', ''))
147     if path is None:
148         msg = _('Directory/File does not exist.')
149         request.user.message_set.create(message=msg)
150         return HttpResponseRedirect(reverse("fb_browse"))
151     abs_path = os.path.join(MEDIA_ROOT, DIRECTORY, path)
152
153     if request.method == 'POST':
154         form = MakeDirForm(abs_path, request.POST)
155         if form.is_valid():
156             server_path = os.path.join(abs_path, form.cleaned_data['dir_name'])
157             try:
158                 # PRE CREATE SIGNAL
159                 filebrowser_pre_createdir.send(sender=request, path=path, dirname=form.cleaned_data['dir_name'])
160                 # CREATE FOLDER
161                 os.mkdir(server_path)
162                 os.chmod(server_path, 0775)
163                 # POST CREATE SIGNAL
164                 filebrowser_post_createdir.send(sender=request, path=path, dirname=form.cleaned_data['dir_name'])
165                 # MESSAGE & REDIRECT
166                 msg = _('The Folder %s was successfully created.') % (form.cleaned_data['dir_name'])
167                 request.user.message_set.create(message=msg)
168                 # on redirect, sort by date desc to see the new directory on top of the list
169                 # remove filter in order to actually _see_ the new folder
170                 redirect_url = reverse("fb_browse") + query_helper(query, "ot=desc,o=date", "ot,o,filter_type,filter_date,q")
171                 return HttpResponseRedirect(redirect_url)
172             except OSError, (errno, strerror):
173                 if errno == 13:
174                     form.errors['dir_name'] = forms.util.ErrorList([_('Permission denied.')])
175                 else:
176                     form.errors['dir_name'] = forms.util.ErrorList([_('Error creating directory.')])
177     else:
178         form = MakeDirForm(abs_path)
179
180     return render_to_response('filebrowser/makedir.html', {
181         'form': form,
182         'query': query,
183         'title': _(u'New Folder'),
184         'settings_var': _get_settings_var(),
185         'breadcrumbs': _get_breadcrumbs(query, path, _(u'New Folder')),
186     }, context_instance=Context(request))
187 mkdir = staff_member_required(never_cache(mkdir))
188
189
190 @csrf_exempt
191 def upload(request):
192     """
193     Multipe File Upload.
194     """
195
196     from django.http import parse_cookie
197
198     # QUERY / PATH CHECK
199     query = request.GET
200     path = _get_path(query.get('dir', ''))
201     if path is None:
202         msg = _('Directory/File does not exist.')
203         request.user.message_set.create(message=msg)
204         return HttpResponseRedirect(reverse("fb_browse"))
205     abs_path = os.path.join(MEDIA_ROOT, DIRECTORY, path)
206
207     # SESSION (used for flash-uploading)
208     cookie_dict = parse_cookie(request.META.get('HTTP_COOKIE', ''))
209     engine = __import__(settings.SESSION_ENGINE, {}, {}, [''])
210     session_key = cookie_dict.get(settings.SESSION_COOKIE_NAME, None)
211
212     return render_to_response('filebrowser/upload.html', {
213         'query': query,
214         'title': _(u'Select files to upload'),
215         'settings_var': _get_settings_var(),
216         'breadcrumbs': _get_breadcrumbs(query, path, _(u'Upload')),
217         'session_key': session_key,
218     }, context_instance=Context(request))
219 upload = staff_member_required(never_cache(upload))
220
221
222 @csrf_exempt
223 def _check_file(request):
224     """
225     Check if file already exists on the server.
226     """
227
228     from django.utils import simplejson
229
230     folder = request.POST.get('folder')
231     fb_uploadurl_re = re.compile(r'^(%s)' % reverse("fb_upload"))
232     folder = fb_uploadurl_re.sub('', folder)
233
234     fileArray = {}
235     if request.method == 'POST':
236         for k, v in request.POST.items():
237             if k != "folder":
238                 v = _convert_filename(v)
239                 if os.path.isfile(os.path.join(MEDIA_ROOT, DIRECTORY, folder, v)):
240                     fileArray[k] = v
241
242     return HttpResponse(simplejson.dumps(fileArray))
243
244
245 # upload signals
246 filebrowser_pre_upload = Signal(providing_args=["path", "file"])
247 filebrowser_post_upload = Signal(providing_args=["path", "file"])
248
249
250 def _upload_file(request):
251     """
252     Upload file to the server.
253     """
254
255     print 'a'
256     from django.core.files.move import file_move_safe
257
258     if request.method == 'POST':
259         folder = request.POST.get('folder')
260         fb_uploadurl_re = re.compile(r'^(%s)' % reverse("fb_upload"))
261         folder = fb_uploadurl_re.sub('', folder)
262         abs_path = os.path.join(MEDIA_ROOT, DIRECTORY, folder)
263         if request.FILES:
264             filedata = request.FILES['Filedata']
265             filedata.name = _convert_filename(filedata.name)
266             # PRE UPLOAD SIGNAL
267             filebrowser_pre_upload.send(sender=request, path=request.POST.get('folder'), file=filedata)
268             # HANDLE UPLOAD
269             uploadedfile = _handle_file_upload(abs_path, filedata)
270             # MOVE UPLOADED FILE
271             # if file already exists
272             if os.path.isfile(os.path.join(MEDIA_ROOT, DIRECTORY, folder, filedata.name)):
273                 old_file = os.path.join(abs_path, filedata.name)
274                 new_file = os.path.join(abs_path, uploadedfile)
275                 file_move_safe(new_file, old_file)
276             # POST UPLOAD SIGNAL
277             filebrowser_post_upload.send(sender=request, path=request.POST.get('folder'), file=FileObject(os.path.join(DIRECTORY, folder, filedata.name)))
278     return HttpResponse('True')
279 _upload_file = csrf_exempt(flash_login_required(_upload_file))
280
281
282 # delete signals
283 filebrowser_pre_delete = Signal(providing_args=["path", "filename"])
284 filebrowser_post_delete = Signal(providing_args=["path", "filename"])
285
286
287 def delete(request):
288     """
289     Delete existing File/Directory.
290
291     When trying to delete a Directory, the Directory has to be empty.
292     """
293
294     # QUERY / PATH CHECK
295     query = request.GET
296     path = _get_path(query.get('dir', ''))
297     filename = _get_file(query.get('dir', ''), query.get('filename', ''))
298     if path is None or filename is None:
299         msg = _('Directory/File does not exist.')
300         request.user.message_set.create(message=msg)
301         return HttpResponseRedirect(reverse("fb_browse"))
302     abs_path = os.path.join(MEDIA_ROOT, DIRECTORY, path)
303
304     msg = ""
305     if request.GET:
306         if request.GET.get('filetype') != "Folder":
307             relative_server_path = os.path.join(DIRECTORY, path, filename)
308             try:
309                 # PRE DELETE SIGNAL
310                 filebrowser_pre_delete.send(sender=request, path=path, filename=filename)
311                 # DELETE IMAGE VERSIONS/THUMBNAILS
312                 for version in VERSIONS:
313                     try:
314                         os.unlink(os.path.join(MEDIA_ROOT, _get_version_path(relative_server_path, version)))
315                     except:
316                         pass
317                 # DELETE FILE
318                 os.unlink(os.path.join(abs_path, filename))
319                 # POST DELETE SIGNAL
320                 filebrowser_post_delete.send(sender=request, path=path, filename=filename)
321                 # MESSAGE & REDIRECT
322                 msg = _('The file %s was successfully deleted.') % (filename.lower())
323                 request.user.message_set.create(message=msg)
324                 redirect_url = reverse("fb_browse") + query_helper(query, "", "filename,filetype")
325                 return HttpResponseRedirect(redirect_url)
326             except OSError:
327                 # todo: define error message
328                 msg = OSError
329         else:
330             try:
331                 # PRE DELETE SIGNAL
332                 filebrowser_pre_delete.send(sender=request, path=path, filename=filename)
333                 # DELETE FOLDER
334                 os.rmdir(os.path.join(abs_path, filename))
335                 # POST DELETE SIGNAL
336                 filebrowser_post_delete.send(sender=request, path=path, filename=filename)
337                 # MESSAGE & REDIRECT
338                 msg = _('The directory %s was successfully deleted.') % (filename.lower())
339                 request.user.message_set.create(message=msg)
340                 redirect_url = reverse("fb_browse") + query_helper(query, "", "filename,filetype")
341                 return HttpResponseRedirect(redirect_url)
342             except OSError:
343                 # todo: define error message
344                 msg = OSError
345
346     if msg:
347         request.user.message_set.create(message=msg)
348
349     return render_to_response('filebrowser/index.html', {
350         'dir': dir_name,
351         'file': request.GET.get('filename', ''),
352         'query': query,
353         'settings_var': _get_settings_var(),
354         'breadcrumbs': _get_breadcrumbs(query, dir_name, ''),
355     }, context_instance=Context(request))
356 delete = staff_member_required(never_cache(delete))
357
358
359 # delete signals
360 filebrowser_pre_rename = Signal(providing_args=["path", "filename"])
361 filebrowser_post_rename = Signal(providing_args=["path", "filename"])
362
363
364 def rename(request):
365     """
366     Rename existing File/Directory.
367
368     Includes renaming existing Image Versions/Thumbnails.
369     """
370
371     from filebrowser.forms import RenameForm
372
373     # QUERY / PATH CHECK
374     query = request.GET
375     path = _get_path(query.get('dir', ''))
376     filename = _get_file(query.get('dir', ''), query.get('filename', ''))
377     if path is None or filename is None:
378         msg = _('Directory/File does not exist.')
379         request.user.message_set.create(message=msg)
380         return HttpResponseRedirect(reverse("fb_browse"))
381     abs_path = os.path.join(MEDIA_ROOT, DIRECTORY, path)
382     file_extension = os.path.splitext(filename)[1].lower()
383
384     if request.method == 'POST':
385         form = RenameForm(abs_path, file_extension, request.POST)
386         if form.is_valid():
387             relative_server_path = os.path.join(DIRECTORY, path, filename)
388             new_relative_server_path = os.path.join(DIRECTORY, path, form.cleaned_data['name'] + file_extension)
389             try:
390                 # PRE RENAME SIGNAL
391                 filebrowser_pre_delete.send(sender=request, path=path, filename=filename)
392                 # DELETE IMAGE VERSIONS/THUMBNAILS
393                 # regenerating versions/thumbs will be done automatically
394                 for version in VERSIONS:
395                     try:
396                         os.unlink(os.path.join(MEDIA_ROOT, _get_version_path(relative_server_path, version)))
397                     except:
398                         pass
399                 # RENAME ORIGINAL
400                 os.rename(os.path.join(MEDIA_ROOT, relative_server_path), os.path.join(MEDIA_ROOT, new_relative_server_path))
401                 # POST RENAME SIGNAL
402                 filebrowser_post_delete.send(sender=request, path=path, filename=filename)
403                 # MESSAGE & REDIRECT
404                 msg = _('Renaming was successful.')
405                 request.user.message_set.create(message=msg)
406                 redirect_url = reverse("fb_browse") + query_helper(query, "", "filename")
407                 return HttpResponseRedirect(redirect_url)
408             except OSError, (errno, strerror):
409                 form.errors['name'] = forms.util.ErrorList([_('Error.')])
410     else:
411         form = RenameForm(abs_path, file_extension)
412
413     return render_to_response('filebrowser/rename.html', {
414         'form': form,
415         'query': query,
416         'file_extension': file_extension,
417         'title': _(u'Rename "%s"') % filename,
418         'settings_var': _get_settings_var(),
419         'breadcrumbs': _get_breadcrumbs(query, path, _(u'Rename')),
420     }, context_instance=Context(request))
421 rename = staff_member_required(never_cache(rename))
422
423
424 def versions(request):
425     """
426     Show all Versions for an Image according to ADMIN_VERSIONS.
427     """
428
429     # QUERY / PATH CHECK
430     query = request.GET
431     path = _get_path(query.get('dir', ''))
432     filename = _get_file(query.get('dir', ''), query.get('filename', ''))
433     if path is None or filename is None:
434         msg = _('Directory/File does not exist.')
435         request.user.message_set.create(message=msg)
436         return HttpResponseRedirect(reverse("fb_browse"))
437     abs_path = os.path.join(MEDIA_ROOT, DIRECTORY, path)
438
439     return render_to_response('filebrowser/versions.html', {
440         'original': _path_to_url(os.path.join(DIRECTORY, path, filename)),
441         'query': query,
442         'title': _(u'Versions for "%s"') % filename,
443         'settings_var': _get_settings_var(),
444         'breadcrumbs': _get_breadcrumbs(query, path, _(u'Versions for "%s"') % filename),
445     }, context_instance=Context(request))
446
447 versions = staff_member_required(never_cache(versions))