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