Frontend: - The files app list now uses ajax calls to refresh the list. - Added support the browser back button (history API). - Added mask + spinner while loading file list Backend: - Added utility function in core JS for parsing query strings. - Moved file list + breadcrumb template data code to helper functions - Fixed some file paths in trashbin app to be similar to the files apptags/v6.0.0alpha2
@@ -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 ); | |||
} | |||
} | |||
$breadcrumbNav = new OCP\Template( "files", "part.breadcrumb", "" ); | |||
$breadcrumbNav->assign( "breadcrumb", $breadcrumb, false ); | |||
$breadcrumb = \OCA\files\lib\Helper::makeBreadcrumb($dir); | |||
$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)); |
@@ -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; | |||
} | |||
@@ -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(); | |||
} |
@@ -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'); |
@@ -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 = $('<div class="mask transparent"></div>'); | |||
$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(); | |||
}); |
@@ -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'))); | |||
} |
@@ -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; | |||
} | |||
} |
@@ -2,7 +2,7 @@ | |||
<div id="controls"> | |||
<?php print_unescaped($_['breadcrumb']); ?> | |||
<?php if ($_['isCreatable']):?> | |||
<div class="actions <?php if (isset($_['files']) and count($_['files'])==0):?>emptyfolder<?php endif; ?>"> | |||
<div class="actions <?php if (isset($_['files']) and count($_['files'])==0):?>emptycontent<?php endif; ?>"> | |||
<div id="new" class="button"> | |||
<a><?php p($l->t('New'));?></a> | |||
<ul> | |||
@@ -55,9 +55,9 @@ | |||
<input type="hidden" name="permissions" value="<?php p($_['permissions']); ?>" id="permissions"> | |||
</div> | |||
<?php if (isset($_['files']) and $_['isCreatable'] and count($_['files'])==0):?> | |||
<div id="emptycontent"><?php p($l->t('Nothing in here. Upload something!'))?></div> | |||
<?php endif; ?> | |||
<div id="emptycontent" <?php if (!isset($_['files']) or !$_['isCreatable'] or count($_['files']) > 0):?>class="hidden"<?php endif; ?>><?php p($l->t('Nothing in here. Upload something!'))?></div> | |||
<input type="hidden" id="disableSharing" data-status="<?php p($_['disableSharing']); ?>"></input> | |||
<table id="filestable" data-allow-public-upload="<?php p($_['publicUploadEnabled'])?>" data-preview-x="36" data-preview-y="36"> | |||
<thead> | |||
@@ -82,7 +82,7 @@ | |||
<th id="headerDate"> | |||
<span id="modified"><?php p($l->t( 'Modified' )); ?></span> | |||
<?php if ($_['permissions'] & OCP\PERMISSION_DELETE): ?> | |||
<!-- NOTE: Temporary fix to allow unsharing of files in root of Shared folder --> | |||
<!-- NOTE: Temporary fix to allow unsharing of files in root of Shared folder --> | |||
<?php if ($_['dir'] == '/Shared'): ?> | |||
<span class="selectedActions"><a href="" class="delete-selected"> | |||
<?php p($l->t('Unshare'))?> |
@@ -1,4 +1,6 @@ | |||
<input type="hidden" id="disableSharing" data-status="<?php p($_['disableSharing']); ?>"> | |||
<?php $totalfiles = 0; | |||
$totaldirs = 0; | |||
$totalsize = 0; ?> | |||
<?php foreach($_['files'] as $file): | |||
// the bigger the file, the darker the shade of grey; megabytes*2 | |||
$simple_size_color = intval(160-$file['size']/(1024*1024)*2); |
@@ -4,7 +4,7 @@ $(document).ready(function() { | |||
if (typeof OC.Share !== 'undefined' && typeof FileActions !== 'undefined' && !disableSharing) { | |||
$('#fileList').one('fileActionsReady',function(){ | |||
$('#fileList').on('fileActionsReady',function(){ | |||
OC.Share.loadIcons('file'); | |||
}); | |||
@@ -147,6 +147,7 @@ if (isset($path)) { | |||
$tmpl->assign('mimetype', \OC\Files\Filesystem::getMimeType($path)); | |||
$tmpl->assign('fileTarget', basename($linkItem['file_target'])); | |||
$tmpl->assign('dirToken', $linkItem['token']); | |||
$tmpl->assign('disableSharing', true); | |||
$allowPublicUploadEnabled = (bool) ($linkItem['permissions'] & OCP\PERMISSION_CREATE); | |||
if (\OCP\App::isEnabled('files_encryption')) { | |||
$allowPublicUploadEnabled = false; | |||
@@ -206,7 +207,6 @@ if (isset($path)) { | |||
} | |||
$list = new OCP\Template('files', 'part.list', ''); | |||
$list->assign('files', $files); | |||
$list->assign('disableSharing', true); | |||
$list->assign('baseURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&path='); | |||
$list->assign('downloadURL', | |||
OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download&path='); |
@@ -0,0 +1,51 @@ | |||
<?php | |||
// only need filesystem apps | |||
$RUNTIME_APPTYPES=array('filesystem'); | |||
// Init owncloud | |||
OCP\JSON::checkLoggedIn(); | |||
// Load the files | |||
$dir = isset( $_GET['dir'] ) ? $_GET['dir'] : ''; | |||
$doBreadcrumb = isset( $_GET['breadcrumb'] ) ? true : false; | |||
$data = array(); | |||
// Make breadcrumb | |||
if($doBreadcrumb) { | |||
$breadcrumb = \OCA\files_trashbin\lib\Helper::makeBreadcrumb($dir); | |||
$breadcrumbNav = new OCP\Template('files_trashbin', 'part.breadcrumb', ''); | |||
$breadcrumbNav->assign('breadcrumb', $breadcrumb, false); | |||
$breadcrumbNav->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php') . '?dir='); | |||
$breadcrumbNav->assign('home', OCP\Util::linkTo('files', 'index.php')); | |||
$data['breadcrumb'] = $breadcrumbNav->fetchPage(); | |||
} | |||
// make filelist | |||
$files = \OCA\files_trashbin\lib\Helper::getTrashFiles($dir); | |||
if ($files === null){ | |||
header("HTTP/1.0 404 Not Found"); | |||
exit(); | |||
} | |||
$dirlisting = false; | |||
if ($dir && $dir !== '/') { | |||
$dirlisting = true; | |||
} | |||
$encodedDir = \OCP\Util::encodePath($dir); | |||
$list = new OCP\Template('files_trashbin', 'part.list', ''); | |||
$list->assign('files', $files, false); | |||
$list->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php'). '?dir='.$encodedDir); | |||
$list->assign('downloadURL', OCP\Util::linkToRoute('download', array('file' => '/'))); | |||
$list->assign('dirlisting', $dirlisting); | |||
$list->assign('disableDownloadActions', true); | |||
$data['files'] = $list->fetchPage(); | |||
OCP\JSON::success(array('data' => $data)); | |||
@@ -10,92 +10,27 @@ OCP\Util::addScript('files_trashbin', 'disableDefaultActions'); | |||
OCP\Util::addScript('files', 'fileactions'); | |||
$tmpl = new OCP\Template('files_trashbin', 'index', 'user'); | |||
$user = \OCP\User::getUser(); | |||
$view = new OC_Filesystemview('/'.$user.'/files_trashbin/files'); | |||
OCP\Util::addStyle('files', 'files'); | |||
OCP\Util::addScript('files', 'filelist'); | |||
// filelist overrides | |||
OCP\Util::addScript('files_trashbin', 'filelist'); | |||
$dir = isset($_GET['dir']) ? stripslashes($_GET['dir']) : ''; | |||
$result = array(); | |||
if ($dir) { | |||
$dirlisting = true; | |||
$dirContent = $view->opendir($dir); | |||
$i = 0; | |||
if(is_resource($dirContent)) { | |||
while(($entryName = readdir($dirContent)) !== false) { | |||
if (!\OC\Files\Filesystem::isIgnoredDir($entryName)) { | |||
$pos = strpos($dir.'/', '/', 1); | |||
$tmp = substr($dir, 0, $pos); | |||
$pos = strrpos($tmp, '.d'); | |||
$timestamp = substr($tmp, $pos+2); | |||
$result[] = array( | |||
'id' => $entryName, | |||
'timestamp' => $timestamp, | |||
'mime' => $view->getMimeType($dir.'/'.$entryName), | |||
'type' => $view->is_dir($dir.'/'.$entryName) ? 'dir' : 'file', | |||
'location' => $dir, | |||
); | |||
} | |||
} | |||
closedir($dirContent); | |||
} | |||
} else { | |||
$dirlisting = false; | |||
$query = \OC_DB::prepare('SELECT `id`,`location`,`timestamp`,`type`,`mime` FROM `*PREFIX*files_trash` WHERE `user` = ?'); | |||
$result = $query->execute(array($user))->fetchAll(); | |||
} | |||
$files = \OCA\files_trashbin\lib\Helper::getTrashFiles($dir); | |||
$files = array(); | |||
foreach ($result as $r) { | |||
$i = array(); | |||
$i['name'] = $r['id']; | |||
$i['date'] = OCP\Util::formatDate($r['timestamp']); | |||
$i['timestamp'] = $r['timestamp']; | |||
$i['mimetype'] = $r['mime']; | |||
$i['type'] = $r['type']; | |||
if ($i['type'] === 'file') { | |||
$fileinfo = pathinfo($r['id']); | |||
$i['basename'] = $fileinfo['filename']; | |||
$i['extension'] = isset($fileinfo['extension']) ? ('.'.$fileinfo['extension']) : ''; | |||
} | |||
$i['directory'] = $r['location']; | |||
if ($i['directory'] === '/') { | |||
$i['directory'] = ''; | |||
} | |||
$i['permissions'] = OCP\PERMISSION_READ; | |||
$i['isPreviewAvailable'] = \OCP\Preview::isMimeSupported($r['mime']); | |||
$i['icon'] = \OCA\files\lib\Helper::determineIcon($i); | |||
$files[] = $i; | |||
// Redirect if directory does not exist | |||
if ($files === null){ | |||
header('Location: ' . OCP\Util::linkTo('files_trashbin', 'index.php')); | |||
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']); | |||
} | |||
$dirlisting = false; | |||
if ($dir && $dir !== '/') { | |||
$dirlisting = true; | |||
} | |||
usort($files, "fileCmp"); | |||
// Make breadcrumb | |||
$pathtohere = ''; | |||
$breadcrumb = array(); | |||
foreach (explode('/', $dir) as $i) { | |||
if ($i !== '') { | |||
if ( preg_match('/^(.+)\.d[0-9]+$/', $i, $match) ) { | |||
$name = $match[1]; | |||
} else { | |||
$name = $i; | |||
} | |||
$pathtohere .= '/' . $i; | |||
$breadcrumb[] = array('dir' => $pathtohere, 'name' => $name); | |||
} | |||
} | |||
$breadcrumb = \OCA\files_trashbin\lib\Helper::makeBreadcrumb($dir); | |||
$breadcrumbNav = new OCP\Template('files_trashbin', 'part.breadcrumb', ''); | |||
$breadcrumbNav->assign('breadcrumb', $breadcrumb); | |||
@@ -108,7 +43,6 @@ $list->assign('files', $files); | |||
$encodedDir = \OCP\Util::encodePath($dir); | |||
$list->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php'). '?dir='.$encodedDir); | |||
$list->assign('downloadURL', OCP\Util::linkTo('files_trashbin', 'download.php') . '?file='.$encodedDir); | |||
$list->assign('disableSharing', true); | |||
$list->assign('dirlisting', $dirlisting); | |||
$list->assign('disableDownloadActions', true); | |||
@@ -116,6 +50,7 @@ $tmpl->assign('dirlisting', $dirlisting); | |||
$tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage()); | |||
$tmpl->assign('fileList', $list->fetchPage()); | |||
$tmpl->assign('files', $files); | |||
$tmpl->assign('dir', \OC\Files\Filesystem::normalizePath($view->getAbsolutePath())); | |||
$tmpl->assign('dir', $dir); | |||
$tmpl->assign('disableSharing', true); | |||
$tmpl->printPage(); |
@@ -0,0 +1,29 @@ | |||
// override reload with own ajax call | |||
FileList.reload = function(){ | |||
FileList.showMask(); | |||
if (FileList._reloadCall){ | |||
FileList._reloadCall.abort(); | |||
} | |||
$.ajax({ | |||
url: OC.filePath('files_trashbin','ajax','list.php'), | |||
data: { | |||
dir : $('#dir').val(), | |||
breadcrumb: true | |||
}, | |||
error: function(result) { | |||
FileList.reloadCallback(result); | |||
}, | |||
success: function(result) { | |||
FileList.reloadCallback(result); | |||
} | |||
}); | |||
} | |||
FileList.setCurrentDir = function(targetDir, changeUrl){ | |||
$('#dir').val(targetDir); | |||
// Note: IE8 handling ignored for now | |||
if (window.history.pushState && changeUrl !== false){ | |||
url = OC.linkTo('files_trashbin', 'index.php')+"?dir="+ encodeURIComponent(targetDir).replace(/%2F/g, '/'), | |||
window.history.pushState({dir: targetDir}, '', url); | |||
} | |||
} |
@@ -171,9 +171,15 @@ $(document).ready(function() { | |||
action(filename); | |||
} | |||
} | |||
// event handlers for breadcrumb items | |||
$('#controls').delegate('.crumb:not(.home) a', 'click', onClickBreadcrumb); | |||
}); | |||
FileActions.actions.dir = {}; | |||
FileActions.actions.dir = { | |||
// only keep 'Open' action for navigation | |||
'Open': FileActions.actions.dir.Open | |||
}; | |||
}); | |||
function processSelection(){ | |||
@@ -246,3 +252,9 @@ function disableActions() { | |||
$(".action").css("display", "none"); | |||
$(":input:checkbox").css("display", "none"); | |||
} | |||
function onClickBreadcrumb(e){ | |||
var $el = $(e.target).closest('.crumb'); | |||
e.preventDefault(); | |||
FileList.changeDirectory(decodeURIComponent($el.data('dir'))); | |||
} | |||
@@ -0,0 +1,97 @@ | |||
<?php | |||
namespace OCA\files_trashbin\lib; | |||
class Helper | |||
{ | |||
/** | |||
* Retrieves the contents of a trash bin directory. | |||
* @param string $dir path to the directory inside the trashbin | |||
* or empty to retrieve the root of the trashbin | |||
* @return array of files | |||
*/ | |||
public static function getTrashFiles($dir){ | |||
$result = array(); | |||
$user = \OCP\User::getUser(); | |||
if ($dir && $dir !== '/') { | |||
$view = new \OC_Filesystemview('/'.$user.'/files_trashbin/files'); | |||
$dirContent = $view->opendir($dir); | |||
if ($dirContent === false){ | |||
return null; | |||
} | |||
if(is_resource($dirContent)){ | |||
while(($entryName = readdir($dirContent)) !== false) { | |||
if (!\OC\Files\Filesystem::isIgnoredDir($entryName)) { | |||
$pos = strpos($dir.'/', '/', 1); | |||
$tmp = substr($dir, 0, $pos); | |||
$pos = strrpos($tmp, '.d'); | |||
$timestamp = substr($tmp, $pos+2); | |||
$result[] = array( | |||
'id' => $entryName, | |||
'timestamp' => $timestamp, | |||
'mime' => $view->getMimeType($dir.'/'.$entryName), | |||
'type' => $view->is_dir($dir.'/'.$entryName) ? 'dir' : 'file', | |||
'location' => $dir, | |||
); | |||
} | |||
} | |||
closedir($dirContent); | |||
} | |||
} else { | |||
$query = \OC_DB::prepare('SELECT `id`,`location`,`timestamp`,`type`,`mime` FROM `*PREFIX*files_trash` WHERE `user` = ?'); | |||
$result = $query->execute(array($user))->fetchAll(); | |||
} | |||
$files = array(); | |||
foreach ($result as $r) { | |||
$i = array(); | |||
$i['name'] = $r['id']; | |||
$i['date'] = \OCP\Util::formatDate($r['timestamp']); | |||
$i['timestamp'] = $r['timestamp']; | |||
$i['mimetype'] = $r['mime']; | |||
$i['type'] = $r['type']; | |||
if ($i['type'] === 'file') { | |||
$fileinfo = pathinfo($r['id']); | |||
$i['basename'] = $fileinfo['filename']; | |||
$i['extension'] = isset($fileinfo['extension']) ? ('.'.$fileinfo['extension']) : ''; | |||
} | |||
$i['directory'] = $r['location']; | |||
if ($i['directory'] === '/') { | |||
$i['directory'] = ''; | |||
} | |||
$i['permissions'] = \OCP\PERMISSION_READ; | |||
$i['isPreviewAvailable'] = \OCP\Preview::isMimeSupported($r['mime']); | |||
$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){ | |||
// Make breadcrumb | |||
$pathtohere = ''; | |||
$breadcrumb = array(); | |||
foreach (explode('/', $dir) as $i) { | |||
if ($i !== '') { | |||
if ( preg_match('/^(.+)\.d[0-9]+$/', $i, $match) ) { | |||
$name = $match[1]; | |||
} else { | |||
$name = $i; | |||
} | |||
$pathtohere .= '/' . $i; | |||
$breadcrumb[] = array('dir' => $pathtohere, 'name' => $name); | |||
} | |||
} | |||
return $breadcrumb; | |||
} | |||
} |
@@ -9,6 +9,9 @@ | |||
<div id="emptycontent"><?php p($l->t('Nothing in here. Your trash bin is empty!'))?></div> | |||
<?php endif; ?> | |||
<input type="hidden" id="disableSharing" data-status="<?php p($_['disableSharing']); ?>"></input> | |||
<input type="hidden" name="dir" value="<?php p($_['dir']) ?>" id="dir"> | |||
<table id="filestable"> | |||
<thead> | |||
<tr> |
@@ -1,11 +1,11 @@ | |||
<div class="crumb"> | |||
<div class="crumb home"> | |||
<a href="<?php print_unescaped($_['home']); ?>"> | |||
<img src="<?php print_unescaped(OCP\image_path('core', 'places/home.svg'));?>" class="svg" /> | |||
</a> | |||
</div> | |||
<?php if(count($_["breadcrumb"])):?> | |||
<div class="crumb svg" | |||
data-dir='<?php print_unescaped($_['baseURL']); ?>'> | |||
data-dir='/'> | |||
<a href="<?php p($_['baseURL']); ?>"><?php p($l->t("Deleted Files")); ?></a> | |||
</div> | |||
<?php endif;?> |
@@ -1,4 +1,3 @@ | |||
<input type="hidden" id="disableSharing" data-status="<?php p($_['disableSharing']); ?>"> | |||
<?php foreach($_['files'] as $file): | |||
$relative_deleted_date = OCP\relative_modified_date($file['timestamp']); | |||
// the older the file, the brighter the shade of grey; days*14 | |||
@@ -12,7 +11,7 @@ | |||
data-permissions='<?php p($file['permissions']); ?>' | |||
<?php if ( $_['dirlisting'] ): ?> | |||
id="<?php p($file['directory'].'/'.$file['name']);?>" | |||
data-file="<?php p($file['directory'].'/'.$file['name']);?>" | |||
data-file="<?php p($name);?>" | |||
data-timestamp='' | |||
data-dirlisting=1 | |||
<?php else: ?> |
@@ -321,6 +321,38 @@ var OC={ | |||
var date = new Date(1000*mtime); | |||
return date.getDate()+'.'+(date.getMonth()+1)+'.'+date.getFullYear()+', '+date.getHours()+':'+date.getMinutes(); | |||
}, | |||
/** | |||
* Parses a URL query string into a JS map | |||
* @param queryString query string in the format param1=1234¶m2=abcde¶m3=xyz | |||
* @return map containing key/values matching the URL parameters | |||
*/ | |||
parseQueryString:function(queryString){ | |||
var parts, | |||
components, | |||
result = {}, | |||
key, | |||
value; | |||
if (!queryString){ | |||
return null; | |||
} | |||
if (queryString[0] === '?'){ | |||
queryString = queryString.substr(1); | |||
} | |||
parts = queryString.split('&'); | |||
for (var i = 0; i < parts.length; i++){ | |||
components = parts[i].split('='); | |||
if (!components.length){ | |||
continue; | |||
} | |||
key = decodeURIComponent(components[0]); | |||
if (!key){ | |||
continue; | |||
} | |||
value = components[1]; | |||
result[key] = value && decodeURIComponent(value); | |||
} | |||
return result; | |||
}, | |||
/** | |||
* Opens a popup with the setting for an app. | |||
* @param appid String. The ID of the app e.g. 'calendar', 'contacts' or 'files'. |