diff --git a/apps/files/ajax/list.php b/apps/files/ajax/list.php index 14ed43cbb3..035ffc0e39 100644 --- a/apps/files/ajax/list.php +++ b/apps/files/ajax/list.php @@ -10,36 +10,34 @@ OCP\JSON::checkLoggedIn(); // Load the files $dir = isset( $_GET['dir'] ) ? $_GET['dir'] : ''; + +if (!\OC\Files\Filesystem::is_dir($dir . '/')) { + header("HTTP/1.0 404 Not Found"); + exit(); +} + $doBreadcrumb = isset($_GET['breadcrumb']); $data = array(); +$baseUrl = OCP\Util::linkTo('files', 'index.php') . '?dir='; // Make breadcrumb if($doBreadcrumb) { - $breadcrumb = array(); - $pathtohere = "/"; - foreach( explode( "/", $dir ) as $i ) { - if( $i != "" ) { - $pathtohere .= "$i/"; - $breadcrumb[] = array( "dir" => $pathtohere, "name" => $i ); - } - } + $breadcrumb = \OCA\files\lib\Helper::makeBreadcrumb($dir); - $breadcrumbNav = new OCP\Template( "files", "part.breadcrumb", "" ); - $breadcrumbNav->assign( "breadcrumb", $breadcrumb, false ); + $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); + $breadcrumbNav->assign('breadcrumb', $breadcrumb, false); + $breadcrumbNav->assign('baseURL', $baseUrl); $data['breadcrumb'] = $breadcrumbNav->fetchPage(); } // make filelist -$files = array(); -foreach( \OC\Files\Filesystem::getDirectoryContent( $dir ) as $i ) { - $i["date"] = OCP\Util::formatDate($i["mtime"] ); - $i['icon'] = \OCA\files\lib\Helper::determineIcon($i); - $files[] = $i; -} +$files = \OCA\files\lib\Helper::getFiles($dir); -$list = new OCP\Template( "files", "part.list", "" ); -$list->assign( "files", $files, false ); -$data = array('files' => $list->fetchPage()); +$list = new OCP\Template("files", "part.list", ""); +$list->assign('files', $files, false); +$list->assign('baseURL', $baseUrl, false); +$list->assign('downloadURL', OCP\Util::linkToRoute('download', array('file' => '/'))); +$data['files'] = $list->fetchPage(); OCP\JSON::success(array('data' => $data)); diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 8053649bd5..f506a37947 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -336,3 +336,25 @@ table.dragshadow td.size { text-align: center; margin-left: -200px; } +.mask { + z-index: 50; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: white; + background-repeat: no-repeat no-repeat; + background-position: 50%; + opacity: 0.7; + filter: alpha(opacity=70); + transition: opacity 100ms; + -moz-transition: opacity 100ms; + -o-transition: opacity 100ms; + -ms-transition: opacity 100ms; + -webkit-transition: opacity 100ms; +} +.mask.transparent{ + opacity: 0; +} + diff --git a/apps/files/index.php b/apps/files/index.php index 4443bf5fde..ec824f895b 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -41,62 +41,25 @@ if (!\OC\Files\Filesystem::is_dir($dir . '/')) { exit(); } -function fileCmp($a, $b) { - if ($a['type'] == 'dir' and $b['type'] != 'dir') { - return -1; - } elseif ($a['type'] != 'dir' and $b['type'] == 'dir') { - return 1; - } else { - return strnatcasecmp($a['name'], $b['name']); - } -} - $files = array(); $user = OC_User::getUser(); if (\OC\Files\Cache\Upgrade::needUpgrade($user)) { //dont load anything if we need to upgrade the cache - $content = array(); $needUpgrade = true; $freeSpace = 0; } else { - $content = \OC\Files\Filesystem::getDirectoryContent($dir); + $files = \OCA\files\lib\Helper::getFiles($dir); $freeSpace = \OC\Files\Filesystem::free_space($dir); $needUpgrade = false; } -foreach ($content as $i) { - $i['date'] = OCP\Util::formatDate($i['mtime']); - if ($i['type'] == 'file') { - $fileinfo = pathinfo($i['name']); - $i['basename'] = $fileinfo['filename']; - if (!empty($fileinfo['extension'])) { - $i['extension'] = '.' . $fileinfo['extension']; - } else { - $i['extension'] = ''; - } - } - $i['directory'] = $dir; - $i['isPreviewAvailable'] = \OCP\Preview::isMimeSupported($i['mimetype']); - $i['icon'] = \OCA\files\lib\Helper::determineIcon($i); - $files[] = $i; -} - -usort($files, "fileCmp"); // Make breadcrumb -$breadcrumb = array(); -$pathtohere = ''; -foreach (explode('/', $dir) as $i) { - if ($i != '') { - $pathtohere .= '/' . $i; - $breadcrumb[] = array('dir' => $pathtohere, 'name' => $i); - } -} +$breadcrumb = \OCA\files\lib\Helper::makeBreadcrumb($dir); // make breadcrumb und filelist markup $list = new OCP\Template('files', 'part.list', ''); $list->assign('files', $files); $list->assign('baseURL', OCP\Util::linkTo('files', 'index.php') . '?dir='); $list->assign('downloadURL', OCP\Util::linkToRoute('download', array('file' => '/'))); -$list->assign('disableSharing', false); $list->assign('isPublic', false); $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); $breadcrumbNav->assign('breadcrumb', $breadcrumb); @@ -154,5 +117,6 @@ if ($needUpgrade) { $tmpl->assign('isPublic', false); $tmpl->assign('publicUploadEnabled', $publicUploadEnabled); $tmpl->assign("encryptedFiles", \OCP\Util::encryptedFiles()); + $tmpl->assign('disableSharing', false); $tmpl->printPage(); } diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 097fe521aa..330fe86f6b 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -196,13 +196,12 @@ FileActions.register('all', 'Rename', OC.PERMISSION_UPDATE, function () { FileList.rename(filename); }); - FileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename) { - var dir = $('#dir').val(); + var dir = $('#dir').val() || '/'; if (dir !== '/') { dir = dir + '/'; } - window.location = OC.linkTo('files', 'index.php') + '?dir=' + encodeURIComponent(dir + filename); + FileList.changeDirectory(dir + filename); }); FileActions.setDefault('dir', 'Open'); diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 29be5e0d36..c205ae32aa 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -1,7 +1,25 @@ var FileList={ useUndo:true, + postProcessList: function(){ + $('#fileList tr').each(function(){ + //little hack to set unescape filenames in attribute + $(this).attr('data-file',decodeURIComponent($(this).attr('data-file'))); + }); + }, update:function(fileListHtml) { - $('#fileList').empty().html(fileListHtml); + var $fileList = $('#fileList'); + $fileList.empty().html(fileListHtml); + $('#emptycontent').toggleClass('hidden', $fileList.find('tr').length > 0); + $fileList.find('tr').each(function () { + FileActions.display($(this).children('td.filename')); + }); + $fileList.trigger(jQuery.Event("fileActionsReady")); + FileList.postProcessList(); + // "Files" might not be loaded in extending apps + if (window.Files){ + Files.setupDragAndDrop(); + } + $fileList.trigger(jQuery.Event("updated")); }, createRow:function(type, name, iconurl, linktarget, size, lastModified, permissions){ var td, simpleSize, basename, extension; @@ -134,20 +152,83 @@ var FileList={ FileActions.display(tr.find('td.filename')); return tr; }, - refresh:function(data) { - var result = jQuery.parseJSON(data.responseText); + /** + * @brief Changes the current directory and reload the file list. + * @param targetDir target directory (non URL encoded) + * @param changeUrl false if the URL must not be changed (defaults to true) + */ + changeDirectory: function(targetDir, changeUrl){ + var $dir = $('#dir'), + url, + currentDir = $dir.val() || '/'; + targetDir = targetDir || '/'; + if (currentDir === targetDir){ + return; + } + FileList.setCurrentDir(targetDir, changeUrl); + FileList.reload(); + }, + setCurrentDir: function(targetDir, changeUrl){ + $('#dir').val(targetDir); + // Note: IE8 handling ignored for now + if (window.history.pushState && changeUrl !== false){ + url = OC.linkTo('files', 'index.php')+"?dir="+ encodeURIComponent(targetDir).replace(/%2F/g, '/'), + window.history.pushState({dir: targetDir}, '', url); + } + }, + /** + * @brief Reloads the file list using ajax call + */ + reload: function(){ + FileList.showMask(); + if (FileList._reloadCall){ + FileList._reloadCall.abort(); + } + FileList._reloadCall = $.ajax({ + url: OC.filePath('files','ajax','list.php'), + data: { + dir : $('#dir').val(), + breadcrumb: true + }, + error: function(result){ + FileList.reloadCallback(result); + }, + success: function(result) { + FileList.reloadCallback(result); + } + }); + }, + reloadCallback: function(result){ + var $controls = $('#controls'); + + delete FileList._reloadCall; + FileList.hideMask(); + + if (!result || result.status === 'error') { + OC.Notification.show(result.data.message); + return; + } + + if (result.status === 404){ + // go back home + FileList.changeDirectory('/'); + return; + } + if(typeof(result.data.breadcrumb) != 'undefined'){ - updateBreadcrumb(result.data.breadcrumb); + $controls.find('.crumb').remove(); + $controls.prepend(result.data.breadcrumb); + // TODO: might need refactor breadcrumb code into a new file + //resizeBreadcrumbs(true); } FileList.update(result.data.files); - resetFileActionPanel(); }, remove:function(name){ $('tr').filterAttr('data-file',name).find('td.filename').draggable('destroy'); $('tr').filterAttr('data-file',name).remove(); FileList.updateFileSummary(); if($('tr[data-file]').length==0){ - $('#emptycontent').show(); + $('#emptycontent').removeClass('hidden'); } }, insertElement:function(name,type,element){ @@ -177,7 +258,7 @@ var FileList={ }else{ $('#fileList').append(element); } - $('#emptycontent').hide(); + $('#emptycontent').addClass('hidden'); FileList.updateFileSummary(); }, loadingDone:function(name, id){ @@ -508,6 +589,30 @@ var FileList={ $connector.show(); } } + }, + showMask: function(){ + // in case one was shown before + var $mask = $('#content .mask'); + if ($mask.length){ + return; + } + + $mask = $('
'); + + $mask.css('background-image', 'url('+ OC.imagePath('core', 'loading.gif') + ')'); + $('#content').append($mask); + + // block UI, but only make visible in case loading takes longer + FileList._maskTimeout = window.setTimeout(function(){ + // reset opacity + $mask.removeClass('transparent'); + }, 250); + }, + hideMask: function(){ + var $mask = $('#content .mask').remove(); + if (FileList._maskTimeout){ + window.clearTimeout(FileList._maskTimeout); + } } }; @@ -629,8 +734,8 @@ $(document).ready(function(){ } // update folder size - var size = parseInt(data.context.data('size')); - size += parseInt(file.size) ; + var size = parseInt(data.context.data('size')); + size += parseInt(file.size); data.context.attr('data-size', size); data.context.find('td.filesize').text(humanFileSize(size)); @@ -710,5 +815,19 @@ $(document).ready(function(){ $(window).trigger('beforeunload'); }); + window.onpopstate = function(e){ + var targetDir; + if (e.state && e.state.dir){ + targetDir = e.state.dir; + } + else{ + // read from URL + targetDir = (OC.parseQueryString(location.search) || {dir: '/'}).dir || '/'; + } + if (targetDir){ + FileList.changeDirectory(targetDir, false); + } + } + FileList.createFileSummary(); }); diff --git a/apps/files/js/files.js b/apps/files/js/files.js index d729077ea7..ce72c7bcb5 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -94,29 +94,34 @@ Files={ OC.Notification.show(t('files_encryption', 'Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files.')); return; } + }, + + setupDragAndDrop: function(){ + var $fileList = $('#fileList'); + + //drag/drop of files + $fileList.find('tr td.filename').each(function(i,e){ + if ($(e).parent().data('permissions') & OC.PERMISSION_DELETE) { + $(e).draggable(dragOptions); + } + }); + + $fileList.find('tr[data-type="dir"] td.filename').each(function(i,e){ + if ($(e).parent().data('permissions') & OC.PERMISSION_CREATE){ + $(e).droppable(folderDropOptions); + } + }); } }; $(document).ready(function() { Files.displayEncryptionWarning(); Files.bindKeyboardShortcuts(document, jQuery); - $('#fileList tr').each(function(){ - //little hack to set unescape filenames in attribute - $(this).attr('data-file',decodeURIComponent($(this).attr('data-file'))); - }); + + FileList.postProcessList(); + Files.setupDragAndDrop(); $('#file_action_panel').attr('activeAction', false); - //drag/drop of files - $('#fileList tr td.filename').each(function(i,e){ - if ($(e).parent().data('permissions') & OC.PERMISSION_DELETE) { - $(e).draggable(dragOptions); - } - }); - $('#fileList tr[data-type="dir"] td.filename').each(function(i,e){ - if ($(e).parent().data('permissions') & OC.PERMISSION_CREATE){ - $(e).droppable(folderDropOptions); - } - }); $('div.crumb:not(.last)').droppable(crumbDropOptions); $('ul#apps>li:first-child').data('dir',''); if($('div.crumb').length){ @@ -335,6 +340,9 @@ $(document).ready(function() { resizeBreadcrumbs(true); + // event handlers for breadcrumb items + $('#controls').delegate('.crumb a', 'click', onClickBreadcrumb); + // display storage warnings setTimeout ( "Files.displayStorageWarnings()", 100 ); OC.Notification.setDefault(Files.displayStorageWarnings); @@ -415,10 +423,6 @@ function boolOperationFinished(data, callback) { } } -function updateBreadcrumb(breadcrumbHtml) { - $('p.nav').empty().html(breadcrumbHtml); -} - var createDragShadow = function(event){ //select dragged file var isDragSelected = $(event.target).parents('tr').find('td input:first').prop('checked'); @@ -681,3 +685,9 @@ function checkTrashStatus() { } }); } + +function onClickBreadcrumb(e){ + var $el = $(e.target).closest('.crumb'); + e.preventDefault(); + FileList.changeDirectory(decodeURIComponent($el.data('dir'))); +} diff --git a/apps/files/lib/helper.php b/apps/files/lib/helper.php index 9170c6e3fc..282f0678a9 100644 --- a/apps/files/lib/helper.php +++ b/apps/files/lib/helper.php @@ -45,5 +45,70 @@ class Helper return \OC_Helper::mimetypeIcon($file['mimetype']); } + /** + * Comparator function to sort files alphabetically and have + * the directories appear first + * @param array $a file + * @param array $b file + * @return -1 if $a must come before $b, 1 otherwise + */ + public static function fileCmp($a, $b) { + if ($a['type'] === 'dir' and $b['type'] !== 'dir') { + return -1; + } elseif ($a['type'] !== 'dir' and $b['type'] === 'dir') { + return 1; + } else { + return strnatcasecmp($a['name'], $b['name']); + } + } + /** + * Retrieves the contents of the given directory and + * returns it as a sorted array. + * @param string $dir path to the directory + * @return array of files + */ + public static function getFiles($dir) { + $content = \OC\Files\Filesystem::getDirectoryContent($dir); + $files = array(); + + foreach ($content as $i) { + $i['date'] = \OCP\Util::formatDate($i['mtime']); + if ($i['type'] === 'file') { + $fileinfo = pathinfo($i['name']); + $i['basename'] = $fileinfo['filename']; + if (!empty($fileinfo['extension'])) { + $i['extension'] = '.' . $fileinfo['extension']; + } else { + $i['extension'] = ''; + } + } + $i['directory'] = $dir; + $i['isPreviewAvailable'] = \OCP\Preview::isMimeSupported($i['mimetype']); + $i['icon'] = \OCA\files\lib\Helper::determineIcon($i); + $files[] = $i; + } + + usort($files, array('\OCA\files\lib\Helper', 'fileCmp')); + + return $files; + } + + /** + * Splits the given path into a breadcrumb structure. + * @param string $dir path to process + * @return array where each entry is a hash of the absolute + * directory path and its name + */ + public static function makeBreadcrumb($dir){ + $breadcrumb = array(); + $pathtohere = ''; + foreach (explode('/', $dir) as $i) { + if ($i !== '') { + $pathtohere .= '/' . $i; + $breadcrumb[] = array('dir' => $pathtohere, 'name' => $i); + } + } + return $breadcrumb; + } } diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index 29cb457cd5..85e21380c6 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -2,7 +2,7 @@