diff --git a/.gitignore b/.gitignore index 0762cf3..de659b8 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ dist/ django_media_manager.egg-info example/example/local_settings.py .DS_Store +.idea/ diff --git a/filebrowser/__init__.py b/filebrowser/__init__.py index e69de29..bef898c 100755 --- a/filebrowser/__init__.py +++ b/filebrowser/__init__.py @@ -0,0 +1 @@ +default_app_config = "filebrowser.apps.FilebrowserAppConfig" diff --git a/filebrowser/apps.py b/filebrowser/apps.py new file mode 100644 index 0000000..6812147 --- /dev/null +++ b/filebrowser/apps.py @@ -0,0 +1,50 @@ +from django.apps import AppConfig +from django.views.decorators.cache import never_cache +from django.core.urlresolvers import reverse +from django.utils.translation import ugettext as _ + +from filebrowser.conf import fb_settings + + +class FilebrowserAppConfig(AppConfig): + name = 'filebrowser' + + def ready(self): + from django.contrib import admin + from django.contrib.admin import sites + + class FilebrowserAdminSite(admin.AdminSite): + @never_cache + def index(self, request, extra_context=None): + resp = super(FilebrowserAdminSite, self).index(request, + extra_context) + app_dict = { + 'app_url': reverse('fb_browse'), + 'models': [ + { + 'admin_url': reverse('fb_browse'), + 'name': 'Browse', + 'add_url': None + }, + { + 'admin_url': reverse('fb_mkdir'), + 'name': _('New Folder'), + 'add_url': None + }, + { + 'admin_url': reverse('fb_upload'), + 'name': _('Upload'), + 'add_url': None + } + ], + 'has_module_perms': True, + 'name': _('Filebrowser'), + 'app_label': 'filebrowser' + } + resp.context_data['app_list'].append(app_dict) + return resp + + if fb_settings.SHOW_AT_ADMIN_PANEL: + fb = FilebrowserAdminSite() + admin.site = fb + sites.site = fb diff --git a/filebrowser/base.py b/filebrowser/base.py index 5c5d6cb..fa2c061 100755 --- a/filebrowser/base.py +++ b/filebrowser/base.py @@ -1,25 +1,15 @@ # coding: utf-8 # imports -import os, re, datetime -from time import gmtime, strftime - -# django imports -from django.conf import settings +import os +import re +import datetime +from PIL import Image # filebrowser imports -from filebrowser.settings import * +from filebrowser.settings import SAVE_FULL_URL, ADMIN_THUMBNAIL from filebrowser.conf import fb_settings -from filebrowser.functions import get_file_type, url_join, is_selectable, get_version_path - -# PIL import -if STRICT_PIL: - from PIL import Image -else: - try: - from PIL import Image - except ImportError: - import Image +from filebrowser.functions import get_file_type, url_join, get_version_path class FileObject(object): @@ -30,21 +20,29 @@ class FileObject(object): """ def __init__(self, path): - ''' - `os.path.split` Split the pathname path into a pair, (head, tail) where tail is the last pathname component and head is everything leading up to that. The tail part will never contain a slash; if path ends in a slash, tail will be empty. If there is no slash in path, head will be empty. If path is empty, both head and tail are empty. - ''' + """ + `os.path.split` Split the pathname path into a pair, (head, tail) + where tail is the last pathname component and head is everything + leading up to that. The tail part will never contain a slash; + if path ends in a slash, tail will be empty. + If there is no slash in path, head will be empty. + If path is empty, both head and tail are empty. + """ self.path = path - self.url_rel = path.replace("\\","/") + self.url_rel = path.replace("\\", "/") self.head, self.filename = os.path.split(path) - self.filename_lower = self.filename.lower() # important for sorting - self.filetype = get_file_type(self.filename) # strange if file no extension then this folder + # important for sorting + self.filename_lower = self.filename.lower() + # strange if file no extension then this folder + self.filetype = get_file_type(self.filename) def _filesize(self): """ Filesize. """ - path = (self.path) - if os.path.isfile(os.path.join(fb_settings.MEDIA_ROOT, path)) or os.path.isdir(os.path.join(fb_settings.MEDIA_ROOT, path)): + path = self.path + if os.path.isfile(os.path.join(fb_settings.MEDIA_ROOT, path)) or \ + os.path.isdir(os.path.join(fb_settings.MEDIA_ROOT, path)): return os.path.getsize(os.path.join(fb_settings.MEDIA_ROOT, path)) return "" filesize = property(_filesize) @@ -53,8 +51,11 @@ def _date(self): """ Date. """ - if os.path.isfile(os.path.join(fb_settings.MEDIA_ROOT, self.path)) or os.path.isdir(os.path.join(fb_settings.MEDIA_ROOT, self.path)): - return os.path.getmtime(os.path.join(fb_settings.MEDIA_ROOT, self.path)) + if os.path.isfile(os.path.join(fb_settings.MEDIA_ROOT, self.path)) or \ + os.path.isdir(os.path.join(fb_settings.MEDIA_ROOT, self.path)): + return os.path.getmtime( + os.path.join(fb_settings.MEDIA_ROOT, self.path) + ) return "" date = property(_date) @@ -96,9 +97,9 @@ def _path_relative_directory(self): """ Path relative to initial directory. """ - directory_re = re.compile(r'^({0})'.format((fb_settings.DIRECTORY))) + directory_re = re.compile(r'^({0})'.format(fb_settings.DIRECTORY)) value = directory_re.sub('', self.path) - return u"{0}".format(value) + return value path_relative_directory = property(_path_relative_directory) def _url_relative(self): @@ -109,7 +110,7 @@ def _url_full(self): """ Full URL including MEDIA_URL. """ - return (url_join(fb_settings.MEDIA_URL, self.url_rel)) + return url_join(fb_settings.MEDIA_URL, self.url_rel) url_full = property(_url_full) def _url_save(self): @@ -127,18 +128,23 @@ def _url_thumbnail(self): Thumbnail URL. """ if self.filetype == "Image": - return u"{0}".format(url_join(fb_settings.MEDIA_URL, get_version_path(self.path, ADMIN_THUMBNAIL))) + return "{0}".format( + url_join( + fb_settings.MEDIA_URL, + get_version_path(self.path, ADMIN_THUMBNAIL) + ) + ) else: return "" url_thumbnail = property(_url_thumbnail) def url_admin(self): if self.filetype_checked == "Folder": - directory_re = re.compile(r'^({0})'.format((fb_settings.DIRECTORY))) + directory_re = re.compile(r'^({0})'.format(fb_settings.DIRECTORY)) value = directory_re.sub('', self.path) - return u"{0}".format(value) + return "{0}".format(value) else: - return u"{0}".format(url_join(fb_settings.MEDIA_URL, self.path)) + return "{0}".format(url_join(fb_settings.MEDIA_URL, self.path)) def _dimensions(self): """ @@ -146,9 +152,11 @@ def _dimensions(self): """ if self.filetype == 'Image': try: - im = Image.open(os.path.join(fb_settings.MEDIA_ROOT, self.path)) + im = Image.open( + os.path.join(fb_settings.MEDIA_ROOT, self.path) + ) return im.size - except: + except IOError: pass else: return False @@ -195,12 +203,10 @@ def _is_empty(self): is_empty = property(_is_empty) def __repr__(self): - return (self.url_save) + return self.url_save def __str__(self): - return (self.url_save) + return self.url_save def __unicode__(self): - return (self.url_save) - - + return self.url_save diff --git a/filebrowser/conf.py b/filebrowser/conf.py index 2fd3ded..c516a90 100755 --- a/filebrowser/conf.py +++ b/filebrowser/conf.py @@ -1,6 +1,6 @@ - from filebrowser import settings + class FileBrowserSettings(object): """ Proxy for file browser settings defined at module level diff --git a/filebrowser/decorators.py b/filebrowser/decorators.py index 792bc33..2d2c9a1 100755 --- a/filebrowser/decorators.py +++ b/filebrowser/decorators.py @@ -21,9 +21,6 @@ def decorator(request, *args, **kwargs): session_data = engine.SessionStore(request.POST.get('session_key')) user_id = session_data['_auth_user_id'] # will return 404 if the session ID does not resolve to a valid user - User = get_user_model() - request.user = get_object_or_404(User, pk=user_id) + request.user = get_object_or_404(get_user_model(), pk=user_id) return function(request, *args, **kwargs) return decorator - - diff --git a/filebrowser/fields.py b/filebrowser/fields.py index 7019e1c..13bb941 100755 --- a/filebrowser/fields.py +++ b/filebrowser/fields.py @@ -4,27 +4,19 @@ import os # django imports -from django.db import models from django import forms from django.forms.widgets import Input -from django.db.models.fields import Field, CharField +from django.db.models.fields import Field from django.template.loader import render_to_string from django.utils.translation import ugettext_lazy as _ # filebrowser imports -from filebrowser.settings import * +from filebrowser.settings import ( + URL_FILEBROWSER_MEDIA, ADMIN_THUMBNAIL, DEBUG, EXTENSIONS, MEDIA_ROOT, + DIRECTORY +) from filebrowser.base import FileObject -from filebrowser.conf import fb_settings -from filebrowser.functions import url_to_path - - -def _template(): - if fb_settings.SUIT_TEMPLATE: - path = 'suit/' - else: - path = 'filebrowser/' - - return path +from filebrowser.functions import url_to_path, _template class FileBrowseWidget(Input): @@ -37,17 +29,19 @@ def __init__(self, attrs=None): self.directory = attrs.get('directory', '') self.extensions = attrs.get('extensions', '') self.format = attrs.get('format', '') - if attrs is not None: - self.attrs = attrs.copy() - else: - self.attrs = {} + super(FileBrowseWidget, self).__init__(attrs) + # if field have a directory - create it + dir_path = os.path.join(MEDIA_ROOT, DIRECTORY, self.directory) + if not os.path.exists(dir_path): + os.makedirs(dir_path) def render(self, name, value, attrs=None): if value is None: value = "" final_attrs = self.build_attrs(attrs, type=self.input_type, name=name) - final_attrs[ - 'search_icon'] = URL_FILEBROWSER_MEDIA + 'img/filebrowser_icon_show.gif' + final_attrs['search_icon'] = os.path.join( + URL_FILEBROWSER_MEDIA, 'img/filebrowser_icon_show.gif' + ) final_attrs['directory'] = self.directory final_attrs['extensions'] = self.extensions final_attrs['format'] = self.format @@ -63,8 +57,6 @@ def render(self, name, value, attrs=None): class FileBrowseFormField(forms.CharField): - widget = FileBrowseWidget - default_error_messages = { 'extension': _( u'Extension %(ext)s is not allowed. Only %(allowed)s is allowed.'), @@ -76,9 +68,15 @@ def __init__(self, max_length=None, min_length=None, self.max_length, self.min_length = max_length, min_length self.directory = directory self.extensions = extensions - if format: - self.format = format or '' - self.extensions = extensions or EXTENSIONS.get(format) + self.format = format or '' + self.extensions = extensions or EXTENSIONS.get(format) + + attrs = { + "directory": self.directory, + "extensions": self.extensions, + "format": self.format + } + self.widget = FileBrowseWidget(attrs) super(FileBrowseFormField, self).__init__(*args, **kwargs) def clean(self, value): @@ -86,7 +84,7 @@ def clean(self, value): if value == '': return value file_extension = os.path.splitext(value)[1].lower() - if self.extensions and not file_extension in self.extensions: + if self.extensions and file_extension not in self.extensions: raise forms.ValidationError( self.error_messages['extension'] % {'ext': file_extension, 'allowed': ", ".join( @@ -99,6 +97,7 @@ def __init__(self, *args, **kwargs): self.directory = kwargs.pop('directory', '') self.extensions = kwargs.pop('extensions', '') self.format = kwargs.pop('format', '') + kwargs['max_length'] = kwargs.get('max_length', 200) super(FileBrowseField, self).__init__(*args, **kwargs) def from_db_value(self, value, expression, connection, context): @@ -116,17 +115,20 @@ def get_db_prep_value(self, value, connection, prepared=False): return None return str(value) - def get_manipulator_field_objs(self): - return [oldforms.TextField] + # FIXME: recheck or need it + # @staticmethod + # def get_manipulator_field_objs(): + # return [oldforms.TextField] def get_internal_type(self): return "CharField" def formfield(self, **kwargs): - attrs = {} - attrs["directory"] = self.directory - attrs["extensions"] = self.extensions - attrs["format"] = self.format + attrs = { + "directory": self.directory, + "extensions": self.extensions, + "format": self.format + } defaults = { 'form_class': FileBrowseFormField, 'widget': FileBrowseWidget(attrs=attrs), @@ -136,11 +138,3 @@ def formfield(self, **kwargs): } defaults.update(kwargs) return super(FileBrowseField, self).formfield(**defaults) - - -try: - from south.modelsinspector import add_introspection_rules - - add_introspection_rules([], ["^filebrowser\.fields\.FileBrowseField"]) -except: - pass diff --git a/filebrowser/forms.py b/filebrowser/forms.py index e958e2f..8284b3f 100755 --- a/filebrowser/forms.py +++ b/filebrowser/forms.py @@ -1,38 +1,49 @@ # coding: utf-8 # imports -import re, os +import re +import os # django imports from django import forms -from django.forms.formsets import BaseFormSet from django.utils.translation import ugettext as _ # filebrowser imports -from filebrowser.settings import MAX_UPLOAD_SIZE, FOLDER_REGEX +from filebrowser.settings import FOLDER_REGEX from filebrowser.functions import convert_filename alnum_name_re = re.compile(FOLDER_REGEX) + class MakeDirForm(forms.Form): """ Form for creating Folder. """ - + dir_name = forms.CharField( + widget=forms.TextInput( + attrs={'class': 'vTextField', 'max_length': 50, 'min_length': 3} + ), + label=_('Name'), + help_text=_('Only letters, numbers, underscores, ' + 'spaces and hyphens are allowed.'), + required=True + ) + def __init__(self, path, *args, **kwargs): self.path = path super(MakeDirForm, self).__init__(*args, **kwargs) - - dir_name = forms.CharField(widget=forms.TextInput(attrs=dict({ 'class': 'vTextField' }, max_length=50, min_length=3)), label=_(u'Name'), help_text=_(u'Only letters, numbers, underscores, spaces and hyphens are allowed.'), required=True) def clean_dir_name(self): - if self.cleaned_data['dir_name']: - # only letters, numbers, underscores, spaces and hyphens are allowed. - if not alnum_name_re.search(self.cleaned_data['dir_name']): - raise forms.ValidationError(_(u'Only letters, numbers, underscores, spaces and hyphens are allowed.')) - # Folder must not already exist. - if os.path.isdir(os.path.join(self.path, convert_filename(self.cleaned_data['dir_name']))): - raise forms.ValidationError(_(u'The Folder already exists.')) + # only letters, numbers, underscores, + # spaces and hyphens are allowed. + if not alnum_name_re.search(self.cleaned_data['dir_name']): + raise forms.ValidationError( + _('Only letters, numbers, underscores, ' + 'spaces and hyphens are allowed.') + ) + # Folder must not already exist. + if os.path.isdir(os.path.join(self.path, convert_filename(self.cleaned_data['dir_name']))): + raise forms.ValidationError(_('The Folder already exists.')) return convert_filename(self.cleaned_data['dir_name']) @@ -40,24 +51,33 @@ class RenameForm(forms.Form): """ Form for renaming Folder/File. """ - + name = forms.CharField( + widget=forms.TextInput( + attrs={'class': 'vTextField', 'max_length': 50, 'min_length': 3} + ), + label=_('New Name'), + help_text=_('Only letters, numbers, underscores, ' + 'spaces and hyphens are allowed.'), + required=True + ) + def __init__(self, path, file_extension, *args, **kwargs): self.path = path self.file_extension = file_extension super(RenameForm, self).__init__(*args, **kwargs) - name = forms.CharField(widget=forms.TextInput(attrs=dict({ 'class': 'vTextField' }, max_length=50, min_length=3)), label=_(u'New Name'), help_text=_('Only letters, numbers, underscores, spaces and hyphens are allowed.'), required=True) - def clean_name(self): if self.cleaned_data['name']: - # only letters, numbers, underscores, spaces and hyphens are allowed. + # only letters, numbers, underscores, + # spaces and hyphens are allowed. if not alnum_name_re.search(self.cleaned_data['name']): - raise forms.ValidationError(_(u'Only letters, numbers, underscores, spaces and hyphens are allowed.')) + raise forms.ValidationError( + _('Only letters, numbers, underscores, ' + 'spaces and hyphens are allowed.') + ) # folder/file must not already exist. if os.path.isdir(os.path.join(self.path, convert_filename(self.cleaned_data['name']))): - raise forms.ValidationError(_(u'The Folder already exists.')) + raise forms.ValidationError(_('The Folder already exists.')) elif os.path.isfile(os.path.join(self.path, convert_filename(self.cleaned_data['name']) + self.file_extension)): - raise forms.ValidationError(_(u'The File already exists.')) + raise forms.ValidationError(_('The File already exists.')) return convert_filename(self.cleaned_data['name']) - - diff --git a/filebrowser/functions.py b/filebrowser/functions.py index 5a810c0..a38ca80 100755 --- a/filebrowser/functions.py +++ b/filebrowser/functions.py @@ -1,42 +1,38 @@ # coding: utf-8 # imports -import os, re, decimal, itertools -from time import gmtime, strftime, localtime, mktime, time -# from urllib import parse +import os +import re +import itertools +from time import gmtime, strftime, localtime, time +from PIL import Image, ImageFile # django imports -from django.utils.translation import ugettext as _ -from django.utils.safestring import mark_safe -from django.core.files import File from django.core.files.storage import default_storage from django.utils.encoding import smart_str # filebrowser imports -from filebrowser.settings import * +from filebrowser.settings import ( + ADMIN_VERSIONS, VERSIONS_BASEDIR, DEBUG, URL_FILEBROWSER_MEDIA, + PATH_FILEBROWSER_MEDIA, URL_TINYMCE, PATH_TINYMCE, EXTENSIONS, + SELECT_FORMATS, VERSIONS, ADMIN_THUMBNAIL, PREVIEW_VERSION, + MAX_UPLOAD_SIZE, CONVERT_FILENAME, STRICT_PIL, IMAGE_MAXBLOCK +) from filebrowser.conf import fb_settings import sys _ver = sys.version_info -# PIL import -if STRICT_PIL: - from PIL import Image -else: - try: - from PIL import Image - except ImportError: - import Image - def url_to_path(value): """ Change URL to PATH. - Value has to be an URL relative to MEDIA URL or a full URL (including MEDIA_URL). - + Value has to be an URL relative to MEDIA URL or a full URL + (including MEDIA_URL). + Returns a PATH relative to MEDIA_ROOT. """ - + mediaurl_re = re.compile(r'^({0})'.format(fb_settings.MEDIA_URL)) value = mediaurl_re.sub('', value) return value @@ -49,7 +45,7 @@ def path_to_url(value): Return an URL relative to MEDIA_ROOT. """ - + mediaroot_re = re.compile(r'^({0})'.format(fb_settings.MEDIA_ROOT)) value = mediaroot_re.sub('', value) return url_join(fb_settings.MEDIA_URL, value) @@ -61,7 +57,7 @@ def dir_from_url(value): URL has to be an absolute URL including MEDIA_URL or an URL relative to MEDIA_URL. """ - + mediaurl_re = re.compile(r'^({0})'.format(fb_settings.MEDIA_URL)) value = mediaurl_re.sub('', value) directory_re = re.compile(r'^({0})'.format(fb_settings.DIRECTORY)) @@ -77,28 +73,32 @@ def get_version_path(value, version_prefix): version_filename = filename + version_prefix + ext Returns a path relative to MEDIA_ROOT. """ - + if os.path.isfile(smart_str(os.path.join(fb_settings.MEDIA_ROOT, value))): path, filename = os.path.split(value) filename, ext = os.path.splitext(filename) # check if this file is a version of an other file - # to return filename_.ext instead of filename__.ext + # to return filename_.ext instead of + # filename__.ext tmp = filename.split("_") if tmp[len(tmp)-1] in ADMIN_VERSIONS: - # it seems like the "original" is actually a version of an other original + # it seems like the "original" + # is actually a version of an other original # so we strip the suffix (aka. version_perfix) new_filename = filename.replace("_" + tmp[len(tmp)-1], "") # check if the version exists when we use the new_filename if os.path.isfile(smart_str(os.path.join(fb_settings.MEDIA_ROOT, path, new_filename + "_" + version_prefix + ext))): - # our "original" filename seem to be filename_ construct + # our "original" filename seem to be + # filename_ construct # so we replace it with the new_filename filename = new_filename - # if a VERSIONS_BASEDIR is set we need to strip it from the path - # or we get a //... construct + # if a VERSIONS_BASEDIR is set we need to strip it + # from the path or we get + # a //... construct if VERSIONS_BASEDIR != "": path = path.replace(VERSIONS_BASEDIR + "/", "") - + version_filename = filename + "_" + version_prefix + ext return os.path.join(VERSIONS_BASEDIR, path, version_filename) else: @@ -108,25 +108,34 @@ def get_version_path(value, version_prefix): def sort_by_attr(seq, attr): """ Sort the sequence of objects by object's attribute - + Arguments: - seq - the list or any sequence (including immutable one) of objects to sort. + seq - the list or any sequence (including immutable one) + of objects to sort. attr - the name of attribute to sort by - + Returns: the sorted list of objects. """ import operator - + # Use the "Schwartzian transform" # Create the auxiliary list of tuples where every i-th tuple has form - # (seq[i].attr, i, seq[i]) and sort it. The second item of tuple is needed not - # only to provide stable sorting, but mainly to eliminate comparison of objects + # (seq[i].attr, i, seq[i]) and sort it. + # The second item of tuple is needed not only to provide stable sorting, + # but mainly to eliminate comparison of objects # (which can be expensive or prohibited) in case of equal attribute values. if _ver >= (3, 0): - intermed = map(None, map(getattr, seq, (attr,)*len(seq)), itertools.zip_longest(range(len(seq)), seq)) - # intermed.sort() + intermed = map( + None, + map( + getattr, + seq, + (attr,)*len(seq) + ), + itertools.zip_longest(range(len(seq)), seq) + ) try: intermed = sorted(intermed) # does this actually DO anything? @@ -135,7 +144,9 @@ def sort_by_attr(seq, attr): except TypeError: return seq else: - intermed = map(None, map(getattr, seq, (attr,)*len(seq)), range(len(seq)), seq) + intermed = map( + None, map(getattr, seq, (attr,)*len(seq)), range(len(seq)), seq + ) intermed.sort() return map(operator.getitem, intermed, (-1,) * len(intermed)) @@ -144,7 +155,7 @@ def url_join(*args): """ URL join routine. """ - + if args[0].startswith("http://"): url = "http://" elif args[0].startswith("https://"): @@ -167,8 +178,10 @@ def get_path(path): """ Get Path. """ - - if path.startswith('.') or os.path.isabs(path) or not os.path.isdir(os.path.join(fb_settings.MEDIA_ROOT, fb_settings.DIRECTORY, path)): + + if path.startswith('.') or \ + os.path.isabs(path) or \ + not os.path.isdir(os.path.join(fb_settings.MEDIA_ROOT, fb_settings.DIRECTORY, path)): return None return path @@ -177,10 +190,18 @@ def get_file(path, filename): """ Get File. """ - - converted_path = smart_str(os.path.join(fb_settings.MEDIA_ROOT, fb_settings.DIRECTORY, path, filename)) - - if not os.path.isfile(converted_path) and not os.path.isdir(converted_path): + + converted_path = smart_str( + os.path.join( + fb_settings.MEDIA_ROOT, + fb_settings.DIRECTORY, + path, + filename + ) + ) + + if not os.path.isfile(converted_path) and \ + not os.path.isdir(converted_path): return None return filename @@ -189,30 +210,38 @@ def get_breadcrumbs(query, path): """ Get breadcrumbs. """ - + breadcrumbs = [] dir_query = "" if path: for item in path.split(os.sep): - dir_query = os.path.join(dir_query,item) - breadcrumbs.append([item,dir_query]) + dir_query = os.path.join(dir_query, item) + breadcrumbs.append([item, dir_query]) return breadcrumbs -def get_filterdate(filterDate, dateTime): +def get_filterdate(filter_date, date_time): """ Get filterdate. """ - + returnvalue = '' - dateYear = strftime("%Y", gmtime(dateTime)) - dateMonth = strftime("%m", gmtime(dateTime)) - dateDay = strftime("%d", gmtime(dateTime)) - if filterDate == 'today' and int(dateYear) == int(localtime()[0]) and int(dateMonth) == int(localtime()[1]) and int(dateDay) == int(localtime()[2]): returnvalue = 'true' - elif filterDate == 'thismonth' and dateTime >= time()-2592000: returnvalue = 'true' - elif filterDate == 'thisyear' and int(dateYear) == int(localtime()[0]): returnvalue = 'true' - elif filterDate == 'past7days' and dateTime >= time()-604800: returnvalue = 'true' - elif filterDate == '': returnvalue = 'true' + date_year = strftime("%Y", gmtime(date_time)) + date_month = strftime("%m", gmtime(date_time)) + date_day = strftime("%d", gmtime(date_time)) + if filter_date == 'today' and \ + int(date_year) == int(localtime()[0]) and \ + int(date_month) == int(localtime()[1]) and \ + int(date_day) == int(localtime()[2]): + returnvalue = 'true' + elif filter_date == 'thismonth' and date_time >= time() - 2592000: + returnvalue = 'true' + elif filter_date == 'thisyear' and int(date_year) == int(localtime()[0]): + returnvalue = 'true' + elif filter_date == 'past7days' and date_time >= time()-604800: + returnvalue = 'true' + elif filter_date == '': + returnvalue = 'true' return returnvalue @@ -220,32 +249,33 @@ def get_settings_var(): """ Get settings variables used for FileBrowser listing. """ - - settings_var = {} - # Main - settings_var['DEBUG'] = DEBUG - settings_var['MEDIA_ROOT'] = fb_settings.MEDIA_ROOT - settings_var['MEDIA_URL'] = fb_settings.MEDIA_URL - settings_var['DIRECTORY'] = fb_settings.DIRECTORY - # FileBrowser - settings_var['URL_FILEBROWSER_MEDIA'] = URL_FILEBROWSER_MEDIA - settings_var['PATH_FILEBROWSER_MEDIA'] = PATH_FILEBROWSER_MEDIA - # TinyMCE - settings_var['URL_TINYMCE'] = URL_TINYMCE - settings_var['PATH_TINYMCE'] = PATH_TINYMCE - # Extensions/Formats (for FileBrowseField) - settings_var['EXTENSIONS'] = EXTENSIONS - settings_var['SELECT_FORMATS'] = SELECT_FORMATS - # Versions - settings_var['VERSIONS_BASEDIR'] = VERSIONS_BASEDIR - settings_var['VERSIONS'] = VERSIONS - settings_var['ADMIN_VERSIONS'] = ADMIN_VERSIONS - settings_var['ADMIN_THUMBNAIL'] = ADMIN_THUMBNAIL - settings_var['PREVIEW_VERSION'] = PREVIEW_VERSION - # FileBrowser Options - settings_var['MAX_UPLOAD_SIZE'] = MAX_UPLOAD_SIZE - # Convert Filenames - settings_var['CONVERT_FILENAME'] = CONVERT_FILENAME + + settings_var = { + # Main + 'DEBUG': DEBUG, + 'MEDIA_ROOT': fb_settings.MEDIA_ROOT, + 'MEDIA_URL': fb_settings.MEDIA_URL, + 'DIRECTORY': fb_settings.DIRECTORY, + # FileBrowser + 'URL_FILEBROWSER_MEDIA': URL_FILEBROWSER_MEDIA, + 'PATH_FILEBROWSER_MEDIA': PATH_FILEBROWSER_MEDIA, + # TinyMCE + 'URL_TINYMCE': URL_TINYMCE, + 'PATH_TINYMCE': PATH_TINYMCE, + # Extensions/Formats (for FileBrowseField) + 'EXTENSIONS': EXTENSIONS, + 'SELECT_FORMATS': SELECT_FORMATS, + # Versions + 'VERSIONS_BASEDIR': VERSIONS_BASEDIR, + 'VERSIONS': VERSIONS, + 'ADMIN_VERSIONS': ADMIN_VERSIONS, + 'ADMIN_THUMBNAIL': ADMIN_THUMBNAIL, + 'PREVIEW_VERSION': PREVIEW_VERSION, + # FileBrowser Options + 'MAX_UPLOAD_SIZE': MAX_UPLOAD_SIZE, + # Convert Filenames + 'CONVERT_FILENAME': CONVERT_FILENAME, + } return settings_var @@ -253,7 +283,7 @@ def handle_file_upload(path, file): """ Handle File Upload. """ - + file_path = os.path.join(path, file.name) uploadedfile = default_storage.save(file_path, file) return uploadedfile @@ -263,10 +293,10 @@ def get_file_type(filename): """ Get file type as defined in EXTENSIONS. """ - + file_extension = os.path.splitext(filename)[1].lower() file_type = '' - for k,v in EXTENSIONS.items(): + for k, v in EXTENSIONS.items(): for extension in v: if file_extension == extension.lower(): file_type = k @@ -277,10 +307,10 @@ def is_selectable(filename, selecttype): """ Get select type as defined in FORMATS. """ - + file_extension = os.path.splitext(filename)[1].lower() select_types = [] - for k,v in SELECT_FORMATS.items(): + for k, v in SELECT_FORMATS.items(): for extension in v: if file_extension == extension.lower(): select_types.append(k) @@ -292,29 +322,30 @@ def version_generator(value, version_prefix, force=None): Generate Version for an Image. value has to be a serverpath relative to MEDIA_ROOT. """ - - # PIL's Error "Suspension not allowed here" work around: - # s. http://mail.python.org/pipermail/image-sig/1999-August/000816.html - if STRICT_PIL: - from PIL import ImageFile - else: - try: - from PIL import ImageFile - except ImportError: - import ImageFile - ImageFile.MAXBLOCK = IMAGE_MAXBLOCK # default is 64k - + ImageFile.MAXBLOCK = IMAGE_MAXBLOCK # default is 64k + try: im = Image.open(smart_str(os.path.join(fb_settings.MEDIA_ROOT, value))) version_path = get_version_path(value, version_prefix) - absolute_version_path = smart_str(os.path.join(fb_settings.MEDIA_ROOT, version_path)) + absolute_version_path = smart_str( + os.path.join(fb_settings.MEDIA_ROOT, version_path) + ) version_dir = os.path.split(absolute_version_path)[0] if not os.path.isdir(version_dir): os.makedirs(version_dir) os.chmod(version_dir, 0o775) - version = scale_and_crop(im, VERSIONS[version_prefix]['width'], VERSIONS[version_prefix]['height'], VERSIONS[version_prefix]['opts']) + version = scale_and_crop( + im, + VERSIONS[version_prefix]['width'], + VERSIONS[version_prefix]['height'], + VERSIONS[version_prefix]['opts'] + ) try: - version.save(absolute_version_path, quality=90, optimize=(os.path.splitext(version_path)[1].lower() != '.gif')) + version.save( + absolute_version_path, + quality=90, + optimize=(os.path.splitext(version_path)[1].lower() != '.gif') + ) except IOError: version.save(absolute_version_path, quality=90) return version_path @@ -327,55 +358,32 @@ def scale_and_crop(im, width, height, opts): Scale and Crop. """ - x, y = [float(v) for v in im.size] + x, y = [float(v) for v in im.size] if width: xr = float(width) else: - xr = float(x*height/y) + xr = float(x * height / y) if height: yr = float(height) else: - yr = float(y*width/x) + yr = float(y * width / x) if 'crop' in opts: - r = max(xr/x, yr/y) + r = max(xr / x, yr / y) else: - r = min(xr/x, yr/y) + r = min(xr / x, yr / y) if r < 1.0 or (r > 1.0 and 'upscale' in opts): - im = im.resize((int(x*r), int(y*r)), resample=Image.ANTIALIAS) + im = im.resize((int(x * r), int(y * r)), resample=Image.ANTIALIAS) if 'crop' in opts: - x, y = [float(v) for v in im.size] - ex, ey = (x-min(x, xr))/2, (y-min(y, yr))/2 + x, y = [float(v) for v in im.size] + ex, ey = (x - min(x, xr)) / 2, (y - min(y, yr)) / 2 if ex or ey: - im = im.crop((int(ex), int(ey), int(x-ex), int(y-ey))) + im = im.crop((int(ex), int(ey), int(x - ex), int(y - ey))) return im - - # if 'crop' in opts: - # if 'top_left' in opts: - # #draw cropping box from upper left corner of image - # box = (0, 0, int(min(x, xr)), int(min(y, yr))) - # im = im.resize((int(x), int(y)), resample=Image.ANTIALIAS).crop(box) - # elif 'top_right' in opts: - # #draw cropping box from upper right corner of image - # box = (int(x-min(x, xr)), 0, int(x), int(min(y, yr))) - # im = im.resize((int(x), int(y)), resample=Image.ANTIALIAS).crop(box) - # elif 'bottom_left' in opts: - # #draw cropping box from lower left corner of image - # box = (0, int(y-min(y, yr)), int(xr), int(y)) - # im = im.resize((int(x), int(y)), resample=Image.ANTIALIAS).crop(box) - # elif 'bottom_right' in opts: - # #draw cropping box from lower right corner of image - # (int(x-min(x, xr)), int(y-min(y, yr)), int(x), int(y)) - # im = im.resize((int(x), int(y)), resample=Image.ANTIALIAS).crop(box) - # else: - # ex, ey = (x-min(x, xr))/2, (y-min(y, yr))/2 - # if ex or ey: - # box = (int(ex), int(ey), int(x-ex), int(y-ey)) - # im = im.resize((int(x), int(y)), resample=Image.ANTIALIAS).crop(box) - # return im - + + scale_and_crop.valid_options = ('crop', 'upscale') @@ -383,8 +391,17 @@ def convert_filename(value): """ Convert Filename. """ - + if CONVERT_FILENAME: return value.replace(" ", "_").lower() else: return value + + +def _template(): + if fb_settings.SUIT_TEMPLATE: + path = 'suit/' + else: + path = 'filebrowser/' + + return path diff --git a/filebrowser/management/commands/version_generator.py b/filebrowser/management/commands/version_generator.py index 9473134..0ba11b7 100755 --- a/filebrowser/management/commands/version_generator.py +++ b/filebrowser/management/commands/version_generator.py @@ -1,26 +1,29 @@ -from django.core.management.base import NoArgsCommand +from django.core.management.base import BaseCommand -class Command(NoArgsCommand): +from filebrowser.settings import EXTENSION_LIST, EXCLUDE, VERSIONS, EXTENSIONS +from filebrowser.conf import fb_settings + +import os +import re + + +class Command(BaseCommand): help = "(Re)Generate versions of Images" def handle_noargs(self, **options): - import os, re - from filebrowser.settings import EXTENSION_LIST, EXCLUDE, VERSIONS, EXTENSIONS - from filebrowser.conf import fb_settings - # Precompile regular expressions filter_re = [] for exp in EXCLUDE: - filter_re.append(re.compile(exp)) - for k,v in VERSIONS.items(): - exp = (r'_{0}.({1})').format(k, '|'.join(EXTENSION_LIST)) + filter_re.append(re.compile(exp)) + for k, v in VERSIONS.items(): + exp = r'_{0}.({1})'.format(k, '|'.join(EXTENSION_LIST)) filter_re.append(re.compile(exp)) path = os.path.join(fb_settings.MEDIA_ROOT, fb_settings.DIRECTORY) # walkt throu the filebrowser directory # for all/new files (except file versions itself and excludes) - for dirpath,dirnames,filenames in os.walk(path): + for dirpath, dirnames, filenames in os.walk(path): for filename in filenames: filtered = False # no "hidden" files (stating with ".") @@ -34,12 +37,13 @@ def handle_noargs(self, **options): continue (tmp, extension) = os.path.splitext(filename) if extension in EXTENSIONS["Image"]: - self.createVersions(os.path.join(dirpath, filename)) - - def createVersions(self, path): + self.create_versions(os.path.join(dirpath, filename)) + + @staticmethod + def create_versions(path): print "generating versions for: ", path from filebrowser.settings import VERSIONS from filebrowser.functions import version_generator for version in VERSIONS: - #print " ", version + # print " ", version version_generator(path, version, True) diff --git a/filebrowser/settings.py b/filebrowser/settings.py index 2ea0fc1..aad4f84 100755 --- a/filebrowser/settings.py +++ b/filebrowser/settings.py @@ -15,7 +15,10 @@ except ImportError: DEFAULT_URL_TINYMCE = getattr(settings, 'ADMIN_MEDIA_PREFIX', os.path.join(settings.STATIC_URL, 'admin/')) + "tinymce/jscripts/tiny_mce/" - DEFAULT_PATH_TINYMCE = os.path.join(settings.MEDIA_ROOT, 'admin/tinymce/jscripts/tiny_mce/') + DEFAULT_PATH_TINYMCE = os.path.join( + settings.MEDIA_ROOT, + 'admin/tinymce/jscripts/tiny_mce/' + ) # Set to True in order to see the FileObject when Browsing. DEBUG = getattr(settings, "FILEBROWSER_DEBUG", False) @@ -26,58 +29,118 @@ # Main FileBrowser Directory. This has to be a directory within MEDIA_ROOT. # Leave empty in order to browse all files under MEDIA_ROOT. -# DO NOT USE A SLASH AT THE BEGINNING, DO NOT FORGET THE TRAILING SLASH AT THE END. +# DO NOT USE A SLASH AT THE BEGINNING, +# DO NOT FORGET THE TRAILING SLASH AT THE END. DIRECTORY = getattr(settings, "FILEBROWSER_DIRECTORY", 'uploads/') # The URL/PATH to your filebrowser media-files. -URL_FILEBROWSER_MEDIA = getattr(settings, "FILEBROWSER_URL_FILEBROWSER_MEDIA", os.path.join(settings.STATIC_URL, 'filebrowser/')) -PATH_FILEBROWSER_MEDIA = getattr(settings, "FILEBROWSER_PATH_FILEBROWSER_MEDIA", os.path.join(settings.STATIC_ROOT, 'filebrowser/')) +URL_FILEBROWSER_MEDIA = getattr( + settings, + "FILEBROWSER_URL_FILEBROWSER_MEDIA", + os.path.join(settings.STATIC_URL, 'filebrowser/') +) +PATH_FILEBROWSER_MEDIA = getattr( + settings, + "FILEBROWSER_PATH_FILEBROWSER_MEDIA", + os.path.join(settings.STATIC_ROOT, 'filebrowser/') +) # The URL/PATH to your TinyMCE Installation. URL_TINYMCE = getattr(settings, "FILEBROWSER_URL_TINYMCE", DEFAULT_URL_TINYMCE) -PATH_TINYMCE = getattr(settings, "FILEBROWSER_PATH_TINYMCE", DEFAULT_PATH_TINYMCE) +PATH_TINYMCE = getattr( + settings, + "FILEBROWSER_PATH_TINYMCE", + DEFAULT_PATH_TINYMCE +) # Allowed Extensions for File Upload. Lower case is important. # Please be aware that there are Icons for the default extension settings. # Therefore, if you add a category (e.g. "Misc"), you won't get an icon. EXTENSIONS = getattr(settings, "FILEBROWSER_EXTENSIONS", { 'Folder': [''], - 'Image': ['.jpg','.jpeg','.gif','.png','.tif','.tiff'], - 'Video': ['.mov','.wmv','.mpeg','.mpg','.avi','.rm'], - 'Document': ['.pdf','.doc','.rtf','.txt','.xls','.csv'], - 'Audio': ['.mp3','.mp4','.wav','.aiff','.midi','.m4p'], - 'Code': ['.html','.py','.js','.css'] + 'Image': ['.jpg', '.jpeg', '.gif', '.png', '.tif', '.tiff'], + 'Video': ['.mov', '.wmv', '.mpeg', '.mpg', '.avi', '.rm'], + 'Document': ['.pdf', '.doc', '.rtf', '.txt', '.xls', '.csv'], + 'Audio': ['.mp3', '.mp4', '.wav', '.aiff', '.midi', '.m4p'], + 'Code': ['.html', '.py', '.js', '.css'] }) # Define different formats for allowed selections. # This has to be a subset of EXTENSIONS. -SELECT_FORMATS = getattr(settings, "FILEBROWSER_SELECT_FORMATS", { - 'File': ['Folder','Document',], - 'Image': ['Image'], - 'Media': ['Video','Sound'], - 'Document': ['Document'], - # for TinyMCE we can also define lower-case items - 'image': ['Image'], - 'file': ['Folder','Image','Document',], - 'media': ['Video','Sound'], -}) +SELECT_FORMATS = getattr( + settings, + "FILEBROWSER_SELECT_FORMATS", + { + 'File': ['Folder', 'Document'], + 'Image': ['Image'], + 'Media': ['Video', 'Sound'], + 'Document': ['Document'], + # for TinyMCE we can also define lower-case items + 'image': ['Image'], + 'file': ['Folder', 'Image', 'Document'], + 'media': ['Video', 'Sound'], + } +) # Directory to Save Image Versions (and Thumbnails). Relative to MEDIA_ROOT. # If no directory is given, versions are stored within the Image directory. # VERSION URL: VERSIONS_BASEDIR/original_path/originalfilename_versionsuffix.extension VERSIONS_BASEDIR = getattr(settings, 'FILEBROWSER_VERSIONS_BASEDIR', '') # Versions Format. Available Attributes: verbose_name, width, height, opts -VERSIONS = getattr(settings, "FILEBROWSER_VERSIONS", { - 'fb_thumb': {'verbose_name': 'Admin Thumbnail', 'width': 60, 'height': 60, 'opts': 'crop upscale'}, - 'thumbnail': {'verbose_name': 'Thumbnail (140px)', 'width': 140, 'height': '', 'opts': ''}, - 'small': {'verbose_name': 'Small (300px)', 'width': 300, 'height': '', 'opts': ''}, - 'medium': {'verbose_name': 'Medium (460px)', 'width': 460, 'height': '', 'opts': ''}, - 'big': {'verbose_name': 'Big (620px)', 'width': 620, 'height': '', 'opts': ''}, - 'cropped': {'verbose_name': 'Cropped (60x60px)', 'width': 60, 'height': 60, 'opts': 'crop'}, - 'croppedthumbnail': {'verbose_name': 'Cropped Thumbnail (140x140px)', 'width': 140, 'height': 140, 'opts': 'crop'}, -}) +VERSIONS = getattr( + settings, + "FILEBROWSER_VERSIONS", + { + 'fb_thumb': { + 'verbose_name': 'Admin Thumbnail', + 'width': 60, + 'height': 60, + 'opts': 'crop upscale' + }, + 'thumbnail': { + 'verbose_name': 'Thumbnail (140px)', + 'width': 140, + 'height': '', + 'opts': '' + }, + 'small': { + 'verbose_name': 'Small (300px)', + 'width': 300, + 'height': '', + 'opts': '' + }, + 'medium': { + 'verbose_name': 'Medium (460px)', + 'width': 460, + 'height': '', + 'opts': '' + }, + 'big': { + 'verbose_name': 'Big (620px)', + 'width': 620, + 'height': '', + 'opts': '' + }, + 'cropped': { + 'verbose_name': 'Cropped (60x60px)', + 'width': 60, + 'height': 60, + 'opts': 'crop' + }, + 'croppedthumbnail': { + 'verbose_name': 'Cropped Thumbnail (140x140px)', + 'width': 140, + 'height': 140, + 'opts': 'crop' + }, + } +) # Versions available within the Admin-Interface. -ADMIN_VERSIONS = getattr(settings, 'FILEBROWSER_ADMIN_VERSIONS', ['thumbnail','small', 'medium','big']) +ADMIN_VERSIONS = getattr( + settings, + 'FILEBROWSER_ADMIN_VERSIONS', + ['thumbnail', 'small', 'medium', 'big'] +) # Which Version should be used as Admin-thumbnail. ADMIN_THUMBNAIL = getattr(settings, 'FILEBROWSER_ADMIN_THUMBNAIL', 'fb_thumb') # Preview Version @@ -98,7 +161,11 @@ EXTENSION_LIST = [] for exts in EXTENSIONS.values(): EXTENSION_LIST += exts -EXCLUDE = getattr(settings, 'FILEBROWSER_EXCLUDE', (r'_(%(exts)s)_.*_q\d{1,3}\.(%(exts)s)' % {'exts': ('|'.join(EXTENSION_LIST))},)) +EXCLUDE = getattr( + settings, + 'FILEBROWSER_EXCLUDE', + (r'_(%(exts)s)_.*_q\d{1,3}\.(%(exts)s)' % {'exts': ('|'.join(EXTENSION_LIST))},) +) # Max. Upload Size in Bytes. MAX_UPLOAD_SIZE = getattr(settings, "FILEBROWSER_MAX_UPLOAD_SIZE", 10485760) # Convert Filename (replace spaces and convert to lowercase) @@ -109,14 +176,26 @@ LIST_PER_PAGE = getattr(settings, "FILEBROWSER_LIST_PER_PAGE", 50) # Default Sorting # Options: date, filesize, filename_lower, filetype_checked -DEFAULT_SORTING_BY = getattr(settings, "FILEBROWSER_DEFAULT_SORTING_BY", "date") +DEFAULT_SORTING_BY = getattr( + settings, + "FILEBROWSER_DEFAULT_SORTING_BY", + "date" +) # Sorting Order: asc, desc -DEFAULT_SORTING_ORDER = getattr(settings, "FILEBROWSER_DEFAULT_SORTING_ORDER", "desc") +DEFAULT_SORTING_ORDER = getattr( + settings, + "FILEBROWSER_DEFAULT_SORTING_ORDER", + "desc" +) # regex to clean dir names before creation (no use!) # SECURITY re for test name new dirs or file name # FILE_AND_DIRS_NAME_REGEXP -FOLDER_REGEX = getattr(settings, "FILEBROWSER_FOLDER_REGEX", r'^[ \w-][ \w.-]*$') +FOLDER_REGEX = getattr( + settings, + "FILEBROWSER_FOLDER_REGEX", + r'^(?u)[ \w-][ \w.-]*$' +) # EXTRA TRANSLATION STRINGS # The following strings are not availabe within views or templates @@ -127,7 +206,11 @@ _('Audio') _('Code') -#Suit Template +# Suit Template SUIT_TEMPLATE = getattr(settings, "FILEBROWSER_SUIT_TEMPLATE", None) - - +# Show menu items at admin panel +SHOW_AT_ADMIN_PANEL = getattr( + settings, + "FILEBROWSER_SHOW_AT_ADMIN_PANEL", + False +) diff --git a/filebrowser/static/admin/js/inlines.js b/filebrowser/static/admin/js/inlines.js index 27dedf4..cceca06 100644 --- a/filebrowser/static/admin/js/inlines.js +++ b/filebrowser/static/admin/js/inlines.js @@ -1,3 +1,4 @@ +/*global DateTimeShortcuts, SelectFilter*/ /** * Django admin inlines * @@ -13,269 +14,283 @@ * * Licensed under the New BSD License * See: http://www.opensource.org/licenses/bsd-license.php - * - * Modified for django-media-manager by Yury Lapshinov (raagin) - * */ (function($) { - $.fn.formset = function(opts) { - var options = $.extend({}, $.fn.formset.defaults, opts); - var $this = $(this); - var $parent = $this.parent(); - var updateElementIndex = function(el, prefix, ndx) { - var id_regex = new RegExp("(" + prefix + "-(\\d+|__prefix__))"); - var replacement = prefix + "-" + ndx; - if ($(el).prop("for")) { - $(el).prop("for", $(el).prop("for").replace(id_regex, replacement)); - } - if (el.id) { - el.id = el.id.replace(id_regex, replacement); - } - if (el.name) { - el.name = el.name.replace(id_regex, replacement); - } - if ($(el).has('input[class="vFileBrowseField"]')) { - var a_fb_link = $('input.vFileBrowseField', el).next(); - if (a_fb_link.attr('href')) { - a_fb_link.attr('href', a_fb_link.attr('href').replace(id_regex, replacement)); - } - } - }; - var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS").prop("autocomplete", "off"); - var nextIndex = parseInt(totalForms.val(), 10); - var maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS").prop("autocomplete", "off"); - // only show the add button if we are allowed to add more items, + 'use strict'; + $.fn.formset = function(opts) { + var options = $.extend({}, $.fn.formset.defaults, opts); + var $this = $(this); + var $parent = $this.parent(); + var updateElementIndex = function(el, prefix, ndx) { + var id_regex = new RegExp("(" + prefix + "-(\\d+|__prefix__))"); + var replacement = prefix + "-" + ndx; + if ($(el).prop("for")) { + $(el).prop("for", $(el).prop("for").replace(id_regex, replacement)); + } + if (el.id) { + el.id = el.id.replace(id_regex, replacement); + } + if (el.name) { + el.name = el.name.replace(id_regex, replacement); + } + if ($(el).has('input[class="vFileBrowseField"]')) { + var a_fb_link = $('input.vFileBrowseField', el).next(); + if (a_fb_link.attr('href')) { + a_fb_link.attr('href', a_fb_link.attr('href').replace(id_regex, replacement)); + } + } + }; + var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS").prop("autocomplete", "off"); + var nextIndex = parseInt(totalForms.val(), 10); + var maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS").prop("autocomplete", "off"); + // only show the add button if we are allowed to add more items, // note that max_num = None translates to a blank string. - var showAddButton = maxForms.val() === '' || (maxForms.val()-totalForms.val()) > 0; - $this.each(function(i) { - $(this).not("." + options.emptyCssClass).addClass(options.formCssClass); - }); - if ($this.length && showAddButton) { - var addButton; - if ($this.prop("tagName") == "TR") { - // If forms are laid out as table rows, insert the - // "add" button in a new table row: - var numCols = this.eq(-1).children().length; - $parent.append('' + options.addText + ""); - addButton = $parent.find("tr:last a"); - } else { - // Otherwise, insert it immediately after the last form: - $this.filter(":last").after('"); - addButton = $this.filter(":last").next().find("a"); - } - addButton.click(function(e) { - e.preventDefault(); - var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS"); - var template = $("#" + options.prefix + "-empty"); - var row = template.clone(true); - row.removeClass(options.emptyCssClass) - .addClass(options.formCssClass) - .attr("id", options.prefix + "-" + nextIndex); - if (row.is("tr")) { - // If the forms are laid out in table rows, insert - // the remove button into the last table cell: - row.children(":last").append('"); - } else if (row.is("ul") || row.is("ol")) { - // If they're laid out as an ordered/unordered list, - // insert an
  • after the last list item: - row.append('
  • ' + options.deleteText + "
  • "); - } else { - // Otherwise, just insert the remove button as the - // last child element of the form's container: - row.children(":first").append('' + options.deleteText + ""); - } - row.find("*").each(function() { - updateElementIndex(this, options.prefix, totalForms.val()); + var showAddButton = maxForms.val() === '' || (maxForms.val() - totalForms.val()) > 0; + $this.each(function(i) { + $(this).not("." + options.emptyCssClass).addClass(options.formCssClass); }); - // Insert the new form when it has been fully edited - row.insertBefore($(template)); - // Update number of total forms - $(totalForms).val(parseInt(totalForms.val(), 10) + 1); - nextIndex += 1; - // Hide add button in case we've hit the max, except we want to add infinitely - if ((maxForms.val() !== '') && (maxForms.val()-totalForms.val()) <= 0) { - addButton.parent().hide(); - } - // The delete button of each row triggers a bunch of other things - row.find("a." + options.deleteCssClass).click(function(e) { - e.preventDefault(); - // Remove the parent form containing this button: - var row = $(this).parents("." + options.formCssClass); - row.remove(); - nextIndex -= 1; - // If a post-delete callback was provided, call it with the deleted form: - if (options.removed) { - options.removed(row); - } - // Update the TOTAL_FORMS form count. - var forms = $("." + options.formCssClass); - $("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length); - // Show add button again once we drop below max - if ((maxForms.val() === '') || (maxForms.val()-forms.length) > 0) { - addButton.parent().show(); - } - // Also, update names and ids for all remaining form controls - // so they remain in sequence: - for (var i=0, formCount=forms.length; i' + options.addText + ""); + addButton = $parent.find("tr:last a"); + } else { + // Otherwise, insert it immediately after the last form: + $this.filter(":last").after('"); + addButton = $this.filter(":last").next().find("a"); + } + addButton.click(function(e) { + e.preventDefault(); + var template = $("#" + options.prefix + "-empty"); + var row = template.clone(true); + row.removeClass(options.emptyCssClass) + .addClass(options.formCssClass) + .attr("id", options.prefix + "-" + nextIndex); + if (row.is("tr")) { + // If the forms are laid out in table rows, insert + // the remove button into the last table cell: + row.children(":last").append('"); + } else if (row.is("ul") || row.is("ol")) { + // If they're laid out as an ordered/unordered list, + // insert an
  • after the last list item: + row.append('
  • ' + options.deleteText + "
  • "); + } else { + // Otherwise, just insert the remove button as the + // last child element of the form's container: + row.children(":first").append('' + options.deleteText + ""); + } + row.find("*").each(function() { + updateElementIndex(this, options.prefix, totalForms.val()); + }); + // Insert the new form when it has been fully edited + row.insertBefore($(template)); + // Update number of total forms + $(totalForms).val(parseInt(totalForms.val(), 10) + 1); + nextIndex += 1; + // Hide add button in case we've hit the max, except we want to add infinitely + if ((maxForms.val() !== '') && (maxForms.val() - totalForms.val()) <= 0) { + addButton.parent().hide(); + } + // The delete button of each row triggers a bunch of other things + row.find("a." + options.deleteCssClass).click(function(e1) { + e1.preventDefault(); + // Remove the parent form containing this button: + row.remove(); + nextIndex -= 1; + // If a post-delete callback was provided, call it with the deleted form: + if (options.removed) { + options.removed(row); + } + $(document).trigger('formset:removed', [row, options.prefix]); + // Update the TOTAL_FORMS form count. + var forms = $("." + options.formCssClass); + $("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length); + // Show add button again once we drop below max + if ((maxForms.val() === '') || (maxForms.val() - forms.length) > 0) { + addButton.parent().show(); + } + // Also, update names and ids for all remaining form controls + // so they remain in sequence: + var i, formCount; + var updateElementCallback = function() { + updateElementIndex(this, options.prefix, i); + }; + for (i = 0, formCount = forms.length; i < formCount; i++) { + updateElementIndex($(forms).get(i), options.prefix, i); + $(forms.get(i)).find("*").each(updateElementCallback); + } + }); + // If a post-add callback was supplied, call it with the added form: + if (options.added) { + options.added(row); + } + $(document).trigger('formset:added', [row, options.prefix]); }); - } - }); - // If a post-add callback was supplied, call it with the added form: - if (options.added) { - options.added(row); } - }); - } - return this; - }; + return this; + }; - /* Setup plugin defaults */ - $.fn.formset.defaults = { - prefix: "form", // The form prefix for your django formset - addText: "add another", // Text for the add link - deleteText: "remove", // Text for the delete link - addCssClass: "add-row", // CSS class applied to the add link - deleteCssClass: "delete-row", // CSS class applied to the delete link - emptyCssClass: "empty-row", // CSS class applied to the empty row - formCssClass: "dynamic-form", // CSS class applied to each form in a formset - added: null, // Function called each time a new form is added - removed: null // Function called each time a form is deleted - }; + /* Setup plugin defaults */ + $.fn.formset.defaults = { + prefix: "form", // The form prefix for your django formset + addText: "add another", // Text for the add link + deleteText: "remove", // Text for the delete link + addCssClass: "add-row", // CSS class applied to the add link + deleteCssClass: "delete-row", // CSS class applied to the delete link + emptyCssClass: "empty-row", // CSS class applied to the empty row + formCssClass: "dynamic-form", // CSS class applied to each form in a formset + added: null, // Function called each time a new form is added + removed: null // Function called each time a form is deleted + }; - // Tabular inlines --------------------------------------------------------- - $.fn.tabularFormset = function(options) { - var $rows = $(this); - var alternatingRows = function(row) { - $($rows.selector).not(".add-row").removeClass("row1 row2") - .filter(":even").addClass("row1").end() - .filter(":odd").addClass("row2"); - }; + // Tabular inlines --------------------------------------------------------- + $.fn.tabularFormset = function(options) { + var $rows = $(this); + var alternatingRows = function(row) { + $($rows.selector).not(".add-row").removeClass("row1 row2") + .filter(":even").addClass("row1").end() + .filter(":odd").addClass("row2"); + }; - var reinitDateTimeShortCuts = function() { - // Reinitialize the calendar and clock widgets by force - if (typeof DateTimeShortcuts != "undefined") { - $(".datetimeshortcuts").remove(); - DateTimeShortcuts.init(); - } - }; + var reinitDateTimeShortCuts = function() { + // Reinitialize the calendar and clock widgets by force + if (typeof DateTimeShortcuts !== "undefined") { + $(".datetimeshortcuts").remove(); + DateTimeShortcuts.init(); + } + }; - var updateSelectFilter = function() { - // If any SelectFilter widgets are a part of the new form, - // instantiate a new SelectFilter instance for it. - if (typeof SelectFilter != 'undefined'){ - $('.selectfilter').each(function(index, value){ - var namearr = value.name.split('-'); - SelectFilter.init(value.id, namearr[namearr.length-1], false, options.adminStaticPrefix ); - }); - $('.selectfilterstacked').each(function(index, value){ - var namearr = value.name.split('-'); - SelectFilter.init(value.id, namearr[namearr.length-1], true, options.adminStaticPrefix ); - }); - } - }; + var updateSelectFilter = function() { + // If any SelectFilter widgets are a part of the new form, + // instantiate a new SelectFilter instance for it. + if (typeof SelectFilter !== 'undefined') { + $('.selectfilter').each(function(index, value) { + var namearr = value.name.split('-'); + SelectFilter.init(value.id, namearr[namearr.length - 1], false); + }); + $('.selectfilterstacked').each(function(index, value) { + var namearr = value.name.split('-'); + SelectFilter.init(value.id, namearr[namearr.length - 1], true); + }); + } + }; - var initPrepopulatedFields = function(row) { - row.find('.prepopulated_field').each(function() { - var field = $(this), - input = field.find('input, select, textarea'), - dependency_list = input.data('dependency_list') || [], - dependencies = []; - $.each(dependency_list, function(i, field_name) { - dependencies.push('#' + row.find('.field-' + field_name).find('input, select, textarea').attr('id')); + var initPrepopulatedFields = function(row) { + row.find('.prepopulated_field').each(function() { + var field = $(this), + input = field.find('input, select, textarea'), + dependency_list = input.data('dependency_list') || [], + dependencies = []; + $.each(dependency_list, function(i, field_name) { + dependencies.push('#' + row.find('.field-' + field_name).find('input, select, textarea').attr('id')); + }); + if (dependencies.length) { + input.prepopulate(dependencies, input.attr('maxlength')); + } + }); + }; + + $rows.formset({ + prefix: options.prefix, + addText: options.addText, + formCssClass: "dynamic-" + options.prefix, + deleteCssClass: "inline-deletelink", + deleteText: options.deleteText, + emptyCssClass: "empty-form", + removed: alternatingRows, + added: function(row) { + initPrepopulatedFields(row); + reinitDateTimeShortCuts(); + updateSelectFilter(); + alternatingRows(row); + } }); - if (dependencies.length) { - input.prepopulate(dependencies, input.attr('maxlength')); - } - }); + + return $rows; }; - $rows.formset({ - prefix: options.prefix, - addText: options.addText, - formCssClass: "dynamic-" + options.prefix, - deleteCssClass: "inline-deletelink", - deleteText: options.deleteText, - emptyCssClass: "empty-form", - removed: alternatingRows, - added: function(row) { - initPrepopulatedFields(row); - reinitDateTimeShortCuts(); - updateSelectFilter(); - alternatingRows(row); - } - }); + // Stacked inlines --------------------------------------------------------- + $.fn.stackedFormset = function(options) { + var $rows = $(this); + var updateInlineLabel = function(row) { + $($rows.selector).find(".inline_label").each(function(i) { + var count = i + 1; + $(this).html($(this).html().replace(/(#\d+)/g, "#" + count)); + }); + }; - return $rows; - }; + var reinitDateTimeShortCuts = function() { + // Reinitialize the calendar and clock widgets by force, yuck. + if (typeof DateTimeShortcuts !== "undefined") { + $(".datetimeshortcuts").remove(); + DateTimeShortcuts.init(); + } + }; - // Stacked inlines --------------------------------------------------------- - $.fn.stackedFormset = function(options) { - var $rows = $(this); - var updateInlineLabel = function(row) { - $($rows.selector).find(".inline_label").each(function(i) { - var count = i + 1; - $(this).html($(this).html().replace(/(#\d+)/g, "#" + count)); - }); - }; + var updateSelectFilter = function() { + // If any SelectFilter widgets were added, instantiate a new instance. + if (typeof SelectFilter !== "undefined") { + $(".selectfilter").each(function(index, value) { + var namearr = value.name.split('-'); + SelectFilter.init(value.id, namearr[namearr.length - 1], false); + }); + $(".selectfilterstacked").each(function(index, value) { + var namearr = value.name.split('-'); + SelectFilter.init(value.id, namearr[namearr.length - 1], true); + }); + } + }; - var reinitDateTimeShortCuts = function() { - // Reinitialize the calendar and clock widgets by force, yuck. - if (typeof DateTimeShortcuts != "undefined") { - $(".datetimeshortcuts").remove(); - DateTimeShortcuts.init(); - } - }; + var initPrepopulatedFields = function(row) { + row.find('.prepopulated_field').each(function() { + var field = $(this), + input = field.find('input, select, textarea'), + dependency_list = input.data('dependency_list') || [], + dependencies = []; + $.each(dependency_list, function(i, field_name) { + dependencies.push('#' + row.find('.form-row .field-' + field_name).find('input, select, textarea').attr('id')); + }); + if (dependencies.length) { + input.prepopulate(dependencies, input.attr('maxlength')); + } + }); + }; - var updateSelectFilter = function() { - // If any SelectFilter widgets were added, instantiate a new instance. - if (typeof SelectFilter != "undefined"){ - $(".selectfilter").each(function(index, value){ - var namearr = value.name.split('-'); - SelectFilter.init(value.id, namearr[namearr.length-1], false, options.adminStaticPrefix); - }); - $(".selectfilterstacked").each(function(index, value){ - var namearr = value.name.split('-'); - SelectFilter.init(value.id, namearr[namearr.length-1], true, options.adminStaticPrefix); + $rows.formset({ + prefix: options.prefix, + addText: options.addText, + formCssClass: "dynamic-" + options.prefix, + deleteCssClass: "inline-deletelink", + deleteText: options.deleteText, + emptyCssClass: "empty-form", + removed: updateInlineLabel, + added: function(row) { + initPrepopulatedFields(row); + reinitDateTimeShortCuts(); + updateSelectFilter(); + updateInlineLabel(row); + } }); - } - }; - var initPrepopulatedFields = function(row) { - row.find('.prepopulated_field').each(function() { - var field = $(this), - input = field.find('input, select, textarea'), - dependency_list = input.data('dependency_list') || [], - dependencies = []; - $.each(dependency_list, function(i, field_name) { - dependencies.push('#' + row.find('.form-row .field-' + field_name).find('input, select, textarea').attr('id')); - }); - if (dependencies.length) { - input.prepopulate(dependencies, input.attr('maxlength')); - } - }); + return $rows; }; - $rows.formset({ - prefix: options.prefix, - addText: options.addText, - formCssClass: "dynamic-" + options.prefix, - deleteCssClass: "inline-deletelink", - deleteText: options.deleteText, - emptyCssClass: "empty-form", - removed: updateInlineLabel, - added: (function(row) { - initPrepopulatedFields(row); - reinitDateTimeShortCuts(); - updateSelectFilter(); - updateInlineLabel(row); - }) + $(document).ready(function() { + $(".js-inline-admin-formset").each(function() { + var data = $(this).data(), + inlineOptions = data.inlineFormset; + switch(data.inlineType) { + case "stacked": + $(inlineOptions.name + "-group .inline-related").stackedFormset(inlineOptions.options); + break; + case "tabular": + $(inlineOptions.name + "-group .tabular.inline-related tbody tr").tabularFormset(inlineOptions.options); + break; + } + }); }); - - return $rows; - }; })(django.jQuery); diff --git a/filebrowser/static/filebrowser/js/FB_CKEditor.js b/filebrowser/static/filebrowser/js/FB_CKEditor.js index 95207b3..819cbc4 100755 --- a/filebrowser/static/filebrowser/js/FB_CKEditor.js +++ b/filebrowser/static/filebrowser/js/FB_CKEditor.js @@ -5,10 +5,12 @@ function ProtectPath(path) { } function gup( name ) { + var url = encodeURI(window.location.href); + url = url.replace(/&/g, '&'); name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]"); var regexS = "[\\?&]"+name+"=([^&#]*)"; var regex = new RegExp(regexS); - var results = regex.exec(window.location.href); + var results = regex.exec(url); if(results == null) return ""; else diff --git a/filebrowser/templates/filebrowser/index.html b/filebrowser/templates/filebrowser/index.html index 6da676f..066b8b4 100755 --- a/filebrowser/templates/filebrowser/index.html +++ b/filebrowser/templates/filebrowser/index.html @@ -25,7 +25,7 @@ {% endifequal %} {{ media }} - + {% if not actions_on_top and not actions_on_bottom %}