diff --git a/README.md b/README.md index 267aa5b..0f57a1f 100644 --- a/README.md +++ b/README.md @@ -40,28 +40,21 @@ As a filemanager In urls.py of your app to make filemanager run at url /abc/
-from filemanager import path_end
-urlpatterns = patterns('app',
-   .
-   .
-   (r'^abc/'+path_end,'view'),
+from filemanager.views import FileManager, path_end
+
+urlpatterns = [
+    .
+    .
+    url(r'^abc/', include('filemanager.urls')),
 )
 
-And then write the view in views.py of your app -
-from filemanager import FileManager
-from settings import MEDIA_ROOT
-def view(request,path):
-  fm = FileManager(MEDIA_ROOT+'user_folder/')
-  return fm.render(request,path)
-
And it is done. +By default this requires a `staff` user to access. + Adding constraints to Filemanager : -FileManager \__init__ is defined as
-def __init__(self,basepath,ckeditor_baseurl='',maxfolders=50,maxspace=5*1024,maxfilesize=1*1024,public_url_base=None,extensions=None):
    """
    basepath: User's directory basepath in server.
    maxfolders: Maximum number of total nested folders allowed inside the user directory.
@@ -71,9 +64,20 @@ def __init__(self,basepath,ckeditor_baseurl='',maxfolders=50,maxspace=5*1024,max
    public_base_url: A base_url if given there will be an option to copy file url with the given url_base.
    """
 
+ Hence one should also pass arguments like maxfolders, maxspace, maxfilesize if one doesn't want to use the default ones. If extensions list is not passed then all file-extensions are allowed for upload. +
+from filemanager.views import FileManager
+
+urlpatterns = [
+    url(r'^filemanager/$', FileManager.as_view(basepath=settings.MEDIA_ROOT, maxspace=400*1024*1024)),
+]
+
+ +WARNING: The above will have NO permission checks, allowing anyone who can reach that URL free access! + Integrating with CKEditor ------------------------- diff --git a/filemanager/__init__.py b/filemanager/__init__.py index 700f070..e69de29 100644 --- a/filemanager/__init__.py +++ b/filemanager/__init__.py @@ -1,305 +0,0 @@ -from django.contrib.auth.decorators import login_required -from django.core.urlresolvers import reverse -from django.shortcuts import render -from django.http import HttpResponseRedirect, HttpResponse -from django.template import RequestContext -from django.core.servers.basehttp import FileWrapper -from django import forms -from PIL import Image -import settings -import mimetypes -import os -import shutil -import re -import tarfile - -path_end = r'(?P[\w\d_ -/.]*)$' - -ActionChoices = ( - ('upload','upload'), - ('rename','rename'), - ('delete','delete'), - ('add','add'), - ('move','move'), - ('copy','copy'), - ) - -class FileManagerForm(forms.Form): - ufile = forms.FileField(required=False) - action = forms.ChoiceField(choices=ActionChoices) - path = forms.CharField(max_length=200,required=False) - name = forms.CharField(max_length=32,required=False) - current_path = forms.CharField(max_length=200,required=False) - file_or_dir = forms.CharField(max_length=4) - -class FileManager(object): - """ - maxspace,maxfilesize in KB - """ - idee = 0 - def __init__(self,basepath,ckeditor_baseurl='',maxfolders=50,maxspace=5*1024,maxfilesize=1*1024,public_url_base=None,extensions=None): - if basepath[-1] == '/': - basepath = basepath[:-1] - if ckeditor_baseurl and ckeditor_baseurl[-1] == '/': - ckeditor_baseurl = ckeditor_baseurl[:-1] - self.basepath = basepath - self.ckeditor_baseurl = ckeditor_baseurl - self.maxfolders = maxfolders - self.maxspace = maxspace - self.maxfilesize = maxfilesize - self.extensions = extensions; - self.public_url_base = public_url_base - - def rename_if_exists(self,folder,file): - if folder[-1] != os.sep: - folder = folder + os.sep - if os.path.exists(folder+file): - if file.find('.') == -1: - # no extension - for i in range(1000): - if not os.path.exists(folder+file+'.'+str(i)): - break - return file+'.'+str(i) - else: - extension = file[file.rfind('.'):] - name = file[:file.rfind('.')] - for i in range(1000): - if not os.path.exists(folder+name+'.'+str(i)+extension): - break - return name + '.' + str(i) + extension - else: - return file - - def get_size(self,start_path): - total_size = 0 - for dirpath, dirnames, filenames in os.walk(start_path): - for f in filenames: - fp = os.path.join(dirpath, f) - total_size += os.path.getsize(fp) - return total_size - - def next_id(self): - self.idee = self.idee+1 - return self.idee - - def handle_form(self,form,files): - action = form.cleaned_data['action'] - path = form.cleaned_data['path'] - name = form.cleaned_data['name'] - ufile = form.cleaned_data['ufile'] - file_or_dir = form.cleaned_data['file_or_dir'] - self.current_path = form.cleaned_data['current_path'] - messages = [] - if name and file_or_dir == 'dir' and not re.match(r'[\w\d_ -]+',name).group(0) == name: - messages.append("Invalid folder name : "+name) - return messages - if name and file_or_dir == 'file' and (re.search('\.\.',name) or not re.match(r'[\w\d_ -.]+',name).group(0) == name): - messages.append("Invalid file name : "+name) - return messages - if not re.match(r'[\w\d_ -/]+',path).group(0) == path: - messages.append("Invalid path : "+path) - return messages - if action == 'upload': - for f in files.getlist('ufile'): - if re.search('\.\.',f.name) or not re.match('[\w\d_ -/.]+',f.name).group(0) == f.name: - messages.append("File name is not valid : "+f.name) - elif f.size > self.maxfilesize*1024: - messages.append("File size exceeded "+str(self.maxfilesize)+" KB : "+f.name) - elif (settings.FILEMANAGER_CHECK_SPACE and - ((self.get_size(self.basepath)+f.size) > self.maxspace*1024)): - messages.append("Total Space size exceeded "+str(self.maxspace)+" KB : "+f.name) - elif self.extensions and len(f.name.split('.'))>1 and f.name.split('.')[-1] not in self.extensions: - messages.append("File extension not allowed (."+f.name.split('.')[-1]+") : "+f.name) - elif self.extensions and len(f.name.split('.'))==1 and f.name.split('.')[-1] not in self.extensions: - messages.append("No file extension in uploaded file : "+f.name) - else: - filepath = self.basepath+path+self.rename_if_exists(self.basepath+path,f.name) - with open(filepath,'w') as dest: - for chunk in f.chunks(): - dest.write(chunk) - f.close() - if len(messages) == 0: - messages.append('All files uploaded successfully') - elif action == 'add': - os.chdir(self.basepath) - no_of_folders = len(list(os.walk('.'))) - if (no_of_folders + 1) <= self.maxfolders: - try: - os.chdir(self.basepath+path) - os.mkdir(name) - messages.append('Folder created successfully : '+name) - except: - messages.append('Folder couldn\'t be created : '+name) - else: - messages.append('Folder couldn\' be created because maximum number of folders exceeded : '+str(self.maxfolders)) - elif action == 'rename' and file_or_dir == 'dir': - oldname = path.split('/')[-2] - path = '/'.join(path.split('/')[:-2]) - try: - os.chdir(self.basepath+path) - os.rename(oldname,name) - messages.append('Folder renamed successfully from '+oldname+' to '+name) - except: - messages.append('Folder couldn\'t renamed to '+name) - elif action == 'delete' and file_or_dir == 'dir': - if path =='/': - messages.append('root folder can\'t be deleted') - else: - name = path.split('/')[-2] - path = '/'.join(path.split('/')[:-2]) - try: - os.chdir(self.basepath+path) - shutil.rmtree(name) - messages.append('Folder deleted successfully : '+name) - except: - messages.append('Folder couldn\'t deleted : '+name) - elif action == 'rename' and file_or_dir == 'file': - oldname = path.split('/')[-1] - old_ext = oldname.split('.')[1] if len(oldname.split('.'))>1 else None - new_ext = name.split('.')[1] if len(name.split('.'))>1 else None - if old_ext == new_ext: - path = '/'.join(path.split('/')[:-1]) - try: - os.chdir(self.basepath+path) - os.rename(oldname,name) - messages.append('File renamed successfully from '+oldname+' to '+name) - except: - messages.append('File couldn\'t be renamed to '+name) - else: - if old_ext: - messages.append('File extension should be same : .'+old_ext) - else: - messages.append('New file extension didn\'t match with old file extension') - elif action == 'delete' and file_or_dir == 'file': - if path =='/': - messages.append('root folder can\'t be deleted') - else: - name = path.split('/')[-1] - path = '/'.join(path.split('/')[:-1]) - try: - os.chdir(self.basepath+path) - os.remove(name) - messages.append('File deleted successfully : '+name) - except: - messages.append('File couldn\'t deleted : '+name) - elif action == 'move' or action == 'copy': - # from path to current_path - if self.current_path.find(path) == 0: - messages.append('Cannot move/copy to a child folder') - else : - path = os.path.normpath(path) # strip trailing slash if any - if os.path.exists(self.basepath+self.current_path+os.path.basename(path)): - messages.append('ERROR: A file/folder with this name already exists in the destination folder.') - else: - if action == 'move': - method = shutil.move - else: - if file_or_dir == 'dir': - method = shutil.copytree - else: - method = shutil.copy - try: - method(self.basepath+path, self.basepath+self.current_path+os.path.basename(path)) - except: - messages.append('File/folder couldn\'t be moved/copied.') - return messages - - def directory_structure(self): - self.idee = 0 - dir_structure = {'':{'id':self.next_id(),'open':'yes','dirs':{},'files':[]}} - os.chdir(self.basepath) - for directory,directories,files in os.walk('.'): - directory_list = directory[1:].split('/') - current_dir = None - nextdirs = dir_structure - for d in directory_list: - current_dir = nextdirs[d] - nextdirs = current_dir['dirs'] - if directory[1:]+'/' == self.current_path: - self.current_id = current_dir['id'] - current_dir['dirs'].update(dict(map(lambda d:(d,{'id':self.next_id(),'open':'no','dirs':{},'files':[]}),directories))) - current_dir['files'] = files - return dir_structure - - def media(self,path): - ext = path.split('.')[-1] - try: - mimetypes.init() - mimetype = mimetypes.guess_type(path)[0] - img = Image.open(self.basepath+'/'+path) - width,height = img.size - mx = max([width,height]) - w,h = width,height - if mx > 60: - w = width*60/mx - h = height*60/mx - img = img.resize((w,h), Image.ANTIALIAS) - response = HttpResponse(content_type = mimetype or "image/"+ext) - response['Cache-Control'] = 'max-age=3600' - img.save(response,mimetype.split('/')[1] if mimetype else ext.upper()) - return response - except Exception as e: - imagepath = settings.FILEMANAGER_STATIC_ROOT+'images/icons/'+ext+'.png' - if not os.path.exists(imagepath): - imagepath = settings.FILEMANAGER_STATIC_ROOT+'images/icons/default.png' - img = Image.open(imagepath) - width,height = img.size - mx = max([width,height]) - w,h = width,height - if mx > 60: - w = width*60/mx - h = height*60/mx - img = img.resize((w,h), Image.ANTIALIAS) - response = HttpResponse(content_type="image/png") - response['Cache-Control'] = 'max-age:3600' - img.save(response,'png') - return response - - def download(self,path,file_or_dir): - if not re.match(r'[\w\d_ -/]*',path).group(0) == path: - return HttpResponse('Invalid path') - if file_or_dir == 'file': - filepath = self.basepath + '/' + path - wrapper = FileWrapper(file(filepath)) - response = HttpResponse(wrapper, content_type=mimetypes.guess_type(filepath)[0]) - response['Content-Length'] = os.path.getsize(filepath) - response['Content-Disposition'] = 'attachment; filename='+path.split('/')[-1] - return response - elif file_or_dir == 'dir': - dirpath = self.basepath + '/' + path - dirname = dirpath.split('/')[-2] - response = HttpResponse(content_type='application/x-gzip') - response['Content-Disposition'] = 'attachment; filename=%s.tar.gz' % dirname - tarred = tarfile.open(fileobj=response, mode='w:gz') - tarred.add(dirpath,arcname=dirname) - tarred.close() - return response - - def render(self,request,path): - if request.GET.has_key('download'): - return self.download(path,request.GET['download']) - if path: - return self.media(path) - CKEditorFuncNum = request.GET.get('CKEditorFuncNum','') - messages = [] - self.current_path = '/' - self.current_id = 1 - if request.method == 'POST': - form = FileManagerForm(request.POST,request.FILES) - if form.is_valid(): - messages = self.handle_form(form,request.FILES) - if settings.FILEMANAGER_CHECK_SPACE: - space_consumed = self.get_size(self.basepath) - else: - space_consumed = 0 - return render(request, 'filemanager/index.html', { - 'dir_structure': self.directory_structure(), - 'messages':map(str,messages), - 'current_id':self.current_id, - 'CKEditorFuncNum':CKEditorFuncNum, - 'ckeditor_baseurl':self.ckeditor_baseurl, - 'public_url_base':self.public_url_base, - 'space_consumed':space_consumed, - 'max_space':self.maxspace, - 'show_space':settings.FILEMANAGER_SHOW_SPACE, - }) diff --git a/filemanager/admin.py b/filemanager/admin.py deleted file mode 100644 index 8c38f3f..0000000 --- a/filemanager/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/filemanager/fields.py b/filemanager/fields.py new file mode 100644 index 0000000..3868974 --- /dev/null +++ b/filemanager/fields.py @@ -0,0 +1,22 @@ +from django.db import models +from django import forms + +from .widgets import CKEditorWidget + + +class CKEditorField(models.TextField): + def __init__(self, *args, **kwargs): + """ arguments config,filemanager_url can be passed here + for the same use as of CKEditorWidget. + """ + self.config = kwargs.pop('config', {}) + self.filemanager_url = kwargs.pop('filemanager_url', '') + super(CKEditorField, self).__init__(*args, **kwargs) + + def formfield(self, **kwargs): + defaults = { + 'form_class': forms.CharField, + 'widget': CKEditorWidget(config=self.config, filemanager_url=self.filemanager_url) + } + defaults.update(kwargs) + return super(CKEditorField, self).formfield(**defaults) diff --git a/filemanager/models.py b/filemanager/models.py deleted file mode 100644 index d0e54b9..0000000 --- a/filemanager/models.py +++ /dev/null @@ -1,20 +0,0 @@ -from django.db import models -from django import forms -from .widgets import CKEditorWidget - -class CKEditorField(models.TextField): - def __init__(self, *args, **kwargs): - """ arguments config,filemanager_url can be passed here - for the same use as of CKEditorWidget. - """ - self.config = kwargs.pop('config', {}) - self.filemanager_url = kwargs.pop('filemanager_url', '') - super(CKEditorField, self).__init__(*args, **kwargs) - - def formfield(self, **kwargs): - defaults = { - 'form_class': forms.CharField, - 'widget': CKEditorWidget(config=self.config, filemanager_url=self.filemanager_url) - } - defaults.update(kwargs) - return super(CKEditorField, self).formfield(**defaults) diff --git a/filemanager/settings.py b/filemanager/settings.py index c8d563d..0c3f1df 100644 --- a/filemanager/settings.py +++ b/filemanager/settings.py @@ -2,10 +2,9 @@ from django.conf import settings FILEMANAGER_STATIC_ROOT = getattr(settings, 'FILEMANAGER_STATIC_ROOT', - os.path.dirname(os.path.abspath(__file__))+'/static/filemanager/') + os.path.dirname(os.path.abspath(__file__))+'/static/filemanager/') FILEMANAGER_CKEDITOR_JS = getattr(settings, 'FILEMANAGER_CKEDITOR_JS', - 'ckeditor/ckeditor.js') -FILEMANAGER_CHECK_SPACE = getattr(settings, 'FILEMANAGER_CHECK_SPACE', - True) -FILEMANAGER_SHOW_SPACE = getattr(settings, 'FILEMANAGER_SHOW_SPACE', - FILEMANAGER_CHECK_SPACE) + 'ckeditor/ckeditor.js') +FILEMANAGER_CHECK_SPACE = getattr(settings, 'FILEMANAGER_CHECK_SPACE', True) +FILEMANAGER_SHOW_SPACE = getattr(settings, 'FILEMANAGER_SHOW_SPACE', FILEMANAGER_CHECK_SPACE) +THUMBNAIL_PREFIX = getattr(settings, 'FILENMANAGER_THUMBNAIL_PREFIX', 'thumbs/') diff --git a/filemanager/static/filemanager/css/style.css b/filemanager/static/filemanager/css/style.css index e6d3485..0d81ea8 100644 --- a/filemanager/static/filemanager/css/style.css +++ b/filemanager/static/filemanager/css/style.css @@ -1,43 +1,50 @@ -*{ +* { margin:0; padding:0; font-family:sans-serif; } -body{ +body { border: 1px solid #888; - background: #E0DFDE -} -#main{ - margin:10px 10px 5px 10px; + background: #E0DFDE; +} +main { + height: calc(100vh - 47px); + margin: 10px 10px 5px 10px; +} +#left { + height: calc(100vh - 69px); + margin-right:10px; + padding:10px; + width:25%; + float:left; + background:white; + border:1px solid #ADABA9; + border-radius: 6px; + overflow:auto; +} + +#right { + height: calc(100vh - 49px;); + width:70%; + float:left; +} +#control { + height:22px; + padding:4px; +} +#content { + height: calc(100vh - 100px); + padding:10px; + background:white; + border:1px solid #ADABA9; + border-radius: 6px; + overflow-y:auto; + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: stretch; + justify-content: flex-start; } -#left{ - margin-right:10px; - padding:10px; - width:25%; - float:left; - background:white; - border:1px solid #ADABA9; - border-radius: 6px; - -moz-border-radius: 6px; - overflow:auto; -} - -#right{ - width:70%; - float:left; -} -#control{ - height:22px; - padding:4px; -} -#content{ - padding:10px; - background:white; - border:1px solid #ADABA9; - border-radius: 6px; - -moz-border-radius: 6px; - overflow-y:auto; -} #status-bar{ margin:0px 15px; height:25px; @@ -53,155 +60,161 @@ body{ .directory-name{ float:left; padding:3px; - padding-left:8px; - color:#302010; -} + padding-left:8px; + color:#302010; +} .directory-image{ width:24px; height:24px; - float:left; + float:left; } .directory-image-name{ float:left; - height:24px; - padding:1px 2px 1px 4px; - cursor:pointer; + height:24px; + padding:1px 2px 1px 4px; + cursor:pointer; } .directory-image-name:hover { background:#ffffd0; - padding:0px 2px 0px 3px; + padding:0px 2px 0px 3px; border:1px solid #ddddb0; border-radius: 2px; - -moz-border-radius: 2px; + -moz-border-radius: 2px; } .directory-sign{ float:left; width:20px; height:20px; - padding:3px; + padding:3px; color:#4050D0; - cursor:pointer; -} + cursor:pointer; +} .directory-sign:hover { color:#2030A0; -} +} .file{ - width:82px; - height:94px; - overflow:hidden; - padding: 4px; - margin: 9px; - float:left; - border: 1px solid #AAA; - border-radius: 4px; - -moz-border-radius: 4px; - cursor:pointer; + flex: 0 0 82px; + height:94px; + overflow:hidden; + padding: 4px; + margin: 9px; + border: 1px solid #AAA; + border-radius: 4px; + cursor:pointer; } .file:hover { background:#dddff0; -} +} .thumbnail{ width:60px; height:60px; - margin:10px; + margin:10px; } .thumbnail div{ width:60px; height:60px; - background:no-repeat center center; -} + background:no-repeat center center; +} .filename{ text-align:center; text-overflow:ellipsis; overflow:hidden; - font:12px sans-serif; + font:12px sans-serif; } .menu{ position:fixed; width:150px; margin:3px; - padding:2px; - left:100px; - top:100px; + padding:2px; z-index:100; background:white; - border:1px solid #555; + border: 1px solid #555; border-radius: 4px; - -moz-border-radius: 4px; } -.menu-option{ - height:20px; - padding:5px; - font-size:11px; - cursor:pointer; -} -.menu-option:hover { - background:#eee; -} -.menu-option img{ - float:left; -} -.menu-option span{ - display:block; - float:left; - padding:2px 5px 5px 10px; -} +.menu li { + list-style-type: none; + list-style-position: inside; + + line-height:20px; + vertical-align: baseline; + padding:5px; + font-size:11px; + cursor:pointer; +} +.menu li:hover { background:#eee; } + +.menu li.add-dir { list-style-image: url(../images/add_folder.png); } +.menu li.cut-dir { list-style-image: url(../images/cut.png); } +.menu li.copy-dir { list-style-image: url(../images/copy.png); } +.menu li.paste-dir { list-style-image: url(../images/paste.png); } +.menu li.rename-dir { list-style-image: url(../images/rename.png); } +.menu li.download-dir { list-style-image: url(../images/download.png); } +.menu li.delete-dir { list-style-image: url(../images/delete.png); } + +.menu li.rename-file { list-style-image: url(../images/rename.png); } +.menu li.cut-file { list-style-image: url(../images/cut.png); } +.menu li.copy-file { list-style-image: url(../images/copy.png); } +.menu li.download-file { list-style-image: url(../images/download.png); } +.menu li.delete-file { list-style-image: url(../images/delete.png); } + +.menu li.copy-public-link { list-style-image: url(../images/copy_public_link.png); } + #popup{ display:none; width:200px; height:90px; background:#ddd; - border:4px solid #555; + border:4px solid #555; border-radius: 10px; - -moz-border-radius: 10px; - position:absolute; + -moz-border-radius: 10px; + position:absolute; top:200px; - left:512px; - z-index:201; -} + left:512px; + z-index:201; +} #popup .div1{ height:40px; - padding:4px; + padding:4px; } #popup .div2{ padding:5px 5px 5px 10px; -} +} #popup .div1 img{ - cursor:pointer; + cursor:pointer; width:16px; height:16px; - float:right; + float:right; } #popup .div1 div{ float:left; - padding:15px 5px 5px 10px; - font-size:12px; + padding:15px 5px 5px 10px; + font-size:12px; } #popup .div2 input{ width:170px; -} +} .control{ width:120px; - height:20px; - float:left; + height:20px; + float:left; padding:2px 2px 2px 4px; - cursor:pointer; -} + cursor:pointer; +} .control:hover { padding:1px 1px 1px 3px; background:#e0e0e4; border:1px solid #b0b0b4; border-radius: 2px; - -moz-border-radius: 2px; + -moz-border-radius: 2px; } #message{ - height:20px; + height:20px; float:left; font:13px sans-serif; - color:#504700; + color:#504700; padding:4px 2px 0px 4px; - overflow:hidden; -} + overflow:hidden; +} .current_directory > .directory-image-name { background: none repeat scroll 0 0 #FFFFD0; diff --git a/filemanager/static/filemanager/images/add_folder.png b/filemanager/static/filemanager/images/add_folder.png index feb9315..8758265 100644 Binary files a/filemanager/static/filemanager/images/add_folder.png and b/filemanager/static/filemanager/images/add_folder.png differ diff --git a/filemanager/static/filemanager/images/close.png b/filemanager/static/filemanager/images/close.png index e6f4fae..76c6351 100644 Binary files a/filemanager/static/filemanager/images/close.png and b/filemanager/static/filemanager/images/close.png differ diff --git a/filemanager/static/filemanager/images/copy.png b/filemanager/static/filemanager/images/copy.png index a4b6b6a..76d44e9 100644 Binary files a/filemanager/static/filemanager/images/copy.png and b/filemanager/static/filemanager/images/copy.png differ diff --git a/filemanager/static/filemanager/images/copy_public_link.png b/filemanager/static/filemanager/images/copy_public_link.png index ffe1b4d..652739d 100644 Binary files a/filemanager/static/filemanager/images/copy_public_link.png and b/filemanager/static/filemanager/images/copy_public_link.png differ diff --git a/filemanager/static/filemanager/images/cut.png b/filemanager/static/filemanager/images/cut.png index d2972d0..74f4a6a 100644 Binary files a/filemanager/static/filemanager/images/cut.png and b/filemanager/static/filemanager/images/cut.png differ diff --git a/filemanager/static/filemanager/images/delete.png b/filemanager/static/filemanager/images/delete.png index 9b87de5..24baaf1 100644 Binary files a/filemanager/static/filemanager/images/delete.png and b/filemanager/static/filemanager/images/delete.png differ diff --git a/filemanager/static/filemanager/images/download.png b/filemanager/static/filemanager/images/download.png index f91e0fa..0326c10 100644 Binary files a/filemanager/static/filemanager/images/download.png and b/filemanager/static/filemanager/images/download.png differ diff --git a/filemanager/static/filemanager/images/folder.png b/filemanager/static/filemanager/images/folder.png index 8dc5735..afdf90a 100644 Binary files a/filemanager/static/filemanager/images/folder.png and b/filemanager/static/filemanager/images/folder.png differ diff --git a/filemanager/static/filemanager/images/folder_big.png b/filemanager/static/filemanager/images/folder_big.png old mode 100755 new mode 100644 index b520e21..abcb0ab Binary files a/filemanager/static/filemanager/images/folder_big.png and b/filemanager/static/filemanager/images/folder_big.png differ diff --git a/filemanager/static/filemanager/images/home_folder.png b/filemanager/static/filemanager/images/home_folder.png index 45deffe..1408c4e 100644 Binary files a/filemanager/static/filemanager/images/home_folder.png and b/filemanager/static/filemanager/images/home_folder.png differ diff --git a/filemanager/static/filemanager/images/icons/css.png b/filemanager/static/filemanager/images/icons/css.png index 6679cbc..a9e8524 100644 Binary files a/filemanager/static/filemanager/images/icons/css.png and b/filemanager/static/filemanager/images/icons/css.png differ diff --git a/filemanager/static/filemanager/images/icons/default.png b/filemanager/static/filemanager/images/icons/default.png index 73ed3c4..c0c42b6 100644 Binary files a/filemanager/static/filemanager/images/icons/default.png and b/filemanager/static/filemanager/images/icons/default.png differ diff --git a/filemanager/static/filemanager/images/icons/doc.png b/filemanager/static/filemanager/images/icons/doc.png index 6f143bc..90baac8 100644 Binary files a/filemanager/static/filemanager/images/icons/doc.png and b/filemanager/static/filemanager/images/icons/doc.png differ diff --git a/filemanager/static/filemanager/images/icons/docx.png b/filemanager/static/filemanager/images/icons/docx.png index fc14b92..b2920a6 100644 Binary files a/filemanager/static/filemanager/images/icons/docx.png and b/filemanager/static/filemanager/images/icons/docx.png differ diff --git a/filemanager/static/filemanager/images/icons/gif.png b/filemanager/static/filemanager/images/icons/gif.png index 59980c6..96bff72 100644 Binary files a/filemanager/static/filemanager/images/icons/gif.png and b/filemanager/static/filemanager/images/icons/gif.png differ diff --git a/filemanager/static/filemanager/images/icons/html.png b/filemanager/static/filemanager/images/icons/html.png index d255f76..6b3889b 100644 Binary files a/filemanager/static/filemanager/images/icons/html.png and b/filemanager/static/filemanager/images/icons/html.png differ diff --git a/filemanager/static/filemanager/images/icons/jpg.png b/filemanager/static/filemanager/images/icons/jpg.png index 4fb9b50..7df9c6f 100644 Binary files a/filemanager/static/filemanager/images/icons/jpg.png and b/filemanager/static/filemanager/images/icons/jpg.png differ diff --git a/filemanager/static/filemanager/images/icons/js.png b/filemanager/static/filemanager/images/icons/js.png index 2f5b13d..5175df7 100644 Binary files a/filemanager/static/filemanager/images/icons/js.png and b/filemanager/static/filemanager/images/icons/js.png differ diff --git a/filemanager/static/filemanager/images/icons/mkv.png b/filemanager/static/filemanager/images/icons/mkv.png index 77863fd..ef9fbde 100644 Binary files a/filemanager/static/filemanager/images/icons/mkv.png and b/filemanager/static/filemanager/images/icons/mkv.png differ diff --git a/filemanager/static/filemanager/images/icons/mp3.png b/filemanager/static/filemanager/images/icons/mp3.png index 017194f..0f0b941 100644 Binary files a/filemanager/static/filemanager/images/icons/mp3.png and b/filemanager/static/filemanager/images/icons/mp3.png differ diff --git a/filemanager/static/filemanager/images/icons/mp4.png b/filemanager/static/filemanager/images/icons/mp4.png index 77863fd..ef9fbde 100644 Binary files a/filemanager/static/filemanager/images/icons/mp4.png and b/filemanager/static/filemanager/images/icons/mp4.png differ diff --git a/filemanager/static/filemanager/images/icons/pdf.png b/filemanager/static/filemanager/images/icons/pdf.png index 823682b..5bcc0ec 100644 Binary files a/filemanager/static/filemanager/images/icons/pdf.png and b/filemanager/static/filemanager/images/icons/pdf.png differ diff --git a/filemanager/static/filemanager/images/icons/png.png b/filemanager/static/filemanager/images/icons/png.png index d65f202..cae3033 100644 Binary files a/filemanager/static/filemanager/images/icons/png.png and b/filemanager/static/filemanager/images/icons/png.png differ diff --git a/filemanager/static/filemanager/images/icons/ppt.png b/filemanager/static/filemanager/images/icons/ppt.png index 5f908c2..e399b54 100644 Binary files a/filemanager/static/filemanager/images/icons/ppt.png and b/filemanager/static/filemanager/images/icons/ppt.png differ diff --git a/filemanager/static/filemanager/images/icons/pptx.png b/filemanager/static/filemanager/images/icons/pptx.png index 93740e6..54ab922 100644 Binary files a/filemanager/static/filemanager/images/icons/pptx.png and b/filemanager/static/filemanager/images/icons/pptx.png differ diff --git a/filemanager/static/filemanager/images/icons/psd.png b/filemanager/static/filemanager/images/icons/psd.png index f94a911..0f3717b 100644 Binary files a/filemanager/static/filemanager/images/icons/psd.png and b/filemanager/static/filemanager/images/icons/psd.png differ diff --git a/filemanager/static/filemanager/images/icons/swf.png b/filemanager/static/filemanager/images/icons/swf.png index 1c9e390..db9dce2 100644 Binary files a/filemanager/static/filemanager/images/icons/swf.png and b/filemanager/static/filemanager/images/icons/swf.png differ diff --git a/filemanager/static/filemanager/images/icons/txt.png b/filemanager/static/filemanager/images/icons/txt.png index 8f0176c..5fa86e2 100644 Binary files a/filemanager/static/filemanager/images/icons/txt.png and b/filemanager/static/filemanager/images/icons/txt.png differ diff --git a/filemanager/static/filemanager/images/icons/xls.png b/filemanager/static/filemanager/images/icons/xls.png index 989a1b6..fac3301 100644 Binary files a/filemanager/static/filemanager/images/icons/xls.png and b/filemanager/static/filemanager/images/icons/xls.png differ diff --git a/filemanager/static/filemanager/images/icons/xlsx.png b/filemanager/static/filemanager/images/icons/xlsx.png index 86a7b03..c2e7a00 100644 Binary files a/filemanager/static/filemanager/images/icons/xlsx.png and b/filemanager/static/filemanager/images/icons/xlsx.png differ diff --git a/filemanager/static/filemanager/images/icons/zip.png b/filemanager/static/filemanager/images/icons/zip.png index 04ab838..2db4c4d 100644 Binary files a/filemanager/static/filemanager/images/icons/zip.png and b/filemanager/static/filemanager/images/icons/zip.png differ diff --git a/filemanager/static/filemanager/images/opened_folder.png b/filemanager/static/filemanager/images/opened_folder.png index 444bec2..3e2e47d 100644 Binary files a/filemanager/static/filemanager/images/opened_folder.png and b/filemanager/static/filemanager/images/opened_folder.png differ diff --git a/filemanager/static/filemanager/images/paste.png b/filemanager/static/filemanager/images/paste.png index c10cd10..a4b6e67 100644 Binary files a/filemanager/static/filemanager/images/paste.png and b/filemanager/static/filemanager/images/paste.png differ diff --git a/filemanager/static/filemanager/images/rename.png b/filemanager/static/filemanager/images/rename.png index e858154..530bb12 100644 Binary files a/filemanager/static/filemanager/images/rename.png and b/filemanager/static/filemanager/images/rename.png differ diff --git a/filemanager/static/filemanager/images/upload.png b/filemanager/static/filemanager/images/upload.png index 2ceaae0..779048c 100644 Binary files a/filemanager/static/filemanager/images/upload.png and b/filemanager/static/filemanager/images/upload.png differ diff --git a/filemanager/static/filemanager/js/script.js b/filemanager/static/filemanager/js/script.js index b7953a2..6035335 100644 --- a/filemanager/static/filemanager/js/script.js +++ b/filemanager/static/filemanager/js/script.js @@ -1,167 +1,168 @@ var action,type; -var selected_dir_id,selected_file; +var selected_dir_id, selected_file; var zclip = false; -function get_human_string(val) -{ - var denominations = ['B', 'KB', 'MB', 'GB', 'TB']; - var i = 0; - while(val > 1024) - { - ++i; - val = val/1024; - } - val = val.toString(); - if(val.indexOf('.') != -1) - val = val.substr(0,val.indexOf('.')+3); - return val + denominations[i]; -} - -function size(){ - $('#main').height($(window).height()-47); - $('#left').height($(window).height()-69); - $('#right').height($(window).height()-49); - $('#content').height($(window).height()-100); +function get_human_string(val) { + var denominations = ['B', 'KB', 'MB', 'GB', 'TB']; + var i = 0; + while(val > 1024) { + ++i; + val = val/1024; + } + val = val.toFixed(2); + return val + denominations[i]; } -function onload(){ - size(); - refresh_dirs(); - show_files(dir_id); - $('body').bind('click',function(e){$('#dir-menu').hide();$('#file-menu').hide();}); - if(messages.length>0){ - $('#message').html(messages[0]); - setTimeout("$('#message').html('Hint : Use right click to add, rename or delete files and folders');",10000); - } - else - $('#message').html('Hint : Use right click to add,rename or delete files and folders'); - $('#ufile').change(function(){form_submit('upload','file');}); - if($.browser.mozilla) { - moz_major_ver = (+($.browser.version.split(".")[0])) - if(moz_major_ver < 23){ - // https://bugzilla.mozilla.org/show_bug.cgi?id=701353 should be fixed in 23 - $('#upload-label').click(function(){ - $('#ufile').click(); - }); +function onload() { + refresh_dirs(); + show_files(dir_id); + $('body').on('click', function(e) { + $('#dir-menu').hide(); + $('#file-menu').hide(); + }); + if(messages.length > 0) { + $('#message').html(messages[0]); + setTimeout(function () { + $('#message').html('Hint : Use right click to add, rename or delete files and folders'); + }, + 10000 + ); } - } - $('#space_quota').width(((space_consumed*100)/max_space).toString()+'%'); - $('#space_quota_string').html(get_human_string(space_consumed) + ' of ' + get_human_string(max_space) + ' used'); + else + $('#message').html('Hint : Use right click to add,rename or delete files and folders'); + + $('#ufile').change(function() { + form_submit('upload', 'file'); + }); + $('#space_quota').width(((space_consumed * 100) / max_space).toString() + '%'); + $('#space_quota_string').html(get_human_string(space_consumed) + ' of ' + get_human_string(max_space) + ' used'); } $('body').ready(onload); -$(window).resize(size); -function refresh_dirs() -{ - $('#left').html(show_directories(dir_structure)); +function refresh_dirs() { + $('#left').html(show_directories(dir_structure)); } -function get_dir(id,ds) -{if(!ds)ds=dir_structure; - dir = null; - for(d in ds) - { if(ds[d]['id']==id) - { dir = ds[d]; - break; - } - var dq = get_dir(id,ds[d]['dirs']); - if(dq != null) - { dir =dq; - break; - } - } - return dir; +function get_dir(id, ds) { + if(!ds) + ds=dir_structure; + dir = null; + for(var d in ds) { + if(ds[d].id == id) { + dir = ds[d]; + break; + } + var dq = get_dir(id,ds[d].dirs); + if(dq !== null) { + dir =dq; + break; + } + } + return dir; } -function get_path(id,ds,basepath) -{if(!ds)ds=dir_structure; - if(!basepath)basepath=''; - path = null; - for(d in ds) - { if(ds[d]['id']==id) - { path=d+'/'; - break - } - var pq = get_path(id,ds[d]['dirs'],d+'/'); - if(pq != null) - { path=pq; - break; - } - } - if(path) return basepath+path; - else return null; -} +function get_path(id, ds, basepath) { + if(!ds) + ds = dir_structure; + + if(!basepath) + basepath = ''; -function change_sign(id) -{d = get_dir(id); - if(d['open']=='yes')d['open']='no'; - else d['open']='yes'; - refresh_dirs(); + path = null; + for(var d in ds) { + if(ds[d].id == id) { + path = d + '/'; + break; + } + var pq = get_path(id, ds[d].dirs, d + '/'); + if(pq !== null) { + path = pq; + break; + } + } + if(path) + return basepath + path; + else + return null; } -function CKEditorRepy(filename) -{ var filepath = ckeditor_baseurl+get_path(dir_id)+filename; - window.opener.CKEDITOR.tools.callFunction(CKEditorFuncNum,filepath); - window.close(); +function change_sign(id) { + d = get_dir(id); + d.open = (d.open) ? false : true; + refresh_dirs(); } -function show_files(id) -{ dir_id = id; - var dirs = []; - var dir_list = get_dir(id)['dirs']; - for(var a in dir_list) - dirs.push({'name':a,'id':dir_list[a]['id']}); - dirs = dirs.sort(function(a,b){ - if(a['name'].toLowerCase() < b['name'].toLowerCase())return -1; - if(a['name'].toLowerCase() > b['name'].toLowerCase())return 1; - return 0; - }); - files = get_dir(id)['files'].sort( - function(a, b) { - if (a.toLowerCase() < b.toLowerCase()) return -1; - if (a.toLowerCase() > b.toLowerCase()) return 1; - return 0; - }); - $('#content').html(''); - for(d in dirs) - { - $('#content').append("
"+ - "
"+ - "
"+dirs[d]['name']+"
\n"); - } - for(f in files) - { var ext = files[f].split('.')[(files[f].split('.').length-1)]; - $('#content').append("
"+ - "
"+ - "
"+files[f]+"
\n"); - } - $('#status').html(get_path(id)) - $('.current_directory').removeClass('current_directory'); - $('#'+dir_id).addClass('current_directory'); +function show_files(id) { + dir_id = id; + var dirs = []; + var dir_list = get_dir(id).dirs; + for(var a in dir_list) + dirs.push({name: a, id: dir_list[a].id}); + + dirs = dirs.sort(function(a,b) { + if(a.name.toLowerCase() < b.name.toLowerCase()) return -1; + if(a.name.toLowerCase() > b.name.toLowerCase()) return 1; + return 0; + }); + files = get_dir(id).files.sort(function(a, b) { + if (a.toLowerCase() < b.toLowerCase()) return -1; + if (a.toLowerCase() > b.toLowerCase()) return 1; + return 0; + }); + $('#content').html(''); + for(var d in dirs) { + $('#content').append( + '
' + + '
' + + '
' + + '
' + + '
' + dirs[d].name + '
' + + '
' + ); + } + for(var f in files) { + var ext = files[f].split('.')[files[f].split('.').length - 1]; + $('#content').append( + '
' + + '
' + + '
' + + '
' + files[f] + '
' + + '
\n' + ); + } + $('#status').html(get_path(id)); + $('.current_directory').removeClass('current_directory'); + $('#'+dir_id).addClass('current_directory'); } -function show_directories(ds) -{ var html = ""; - for(d in ds) - { var image = (ds[d]['open']=='yes'?'opened_folder.png':'folder.png'); - if(d=='')image = 'home_folder.png'; - var id = ds[d]['id']; - var sign; - sign = (ds[d]['open']=='yes'?'[-]':'[+]'); - var empty = true; - for(i in ds[d]['dirs']){empty=false;break;} - if(empty)sign = ''; - html+="
"+sign+"
"+ - "
"+ - ""+ - "
"+(d==''?'root':d)+"
\n"; - if(ds[d]['open']=='yes') - html+="
"+show_directories(ds[d]['dirs'])+"
\n"; - } - return html; +function show_directories(ds) { + var html = ''; + for(var d in ds) { + var image = ds[d].open ? 'opened_folder.png' : 'folder.png'; + if(d === '') + image = 'home_folder.png'; + var id = ds[d].id; + var sign; + sign = ds[d].open ? '[-]' : '[+]'; + var empty = true; + for(var i in ds[d].dirs){ + empty=false; break; + } + if(empty) + sign = ''; + html += '
' + + '
' + sign + '
' + + '
' + + '' + + '
' + (d === '' ? 'root' : d) + '
' + + '
' + + '
'; + if(ds[d].open) + html += "
" + show_directories(ds[d].dirs) + "
\n"; + } + return html; } function getPosition(e) { @@ -182,151 +183,192 @@ function getPosition(e) { return cursor; } -function rightclick_handle(e,id,type) -{ var c = getPosition(e); - if(type == 'dir'){ - if(e.button==2){ - selected_dir_id = id; - $('#dir-menu').css('left',c.x+8); - $('#dir-menu').css('top',c.y+2); - if(clipboard['empty']) - { - $('#paste-dir').hide(); - $('#paste-dir').next().hide(); - } - else - { - $('#paste-dir').show(); - $('#paste-dir').next().show(); - } - $('#dir-menu').show(); - } - } - else if(type == 'file'){ - if(e.button==2){ - selected_file = id; - $('#file-menu').css('left',c.x+8); - $('#file-menu').css('top',c.y+2); - $('#file-menu').show(); - if(!zclip){ - zclip = true; - $('#copy-public-link-file').zclip({ - path: static_url+'filemanager/js/jquery/zclip/ZeroClipboard.swf', - copy: function(){ - var public_link = public_url_base+get_path(dir_id)+selected_file; - return public_link; - }, - clickAfter: false, - beforeCopy: function(){}, - afterCopy: function(){ - $('#file-menu').hide(); +function rightclick_handle(e,id,type) { + var c = getPosition(e); + if(type == 'dir') { + if(e.button == 2) { + selected_dir_id = id; + $('#dir-menu').css('left', c.x+8); + $('#dir-menu').css('top', c.y+2); + if(clipboard.empty) + { + $('#paste-dir').hide(); + $('#paste-dir').next().hide(); + } + else + { + $('#paste-dir').show(); + $('#paste-dir').next().show(); + } + $('#dir-menu').show(); } - }); } + else if(type == 'file') { + if(e.button == 2) { + selected_file = id; + $('#file-menu').css('left', c.x+8); + $('#file-menu').css('top', c.y+2); + $('#file-menu').show(); + if(!zclip){ + zclip = true; + $('#copy-public-link-file').zclip({ + path: static_url + 'filemanager/js/jquery/zclip/ZeroClipboard.swf', + copy: function() { + var public_link = public_url_base + get_path(dir_id) + selected_file; + return public_link; + }, + clickAfter: false, + beforeCopy: function() {}, + afterCopy: function() { + $('#file-menu').hide(); + } + }); + } + } } - if(e.button==0 && e.detail>=2 && CKEditorFuncNum){ - CKEditorRepy(id); - } - } } -function do_action(act,t) -{var heading; - if(t == 'dir') - {if(act == 'add')heading = "Enter name of sub-folder"; - if(act == 'rename')heading = "Enter new name of folder"; - if(act == 'delete'){form_submit(act,t);return;} - if(act == 'download'){window.open('.'+get_path(selected_dir_id)+'?download=dir');return;} - if(act == 'cut'){ if(selected_dir_id == 1){alert('You cannot cut root directory');return;}clipboard['empty']=false;clipboard['type']='dir';clipboard['path']=get_path(selected_dir_id);clipboard['mode']='cut';return;} - if(act == 'copy'){ clipboard['empty']=false;clipboard['type']='dir';clipboard['path']=get_path(selected_dir_id);clipboard['mode']='copy';return;} - if(act == 'paste') - { - if(clipboard['empty']) - { - alert('Clipboard is empty. Please cut/copy the required file.'); - return; - } - if(get_path(selected_dir_id).indexOf(clipboard['path']) == 0) - { - alert('Cannot move/copy to a child folder'); - return; +function do_action(act, t) { + var heading; + if(t == 'dir') { + if(act == 'add') + heading = "Enter name of sub-folder"; + if(act == 'rename') + heading = "Enter new name of folder"; + if(act == 'delete') { + form_submit(act, t); + return; + } + if(act == 'download') { + window.open('.' + get_path(selected_dir_id) + '?download=dir'); + return; + } + if(act == 'cut') { + if(selected_dir_id == 1){ + alert('You cannot cut root directory'); + return; + } + clipboard.empty = false; + clipboard.type = 'dir'; + clipboard.path = get_path(selected_dir_id); + clipboard.mode = 'cut'; + return; + } + if(act == 'copy') { + clipboard.empty = false; + clipboard.type = 'dir'; + clipboard.path = get_path(selected_dir_id); + clipboard.mode = 'copy'; + return; + } + if(act == 'paste') { + if(clipboard.empty) { + alert('Clipboard is empty. Please cut/copy the required file.'); + return; + } + if(get_path(selected_dir_id).indexOf(clipboard.path) === 0) { + alert('Cannot move/copy to a child folder'); + return; + } + if(window.confirm(clipboard.mode + ' ' + clipboard.path + ' to ' + get_path(selected_dir_id))) { + form_submit(clipboard.mode, '', ''); + } + return; + } } - if(window.confirm(clipboard['mode']+' '+clipboard['path'] + ' to '+get_path(selected_dir_id))) - { - form_submit(clipboard['mode'],'', ''); + if(t == 'file') { + if(act == 'rename') + heading = "Enter new name of file"; + if(act == 'delete') { + form_submit(act,t); + return; + } + if(act == 'download'){ + window.open('.' + get_path(dir_id) + selected_file + '?download=file'); + return; + } + if(act == 'copy-public-link'){ + window.prompt("Public URL(Ctrl+C to copy to clipboard):", public_url_base + get_path(dir_id) + selected_file); + return; + } + if(act == 'cut') { + clipboard.empty = false; + clipboard.type = 'file'; + clipboard.path = get_path(dir_id) + selected_file; + clipboard.mode = 'cut'; + return; + } + if(act == 'copy') { + clipboard.empty = false; + clipboard.type = 'file'; + clipboard.path = get_path(dir_id) + selected_file; + clipboard.mode = 'copy'; + return; + } } - return; - } - } - if(t == 'file') - {if(act == 'rename')heading = "Enter new name of file"; - if(act == 'delete'){form_submit(act,t);return;} - if(act == 'download'){window.open('.'+get_path(dir_id)+selected_file+'?download=file');return;} - if(act == 'copy-public-link'){window.prompt("Public URL(Ctrl+C to copy to clipboard):",public_url_base+get_path(dir_id)+selected_file);return;} - if(act == 'cut'){ clipboard['empty']=false;clipboard['type']='file';clipboard['path']=get_path(dir_id)+selected_file;clipboard['mode']='cut';return;} - if(act == 'copy'){ clipboard['empty']=false;clipboard['type']='file';clipboard['path']=get_path(dir_id)+selected_file;clipboard['mode']='copy';return;} - } - action = act; - type = t; - var w = $(window); - $('#popup').css('left',w.width()/2-100); - $('#popup').css('top',w.height()/2-100); - $('#heading').html(heading); - $('#popup').show(); - $('#input').focus(); - $('#input').keypress( - function(e){ - code= (e.keyCode ? e.keyCode : e.which); - if(code == 13){ - var value=$('#input').val(); - $('#input').val(''); - $('#popup').hide(); - form_submit(action,type,value); - } - }); + action = act; + type = t; + var w = $(window); + $('#popup').css('left', w.width() / 2 - 100); + $('#popup').css('top', w.height() / 2 - 100); + $('#heading').html(heading); + $('#popup').show(); + $('#input').focus(); + $('#input').keypress( + function(e){ + code= (e.keyCode ? e.keyCode : e.which); + if(code == 13) { + var value=$('#input').val(); + $('#input').val(''); + $('#popup').hide(); + form_submit(action,type,value); + } + }); } function form_submit(action,type,value){ - $('#path').val(get_path(dir_id)); - $('#current_path').val(get_path(dir_id)); - $('#file_or_dir').val(type); - if(action == 'cut') - { - $('#action').val('move'); - $('#file_or_dir').val(clipboard['type']); - $('#path').val(clipboard['path']); - $('#current_path').val(get_path(selected_dir_id)); - $('#submit').trigger('click'); - } - if(action == 'copy') - { - $('#action').val('copy'); - $('#file_or_dir').val(clipboard['type']); - $('#path').val(clipboard['path']); - $('#current_path').val(get_path(selected_dir_id)); - $('#submit').trigger('click'); - } - if(action == 'upload') - { $('#action').val('upload'); - $('#submit').trigger('click'); - } - else if(action == 'add') - { $('#path').val(get_path(selected_dir_id)); - $('#action').val('add'); - $('#name').val(value); - $('#submit').trigger('click'); - } - else if(action == 'rename') - { if(type == 'dir')$('#path').val(get_path(selected_dir_id)); - if(type == 'file')$('#path').val(get_path(dir_id)+selected_file); - $('#action').val('rename'); - $('#name').val(value); - $('#submit').trigger('click'); - } - else if(action == 'delete') - { if(type == 'dir')$('#path').val(get_path(selected_dir_id)); - if(type == 'file')$('#path').val(get_path(dir_id)+selected_file); - $('#action').val('delete'); - $('#submit').trigger('click'); - } + $('#path').val(get_path(dir_id)); + $('#current_path').val(get_path(dir_id)); + $('#file_or_dir').val(type); + if(action == 'cut') { + $('#action').val('move'); + $('#file_or_dir').val(clipboard.type); + $('#path').val(clipboard.path); + $('#current_path').val(get_path(selected_dir_id)); + $('#submit').trigger('click'); + } + if(action == 'copy') { + $('#action').val('copy'); + $('#file_or_dir').val(clipboard.type); + $('#path').val(clipboard.path); + $('#current_path').val(get_path(selected_dir_id)); + $('#submit').trigger('click'); + } + if(action == 'upload') { + $('#action').val('upload'); + $('#submit').trigger('click'); + } + else if(action == 'add') { + $('#path').val(get_path(selected_dir_id)); + $('#action').val('add'); + $('#name').val(value); + $('#submit').trigger('click'); + } + else if(action == 'rename') { + if(type == 'dir') + $('#path').val(get_path(selected_dir_id)); + if(type == 'file') + $('#path').val(get_path(dir_id)+selected_file); + $('#action').val('rename'); + $('#name').val(value); + $('#submit').trigger('click'); + } + else if(action == 'delete') { + if(type == 'dir') + $('#path').val(get_path(selected_dir_id)); + if(type == 'file') + $('#path').val(get_path(dir_id)+selected_file); + $('#action').val('delete'); + $('#submit').trigger('click'); + } } diff --git a/filemanager/templates/filemanager/index.html b/filemanager/templates/filemanager/index.html index eb0b02f..1dd2a46 100644 --- a/filemanager/templates/filemanager/index.html +++ b/filemanager/templates/filemanager/index.html @@ -1,118 +1,80 @@ - +{% load static %} - - - - - - - + + + + - - - - -
-
-
- -
- - -
- {% if show_space %} -
-
-
-
- - - {% endif %} - - -
- + + + + +
+
+ +
+ + + +
+ {% if show_space %} +
+
+
+ + {% endif %} + +
+ diff --git a/filemanager/tests.py b/filemanager/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/filemanager/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/filemanager/urls.py b/filemanager/urls.py new file mode 100644 index 0000000..1e7e17e --- /dev/null +++ b/filemanager/urls.py @@ -0,0 +1,14 @@ +from django.conf import settings +from django.conf.urls import url +from django.contrib.auth.decorators import user_passes_test + +from . import views + +staff_required = user_passes_test(lambda u: u.is_staff) + + +urlpatterns = [ + url(r'^(?P[\w -/.]*)$', + staff_required(views.FileManager.as_view(basepath=settings.MEDIA_ROOT)) + ), +] diff --git a/filemanager/views.py b/filemanager/views.py new file mode 100644 index 0000000..dae352a --- /dev/null +++ b/filemanager/views.py @@ -0,0 +1,325 @@ +from io import BytesIO +import json +import mimetypes +import os +import re +import shutil +import tarfile + +from django import forms +from django.contrib.staticfiles.templatetags.staticfiles import static +from django.core.files import File +from django.core.files.storage import default_storage +from django.http import HttpResponse, HttpResponseRedirect +from django.shortcuts import render +from django.utils._os import safe_join +from django.utils.six.moves.urllib.parse import urljoin +from django.views.generic import View + +from PIL import Image + +from . import settings + +KB = 1024 + +ActionChoices = ( + ('upload', 'upload'), + ('rename', 'rename'), + ('delete', 'delete'), + ('add', 'add'), + ('move', 'move'), + ('copy', 'copy'), +) + + +def is_valid_filename(name): + return not re.match(r'[^-\w\d \./]', name) + + +def is_valid_dirname(name): + return is_valid_filename(name) + + +class FileManagerForm(forms.Form): + ufile = forms.FileField(required=False) + action = forms.ChoiceField(choices=ActionChoices) + path = forms.CharField(max_length=200, required=False) + name = forms.CharField(max_length=32, required=False) + current_path = forms.CharField(max_length=200, required=False) + file_or_dir = forms.CharField(max_length=4) + + def clean_path(self): + return self.cleaned_data['path'].lstrip('/') + + +class FileManager(View): + """ + maxspace,maxfilesize in KB + """ + basepath = None + maxfolders = 50 + maxspace = 5 * KB + maxfilesize = 1 * KB + extensions = [] + public_url_base = None + + def dispatch(self, request, path): + self.idee = 0 + + if 'download' in request.GET: + return self.download(path, request.GET['download']) + if path: + return self.media(path) + messages = [] + self.current_path = '/' + self.current_id = 1 + if request.method == 'POST': + form = FileManagerForm(request.POST, request.FILES) + if form.is_valid(): + messages = self.handle_form(form, request.FILES) + if settings.FILEMANAGER_CHECK_SPACE: + space_consumed = self.get_size(self.basepath) + else: + space_consumed = 0 + return render(request, 'filemanager/index.html', { + 'dir_structure': json.dumps(self.directory_structure()), + 'messages': [str(m) for m in messages], + 'current_id': self.current_id, + 'public_url_base': self.public_url_base, + 'space_consumed': space_consumed, + 'max_space': self.maxspace, + 'show_space': settings.FILEMANAGER_SHOW_SPACE, + }) + + # XXX Replace with with using storage API + def rename_if_exists(self, folder, filename): + if os.path.exists(safe_join(folder, filename)): + root, ext = os.path.splitext(filename) + if not ext: + fmt = '{root}.{i}' + else: + fmt = '{root}.{i}.{ext}' + for i in range(1000): + filename = fmt.format(root=root, i=i, ext=ext) + if not os.path.exists(safe_join(folder, filename)): + break + return filename + + def get_size(self, start_path): + total_size = 0 + for dirpath, dirnames, filenames, dir_fd in os.fwalk(start_path): + total_size += sum(os.stat(f, dir_fd=dir_fd).st_size for f in filenames) + return total_size + + def next_id(self): + self.idee = self.idee + 1 + return self.idee + + def handle_form(self, form, files): + action = form.cleaned_data['action'] + file_or_dir = form.cleaned_data['file_or_dir'] + self.current_path = form.cleaned_data['current_path'] + + try: + handler = getattr(self, 'do_{}_{}'.format(file_or_dir, action)) + except AttributeError: + return ['Action not supported!'] + else: + return handler(files=files, **form.cleaned_data) + + def do_file_upload(self, *, path=None, files=None, **kwargs): + messages = [] + for f in files.getlist('ufile'): + root, ext = os.path.splitext(f.name) + if not is_valid_filename(f.name): + messages.append("File name is not valid : " + f.name) + elif f.size > self.maxfilesize * KB: + messages.append("File size exceeded {} KB : {}".format(self.maxfilesize, f.name)) + elif settings.FILEMANAGER_CHECK_SPACE and (self.get_size(self.basepath) + f.size) > self.maxspace * KB: + messages.append("Total Space size exceeded {} KB: {}".format(self.maxspace, f.name)) + elif ext and self.extensions and ext.lower().lstrip('.') not in self.extensions: + messages.append("File extension not allowed ({}) : {}".format(ext, f.name)) + elif not ext and self.extensions and root not in self.extensions: + messages.append("No file extension in uploaded file : " + f.name) + else: + full_path = safe_join(self.basepath, path) + filepath = safe_join(full_path, self.rename_if_exists(full_path, f.name)) + with open(filepath, 'wb') as dest: + for chunk in f.chunks(): + dest.write(chunk) + f.close() + if not messages: + messages.append('All files uploaded successfully') + return messages + + def do_dir_rename(self, *, path=None, name=None, **kwargs): + path, oldname = os.path.split(path) + try: + os.chdir(safe_join(self.basepath, path)) + os.rename(oldname, name) + except: + return ["Folder couldn't renamed to {}".format(name)] + return ['Folder renamed successfully from {} to {}'.format(oldname, name)] + + def do_file_rename(self, *, path=None, name=None, **kwargs): + path, oldname = os.path.split(path) + _, old_ext = os.path.splitext(oldname) + _, new_ext = os.path.splitext(name) + if old_ext == new_ext: + try: + os.chdir(safe_join(self.basepath, path)) + os.rename(oldname, name) + except: + return ["File couldn't be renamed to {}".format(name)] + return ['File renamed successfully from {} to {}'.format(oldname, name)] + else: + if old_ext: + return ['File extension should be same : .{}'.format(old_ext)] + else: + return ["New file extension didn't match with old file extension"] + + def do_dir_delete(self, *, path=None, **kwargs): + if path == '': + return ["root folder can't be deleted"] + else: + full_path = safe_join(self.basepath, path) + base_path, name = os.path.split(full_path) + try: + os.chdir(base_path) + shutil.rmtree(name) + except: + return ["Folder couldn't deleted : {}".format(name)] + return ['Folder deleted successfully : {}'.format(name)] + + def do_file_delete(self, *, path=None, **kwargs): + if path == '': + return ["root folder can't be deleted"] + path, name = os.path.split(path) + try: + os.chdir(safe_join(self.basepath, path)) + os.remove(name) + except: + return ["File couldn't deleted : {}".format(name)] + return ['File deleted successfully : {}'.format(name)] + + def do_dir_add(self, *, path=None, name=None, **kwargs): + os.chdir(self.basepath) + no_of_folders = len(list(os.walk('.'))) + if no_of_folders >= self.maxfolders: + return ["Folder couldn' be created because maximum number of folders exceeded : {}".format(self.maxfolders)] + try: + os.chdir(safe_join(self.basepath, path)) + os.mkdir(name) + except: + return ["Folder couldn't be created : {}".format(name)] + return ['Folder created successfully : {}'.format(name)] + + def do_file_move(self, **kwargs): + return self._more_or_copy(method=shutil.move, **kwargs) + + def do_dir_move(self, **kwargs): + return self._more_or_copy(method=shutil.move, **kwargs) + + def do_file_copy(self, **kwargs): + return self._move_or_copy(method=shutil.copy, **kwargs) + + def do_dir_copy(self, **kwargs): + return self._move_or_copy(method=shutil.copytree, **kwargs) + + def _move_or_copy(self, *, method=None, path=None, **kwargs): + # from path to current_path + if self.current_path.find(path) == 0: + return ['Cannot move/copy to a child folder'] + path = os.path.normpath(path) # strip trailing slash if any + if os.path.exists(safe_join(self.basepath, self.current_path, os.path.basename(path))): + return ['ERROR: A file/folder with this name already exists in the destination folder.'] + try: + method(safe_join(self.basepath, path), + safe_join(self.basepath, self.current_path, os.path.basename(path))) + except: + return ["File/folder couldn't be moved/copied."] + + return [] + + def directory_structure(self): + self.idee = 0 + dir_structure = { + '': { + 'id': self.next_id(), + 'open': True, + 'dirs': {}, + 'files': [], + }, + } + os.chdir(self.basepath) + for directory, directories, files in os.walk('.'): + directory_list = directory[1:].split('/') + current_dir = None + nextdirs = dir_structure + for d in directory_list: + current_dir = nextdirs[d] + nextdirs = current_dir['dirs'] + if directory[1:] + '/' == self.current_path: + self.current_id = current_dir['id'] + current_dir['dirs'].update({ + d: { + 'id': self.next_id(), + 'open': False, + 'dirs': {}, + 'files': [], + } + for d in directories + }) + current_dir['files'] = files + return dir_structure + + def media(self, path): + filename = os.path.basename(path) + root, ext = os.path.splitext(filename) + mimetype, _ = mimetypes.guess_type(filename) + if mimetype and mimetype.startswith('image/'): + if not path.startswith(settings.THUMBNAIL_PREFIX): + # Generate target filename + target_name = os.path.join(settings.THUMBNAIL_PREFIX, path) + if not default_storage.exists(target_name): + # Generate the thumbnail + img = Image.open(default_storage.open(path)) + w, h = width, height = img.size + mx = max(width, height) + if mx > 60: + w = width * 60 // mx + h = height * 60 // mx + img = img.resize((w, h), Image.ANTIALIAS) + ifile = BytesIO() + # Thanks, SmileyChris + fmt = Image.EXTENSION.get(ext.lower(), 'JPEG') + img.save(ifile, fmt) + default_storage.save(target_name, File(ifile)) + url = urljoin(settings.settings.MEDIA_URL, default_storage.url(target_name)) + else: + url = urljoin(settings.settings.MEDIA_URL, default_storage.url(path)) + else: + # Use generic image for file type, if we have one + try: + url = static('filemanager/images/icons/{}.png'.format(ext.strip('.'))) + except ValueError: + url = static('filemanager/images/icons/default.png') + return HttpResponseRedirect(url) + + def download(self, path, file_or_dir): + full_path = safe_join(self.basepath, path) + base_name = os.path.basename(path) + if not re.match(r'[\w\d_ -/]*', path).group(0) == path: + return HttpResponse('Invalid path') + if file_or_dir == 'file': + response = HttpResponse(open(full_path), content_type=mimetypes.guess_type(full_path)[0]) + response['Content-Length'] = os.path.getsize(full_path) + response['Content-Disposition'] = 'attachment; filename={}'.format(base_name) + return response + elif file_or_dir == 'dir': + response = HttpResponse(content_type='application/x-gzip') + response['Content-Disposition'] = 'attachment; filename={}.tar.gz'.format(base_name) + tarred = tarfile.open(fileobj=response, mode='w:gz') + tarred.add(full_path, arcname=base_name) + tarred.close() + return response diff --git a/filemanager/widgets.py b/filemanager/widgets.py index cc6d163..198bc09 100644 --- a/filemanager/widgets.py +++ b/filemanager/widgets.py @@ -1,46 +1,41 @@ from django import forms from django.utils.safestring import mark_safe -from settings import FILEMANAGER_CKEDITOR_JS +from .settings import FILEMANAGER_CKEDITOR_JS + def filemanager_config(url): - d = {} - d['filebrowserBrowseUrl'] = url - d['filebrowserImageBrowseUrl'] = url - d['filebrowserWidth'] = 800 - d['filebrowserHeight'] = 500 - return d + d = {} + d['filebrowserBrowseUrl'] = url + d['filebrowserImageBrowseUrl'] = url + d['filebrowserWidth'] = 800 + d['filebrowserHeight'] = 500 + return d -class CKEditorWidget(forms.Textarea): - def __init__(self, attrs={}, config = {}, filemanager_url=''): - """ config : CKEditor config - filemanager_url : for user to 'browse server' - In config : toolbar = 'Basic'/'Standard'/'Full' - """ - default = { - 'toolbar': 'Standard', - 'height': 250, - 'width': 900 - } - default.update(config) - if filemanager_url: - default.update(filemanager_config(filemanager_url)) - self.config = default - return super(CKEditorWidget, self).__init__(attrs) - class Media: - js = ( - FILEMANAGER_CKEDITOR_JS, - ) +class CKEditorWidget(forms.Textarea): + def __init__(self, attrs={}, config={}, filemanager_url=''): + """ config : CKEditor config + filemanager_url : for user to 'browse server' + In config : toolbar = 'Basic'/'Standard'/'Full' + """ + default = { + 'toolbar': 'Standard', + 'height': 250, + 'width': 900 + } + default.update(config) + if filemanager_url: + default.update(filemanager_config(filemanager_url)) + self.config = default + return super(CKEditorWidget, self).__init__(attrs) - def render(self, name, value, attrs=None): - rendered = super(CKEditorWidget, self).render(name, value, attrs) - div = "
" - return rendered+mark_safe(div+ - u""" - """%(attrs['id'], self.config)) + class Media: + js = ( + FILEMANAGER_CKEDITOR_JS, + ) + def render(self, name, value, attrs=None): + rendered = super(CKEditorWidget, self).render(name, value, attrs) + div = "
" + return rendered + mark_safe(div + u""" """ % (attrs['id'], self.config))