Merge pull request #6968 from owncloud/files-ajaxload

Ajaxify files list for files + trashbin + public page
This commit is contained in:
Morris Jobke 2014-04-03 22:02:05 +02:00
commit bf7624fb25
42 changed files with 2449 additions and 1378 deletions

View File

@ -7,37 +7,21 @@ OCP\JSON::checkLoggedIn();
$dir = isset( $_GET['dir'] ) ? $_GET['dir'] : '';
$dir = \OC\Files\Filesystem::normalizePath($dir);
$dirInfo = \OC\Files\Filesystem::getFileInfo($dir);
if (!$dirInfo->getType() === 'dir') {
if (!$dirInfo || !$dirInfo->getType() === 'dir') {
header("HTTP/1.0 404 Not Found");
exit();
}
$doBreadcrumb = isset($_GET['breadcrumb']);
$data = array();
$baseUrl = OCP\Util::linkTo('files', 'index.php') . '?dir=';
$permissions = $dirInfo->getPermissions();
// Make breadcrumb
if($doBreadcrumb) {
$breadcrumb = \OCA\Files\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 = \OCA\Files\Helper::getFiles($dir);
$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' => '/')));
$list->assign('isPublic', false);
$data['files'] = $list->fetchPage();
$data['directory'] = $dir;
$data['files'] = \OCA\Files\Helper::formatFileInfos($files);
$data['permissions'] = $permissions;
OCP\JSON::success(array('data' => $data));

View File

@ -112,9 +112,8 @@ if($source) {
}
if($result) {
$meta = \OC\Files\Filesystem::getFileInfo($target);
$mime=$meta['mimetype'];
$id = $meta['fileid'];
$eventSource->send('success', array('mime' => $mime, 'size' => \OC\Files\Filesystem::filesize($target), 'id' => $id, 'etag' => $meta['etag']));
$data = \OCA\Files\Helper::formatFileInfo($meta);
$eventSource->send('success', $data);
} else {
$eventSource->send('error', array('message' => $l10n->t('Error while downloading %s to %s', array($source, $target))));
}
@ -139,16 +138,7 @@ if($source) {
if($success) {
$meta = \OC\Files\Filesystem::getFileInfo($target);
$id = $meta['fileid'];
$mime = $meta['mimetype'];
$size = $meta['size'];
OCP\JSON::success(array('data' => array(
'id' => $id,
'mime' => $mime,
'size' => $size,
'content' => $content,
'etag' => $meta['etag'],
)));
OCP\JSON::success(array('data' => \OCA\Files\Helper::formatFileInfo($meta)));
exit();
}
}

View File

@ -58,8 +58,8 @@ if(\OC\Files\Filesystem::mkdir($target)) {
$path = '/'.$foldername;
}
$meta = \OC\Files\Filesystem::getFileInfo($path);
$id = $meta['fileid'];
OCP\JSON::success(array('data' => array('id' => $id)));
$meta['type'] = 'dir'; // missing ?!
OCP\JSON::success(array('data' => \OCA\Files\Helper::formatFileInfo($meta)));
exit();
}

View File

@ -1,54 +0,0 @@
<?php
OCP\JSON::checkLoggedIn();
\OC::$session->close();
// Load the files
$dir = isset($_GET['dir']) ? $_GET['dir'] : '';
$mimetypes = isset($_GET['mimetypes']) ? json_decode($_GET['mimetypes'], true) : '';
// Clean up duplicates from array and deal with non-array requests
if (is_array($mimetypes)) {
$mimetypes = array_unique($mimetypes);
} elseif (is_null($mimetypes)) {
$mimetypes = array($_GET['mimetypes']);
}
// make filelist
$files = array();
/**
* @var \OCP\Files\FileInfo[] $files
*/
// If a type other than directory is requested first load them.
if ($mimetypes && !in_array('httpd/unix-directory', $mimetypes)) {
$files = array_merge($files, \OC\Files\Filesystem::getDirectoryContent($dir, 'httpd/unix-directory'));
}
if (is_array($mimetypes) && count($mimetypes)) {
foreach ($mimetypes as $mimetype) {
$files = array_merge($files, \OC\Files\Filesystem::getDirectoryContent($dir, $mimetype));
}
} else {
$files = array_merge($files, \OC\Files\Filesystem::getDirectoryContent($dir));
}
// Sort by name
usort($files, array('\OCA\Files\Helper', 'fileCmp'));
$result = array();
foreach ($files as $file) {
$fileData = array();
$fileData['directory'] = $dir;
$fileData['name'] = $file->getName();
$fileData['type'] = $file->getType();
$fileData['path'] = $file['path'];
$fileData['id'] = $file->getId();
$fileData['size'] = $file->getSize();
$fileData['mtime'] = $file->getMtime();
$fileData['mimetype'] = $file->getMimetype();
$fileData['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($file->getMimetype());
$fileData["date"] = OCP\Util::formatDate($file->getMtime());
$fileData['mimetype_icon'] = \OCA\Files\Helper::determineIcon($file);
$result[] = $fileData;
}
OC_JSON::success(array('data' => $result));

View File

@ -20,6 +20,10 @@ if (empty($_POST['dirToken'])) {
die();
}
} else {
// TODO: ideally this code should be in files_sharing/ajax/upload.php
// and the upload/file transfer code needs to be refactored into a utility method
// that could be used there
// return only read permissions for public upload
$allowedPermissions = OCP\PERMISSION_READ;
$public_directory = !empty($_POST['subdir']) ? $_POST['subdir'] : '/';
@ -141,19 +145,14 @@ if (strpos($dir, '..') === false) {
$error = $l->t('The target folder has been moved or deleted.');
$errorCode = 'targetnotfound';
} else {
$result[] = array('status' => 'success',
'mime' => $meta['mimetype'],
'mtime' => $meta['mtime'],
'size' => $meta['size'],
'id' => $meta['fileid'],
'name' => basename($target),
'etag' => $meta['etag'],
'originalname' => $files['tmp_name'][$i],
'uploadMaxFilesize' => $maxUploadFileSize,
'maxHumanFilesize' => $maxHumanFileSize,
'permissions' => $meta['permissions'] & $allowedPermissions,
'directory' => $directory,
);
$data = \OCA\Files\Helper::formatFileInfo($meta);
$data['status'] = 'success';
$data['originalname'] = $files['tmp_name'][$i];
$data['uploadMaxFilesize'] = $maxUploadFileSize;
$data['maxHumanFilesize'] = $maxHumanFileSize;
$data['permissions'] = $meta['permissions'] & $allowedPermissions;
$data['directory'] = $directory;
$result[] = $data;
}
} else {
@ -169,19 +168,15 @@ if (strpos($dir, '..') === false) {
if ($meta === false) {
$error = $l->t('Upload failed. Could not get file info.');
} else {
$result[] = array('status' => 'existserror',
'mime' => $meta['mimetype'],
'mtime' => $meta['mtime'],
'size' => $meta['size'],
'id' => $meta['fileid'],
'name' => basename($target),
'etag' => $meta['etag'],
'originalname' => $files['tmp_name'][$i],
'uploadMaxFilesize' => $maxUploadFileSize,
'maxHumanFilesize' => $maxHumanFileSize,
'permissions' => $meta['permissions'] & $allowedPermissions,
'directory' => $directory,
);
$data = \OCA\Files\Helper::formatFileInfo($meta);
$data['permissions'] = $data['permissions'] & $allowedPermissions;
$data['status'] = 'existserror';
$data['originalname'] = $files['tmp_name'][$i];
$data['uploadMaxFilesize'] = $maxUploadFileSize;
$data['maxHumanFilesize'] = $maxHumanFileSize;
$data['permissions'] = $meta['permissions'] & $allowedPermissions;
$data['directory'] = $directory;
$result[] = $data;
}
}
}

View File

@ -32,6 +32,7 @@ OCP\Util::addscript('files', 'file-upload');
OCP\Util::addscript('files', 'jquery.iframe-transport');
OCP\Util::addscript('files', 'jquery.fileupload');
OCP\Util::addscript('files', 'jquery-visibility');
OCP\Util::addscript('files', 'breadcrumb');
OCP\Util::addscript('files', 'filelist');
OCP\App::setActiveNavigationEntry('files_index');
@ -60,37 +61,12 @@ if ($isIE8 && isset($_GET['dir'])){
exit();
}
$ajaxLoad = false;
$files = array();
$user = OC_User::getUser();
if ($isIE8){
// after the redirect above, the URL will have a format
// like "files#?dir=path" which means that no path was given
// (dir is not set). In that specific case, we don't return any
// files because the client will take care of switching the dir
// to the one from the hash, then ajax-load the initial file list
$files = array();
$ajaxLoad = true;
}
else{
$files = \OCA\Files\Helper::getFiles($dir);
}
$config = \OC::$server->getConfig();
// Make breadcrumb
$breadcrumb = \OCA\Files\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('isPublic', false);
$breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', '');
$breadcrumbNav->assign('breadcrumb', $breadcrumb);
$breadcrumbNav->assign('baseURL', OCP\Util::linkTo('files', 'index.php') . '?dir=');
// needed for share init, permissions will be reloaded
// anyway with ajax load
$permissions = $dirInfo->getPermissions();
// information about storage capacities
@ -112,20 +88,12 @@ if ($trashEnabled) {
$trashEmpty = \OCA\Files_Trashbin\Trashbin::isEmpty($user);
}
$isCreatable = \OC\Files\Filesystem::isCreatable($dir . '/');
$fileHeader = (!isset($files) or count($files) > 0);
$emptyContent = ($isCreatable and !$fileHeader) or $ajaxLoad;
OCP\Util::addscript('files', 'fileactions');
OCP\Util::addscript('files', 'files');
OCP\Util::addscript('files', 'keyboardshortcuts');
$tmpl = new OCP\Template('files', 'index', 'user');
$tmpl->assign('fileList', $list->fetchPage());
$tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage());
$tmpl->assign('dir', $dir);
$tmpl->assign('isCreatable', $isCreatable);
$tmpl->assign('permissions', $permissions);
$tmpl->assign('files', $files);
$tmpl->assign('trash', $trashEnabled);
$tmpl->assign('trashEmpty', $trashEmpty);
$tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); // minimium of freeSpace and uploadLimit
@ -141,8 +109,5 @@ $tmpl->assign("mailNotificationEnabled", $config->getAppValue('core', 'shareapi_
$tmpl->assign("allowShareWithLink", $config->getAppValue('core', 'shareapi_allow_links', 'yes'));
$tmpl->assign("encryptionInitStatus", $encryptionInitStatus);
$tmpl->assign('disableSharing', false);
$tmpl->assign('ajaxLoad', $ajaxLoad);
$tmpl->assign('emptyContent', $emptyContent);
$tmpl->assign('fileHeader', $fileHeader);
$tmpl->printPage();

242
apps/files/js/breadcrumb.js Normal file
View File

@ -0,0 +1,242 @@
/**
* ownCloud
*
* @author Vincent Petry
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* global OC */
/* global SVGSupport, replaceSVG */
(function() {
/**
* Creates an breadcrumb element in the given container
*/
var BreadCrumb = function(options){
this.$el = $('<div class="breadcrumb"></div>');
options = options || {};
if (options.onClick) {
this.onClick = options.onClick;
}
if (options.onDrop) {
this.onDrop = options.onDrop;
}
if (options.getCrumbUrl) {
this.getCrumbUrl = options.getCrumbUrl;
}
};
BreadCrumb.prototype = {
$el: null,
dir: null,
lastWidth: 0,
hiddenBreadcrumbs: 0,
totalWidth: 0,
breadcrumbs: [],
onClick: null,
onDrop: null,
/**
* Sets the directory to be displayed as breadcrumb.
* This will re-render the breadcrumb.
* @param dir path to be displayed as breadcrumb
*/
setDirectory: function(dir) {
dir = dir || '/';
if (dir !== this.dir) {
this.dir = dir;
this.render();
}
},
/**
* Returns the full URL to the given directory
* @param part crumb data as map
* @param index crumb index
* @return full URL
*/
getCrumbUrl: function(part, index) {
return '#';
},
/**
* Renders the breadcrumb elements
*/
render: function() {
var parts = this._makeCrumbs(this.dir || '/');
var $crumb;
this.$el.empty();
this.breadcrumbs = [];
for (var i = 0; i < parts.length; i++) {
var part = parts[i];
var $image;
var $link = $('<a></a>').attr('href', this.getCrumbUrl(part, i));
$link.text(part.name);
$crumb = $('<div class="crumb svg"></div>');
$crumb.append($link);
$crumb.attr('data-dir', part.dir);
if (part.img) {
$image = $('<img class="svg"></img>');
$image.attr('src', part.img);
$link.append($image);
}
this.breadcrumbs.push($crumb);
this.$el.append($crumb);
if (this.onClick) {
$crumb.on('click', this.onClick);
}
}
$crumb.addClass('last');
// in case svg is not supported by the browser we need to execute the fallback mechanism
if (!SVGSupport()) {
replaceSVG();
}
// setup drag and drop
if (this.onDrop) {
this.$el.find('.crumb:not(.last)').droppable({
drop: this.onDrop,
tolerance: 'pointer'
});
}
this._updateTotalWidth();
this.resize($(window).width(), true);
},
/**
* Makes a breadcrumb structure based on the given path
* @param dir path to split into a breadcrumb structure
* @return array of map {dir: path, name: displayName}
*/
_makeCrumbs: function(dir) {
var crumbs = [];
var pathToHere = '';
// trim leading and trailing slashes
dir = dir.replace(/^\/+|\/+$/g, '');
var parts = dir.split('/');
if (dir === '') {
parts = [];
}
// root part
crumbs.push({
dir: '/',
name: '',
img: OC.imagePath('core', 'places/home.svg')
});
for (var i = 0; i < parts.length; i++) {
var part = parts[i];
pathToHere = pathToHere + '/' + part;
crumbs.push({
dir: pathToHere,
name: part
});
}
return crumbs;
},
_updateTotalWidth: function () {
var self = this;
this.lastWidth = 0;
// initialize with some extra space
this.totalWidth = 64;
// FIXME: this class should not know about global elements
if ( $('#navigation').length ) {
this.totalWidth += $('#navigation').get(0).offsetWidth;
}
this.hiddenBreadcrumbs = 0;
for (var i = 0; i < this.breadcrumbs.length; i++ ) {
this.totalWidth += $(this.breadcrumbs[i]).get(0).offsetWidth;
}
$.each($('#controls .actions>div'), function(index, action) {
self.totalWidth += $(action).get(0).offsetWidth;
});
},
/**
* Show/hide breadcrumbs to fit the given width
*/
resize: function (width, firstRun) {
var i, $crumb;
if (width === this.lastWidth) {
return;
}
// window was shrinked since last time or first run ?
if ((width < this.lastWidth || firstRun) && width < this.totalWidth) {
if (this.hiddenBreadcrumbs === 0 && this.breadcrumbs.length > 1) {
// start by hiding the first breadcrumb after home,
// that one will have extra three dots displayed
$crumb = this.breadcrumbs[1];
this.totalWidth -= $crumb.get(0).offsetWidth;
$crumb.find('a').addClass('hidden');
$crumb.append('<span class="ellipsis">...</span>');
this.totalWidth += $crumb.get(0).offsetWidth;
this.hiddenBreadcrumbs = 2;
}
i = this.hiddenBreadcrumbs;
// hide subsequent breadcrumbs if the space is still not enough
while (width < this.totalWidth && i > 1 && i < this.breadcrumbs.length - 1) {
$crumb = this.breadcrumbs[i];
this.totalWidth -= $crumb.get(0).offsetWidth;
$crumb.addClass('hidden');
this.hiddenBreadcrumbs = i;
i++;
}
// window is bigger than last time
} else if (width > this.lastWidth && this.hiddenBreadcrumbs > 0) {
i = this.hiddenBreadcrumbs;
while (width > this.totalWidth && i > 0) {
if (this.hiddenBreadcrumbs === 1) {
// special handling for last one as it has the three dots
$crumb = this.breadcrumbs[1];
if ($crumb) {
this.totalWidth -= $crumb.get(0).offsetWidth;
$crumb.find('.ellipsis').remove();
$crumb.find('a').removeClass('hidden');
this.totalWidth += $crumb.get(0).offsetWidth;
}
} else {
$crumb = this.breadcrumbs[i];
$crumb.removeClass('hidden');
this.totalWidth += $crumb.get(0).offsetWidth;
if (this.totalWidth > width) {
this.totalWidth -= $crumb.get(0).offsetWidth;
$crumb.addClass('hidden');
break;
}
}
i--;
this.hiddenBreadcrumbs = i;
}
}
this.lastWidth = width;
}
};
window.BreadCrumb = BreadCrumb;
})();

View File

@ -180,7 +180,7 @@ OC.Upload = {
},
init: function() {
if ( $('#file_upload_start').exists() && $('#file_upload_start').is(':visible')) {
if ( $('#file_upload_start').exists() ) {
var file_upload_param = {
dropZone: $('#content'), // restrict dropZone to content div
@ -483,28 +483,6 @@ OC.Upload = {
$('#file_upload_start').attr('multiple', 'multiple');
}
//if the breadcrumb is to long, start by replacing foldernames with '...' except for the current folder
var crumb=$('div.crumb').first();
while($('div.controls').height() > 40 && crumb.next('div.crumb').length > 0) {
crumb.children('a').text('...');
crumb = crumb.next('div.crumb');
}
//if that isn't enough, start removing items from the breacrumb except for the current folder and it's parent
var crumb = $('div.crumb').first();
var next = crumb.next('div.crumb');
while($('div.controls').height()>40 && next.next('div.crumb').length > 0) {
crumb.remove();
crumb = next;
next = crumb.next('div.crumb');
}
//still not enough, start shorting down the current folder name
var crumb=$('div.crumb>a').last();
while($('div.controls').height() > 40 && crumb.text().length > 6) {
var text=crumb.text();
text = text.substr(0,text.length-6)+'...';
crumb.text(text);
}
$(document).click(function(ev) {
// do not close when clicking in the dropdown
if ($(ev.target).closest('#new').length){
@ -617,21 +595,7 @@ OC.Upload = {
{dir:$('#dir').val(), filename:name},
function(result) {
if (result.status === 'success') {
var date = new Date();
// TODO: ideally addFile should be able to receive
// all attributes and set them automatically,
// and also auto-load the preview
var tr = FileList.addFile(name, 0, date, false, hidden);
tr.attr('data-size', result.data.size);
tr.attr('data-mime', result.data.mime);
tr.attr('data-id', result.data.id);
tr.attr('data-etag', result.data.etag);
tr.find('.filesize').text(humanFileSize(result.data.size));
var path = getPathForPreview(name);
Files.lazyLoadPreview(path, result.data.mime, function(previewpath) {
tr.find('td.filename').attr('style','background-image:url('+previewpath+')');
}, null, null, result.data.etag);
FileActions.display(tr.find('td.filename'), true);
FileList.add(result.data, {hidden: hidden, insert: true});
} else {
OC.dialogs.alert(result.data.message, t('core', 'Could not create file'));
}
@ -644,10 +608,7 @@ OC.Upload = {
{dir:$('#dir').val(), foldername:name},
function(result) {
if (result.status === 'success') {
var date=new Date();
FileList.addDir(name, 0, date, hidden);
var tr = FileList.findFileEl(name);
tr.attr('data-id', result.data.id);
FileList.add(result.data, {hidden: hidden, insert: true});
} else {
OC.dialogs.alert(result.data.message, t('core', 'Could not create folder'));
}
@ -682,20 +643,10 @@ OC.Upload = {
}
});
eventSource.listen('success',function(data) {
var mime = data.mime;
var size = data.size;
var id = data.id;
var file = data;
$('#uploadprogressbar').fadeOut();
var date = new Date();
FileList.addFile(localName, size, date, false, hidden);
var tr = FileList.findFileEl(localName);
tr.data('mime', mime).data('id', id);
tr.attr('data-id', id);
var path = $('#dir').val()+'/'+localName;
Files.lazyLoadPreview(path, mime, function(previewpath) {
tr.find('td.filename').attr('style', 'background-image:url('+previewpath+')');
}, null, null, data.etag);
FileActions.display(tr.find('td.filename'), true);
FileList.add(file, {hidden: hidden, insert: true});
});
eventSource.listen('error',function(error) {
$('#uploadprogressbar').fadeOut();

View File

@ -8,7 +8,7 @@
*
*/
/* global OC, FileList */
/* global OC, FileList, Files */
/* global trashBinApp */
var FileActions = {
actions: {},
@ -214,7 +214,7 @@ $(document).ready(function () {
FileActions.register(downloadScope, 'Download', OC.PERMISSION_READ, function () {
return OC.imagePath('core', 'actions/download');
}, function (filename) {
var url = FileList.getDownloadUrl(filename);
var url = Files.getDownloadUrl(filename);
if (url) {
OC.redirect(url);
}

File diff suppressed because it is too large Load Diff

View File

@ -161,80 +161,33 @@ var Files = {
});
},
lastWidth: 0,
initBreadCrumbs: function () {
var $controls = $('#controls');
Files.lastWidth = 0;
Files.breadcrumbs = [];
// initialize with some extra space
Files.breadcrumbsWidth = 64;
if ( document.getElementById("navigation") ) {
Files.breadcrumbsWidth += $('#navigation').get(0).offsetWidth;
/**
* Returns the download URL of the given file(s)
* @param filename string or array of file names to download
* @param dir optional directory in which the file name is, defaults to the current directory
*/
getDownloadUrl: function(filename, dir) {
if ($.isArray(filename)) {
filename = JSON.stringify(filename);
}
Files.hiddenBreadcrumbs = 0;
$.each($('.crumb'), function(index, breadcrumb) {
Files.breadcrumbs[index] = breadcrumb;
Files.breadcrumbsWidth += $(breadcrumb).get(0).offsetWidth;
});
$.each($('#controls .actions>div'), function(index, action) {
Files.breadcrumbsWidth += $(action).get(0).offsetWidth;
});
// event handlers for breadcrumb items
$controls.find('.crumb a').on('click', onClickBreadcrumb);
// setup drag and drop
$controls.find('.crumb:not(.last)').droppable(crumbDropOptions);
var params = {
dir: dir || FileList.getCurrentDirectory(),
files: filename
};
return this.getAjaxUrl('download', params);
},
resizeBreadcrumbs: function (width, firstRun) {
if (width !== Files.lastWidth) {
if ((width < Files.lastWidth || firstRun) && width < Files.breadcrumbsWidth) {
if (Files.hiddenBreadcrumbs === 0) {
bc = $(Files.breadcrumbs[1]).get(0);
if (typeof bc != 'undefined') {
Files.breadcrumbsWidth -= bc.offsetWidth;
$(Files.breadcrumbs[1]).find('a').hide();
$(Files.breadcrumbs[1]).append('<span>...</span>');
Files.breadcrumbsWidth += bc.offsetWidth;
Files.hiddenBreadcrumbs = 2;
}
}
var i = Files.hiddenBreadcrumbs;
while (width < Files.breadcrumbsWidth && i > 1 && i < Files.breadcrumbs.length - 1) {
Files.breadcrumbsWidth -= $(Files.breadcrumbs[i]).get(0).offsetWidth;
$(Files.breadcrumbs[i]).hide();
Files.hiddenBreadcrumbs = i;
i++;
}
} else if (width > Files.lastWidth && Files.hiddenBreadcrumbs > 0) {
var i = Files.hiddenBreadcrumbs;
while (width > Files.breadcrumbsWidth && i > 0) {
if (Files.hiddenBreadcrumbs === 1) {
Files.breadcrumbsWidth -= $(Files.breadcrumbs[1]).get(0).offsetWidth;
$(Files.breadcrumbs[1]).find('span').remove();
$(Files.breadcrumbs[1]).find('a').show();
Files.breadcrumbsWidth += $(Files.breadcrumbs[1]).get(0).offsetWidth;
} else {
$(Files.breadcrumbs[i]).show();
Files.breadcrumbsWidth += $(Files.breadcrumbs[i]).get(0).offsetWidth;
if (Files.breadcrumbsWidth > width) {
Files.breadcrumbsWidth -= $(Files.breadcrumbs[i]).get(0).offsetWidth;
$(Files.breadcrumbs[i]).hide();
break;
}
}
i--;
Files.hiddenBreadcrumbs = i;
}
}
Files.lastWidth = width;
/**
* Returns the ajax URL for a given action
* @param action action string
* @param params optional params map
*/
getAjaxUrl: function(action, params) {
var q = '';
if (params) {
q = '?' + OC.buildQueryString(params);
}
return OC.filePath('files', 'ajax', action + '.php') + q;
}
};
$(document).ready(function() {
@ -245,14 +198,10 @@ $(document).ready(function() {
Files.displayEncryptionWarning();
Files.bindKeyboardShortcuts(document, jQuery);
FileList.postProcessList();
Files.setupDragAndDrop();
$('#file_action_panel').attr('activeAction', false);
// allow dropping on the "files" app icon
$('ul#apps li:first-child').data('dir','').droppable(crumbDropOptions);
// Triggers invisible file input
$('#upload a').on('click', function() {
$(this).parent().children('#file_upload_start').trigger('click');
@ -311,7 +260,7 @@ $(document).ready(function() {
var filename=$(this).parent().parent().attr('data-file');
var tr = FileList.findFileEl(filename);
var renaming=tr.data('renaming');
if (!renaming && !FileList.isLoading(filename)) {
if (!renaming) {
FileActions.currentFile = $(this).parent();
var mime=FileActions.getCurrentMimeType();
var type=FileActions.getCurrentType();
@ -377,15 +326,15 @@ $(document).ready(function() {
dir = OC.dirname(dir) || '/';
}
else {
files = getSelectedFilesTrash('name');
files = Files.getSelectedFiles('name');
}
OC.Notification.show(t('files','Your download is being prepared. This might take some time if the files are big.'));
OC.redirect(FileList.getDownloadUrl(files, dir));
OC.redirect(Files.getDownloadUrl(files, dir));
return false;
});
$('.delete-selected').click(function(event) {
var files=getSelectedFilesTrash('name');
var files = Files.getSelectedFiles('name');
event.preventDefault();
if (FileList.isAllSelected()) {
files = null;
@ -403,16 +352,6 @@ $(document).ready(function() {
//do a background scan if needed
scanFiles();
Files.initBreadCrumbs();
$(window).resize(function() {
var width = $(this).width();
Files.resizeBreadcrumbs(width, false);
});
var width = $(this).width();
Files.resizeBreadcrumbs(width, true);
// display storage warnings
setTimeout(Files.displayStorageWarnings, 100);
OC.Notification.setDefault(Files.displayStorageWarnings);
@ -503,7 +442,7 @@ var createDragShadow = function(event) {
$(event.target).parents('tr').find('td input:first').prop('checked',true);
}
var selectedFiles = getSelectedFilesTrash();
var selectedFiles = Files.getSelectedFiles();
if (!isDragSelected && selectedFiles.length === 1) {
//revert the selection
@ -619,52 +558,8 @@ var folderDropOptions={
tolerance: 'pointer'
};
var crumbDropOptions={
drop: function( event, ui ) {
var target=$(this).data('dir');
var dir = $('#dir').val();
while(dir.substr(0,1) === '/') {//remove extra leading /'s
dir=dir.substr(1);
}
dir = '/' + dir;
if (dir.substr(-1,1) !== '/') {
dir = dir + '/';
}
if (target === dir || target+'/' === dir) {
return;
}
var files = ui.helper.find('tr');
$(files).each(function(i,row) {
var dir = $(row).data('dir');
var file = $(row).data('filename');
//slapdash selector, tracking down our original element that the clone budded off of.
var origin = $('tr[data-id=' + $(row).data('origin') + ']');
var td = origin.children('td.filename');
var oldBackgroundImage = td.css('background-image');
td.css('background-image', 'url('+ OC.imagePath('core', 'loading.gif') + ')');
$.post(OC.filePath('files', 'ajax', 'move.php'), { dir: dir, file: file, target: target }, function(result) {
if (result) {
if (result.status === 'success') {
FileList.remove(file);
procesSelection();
$('#notification').hide();
} else {
$('#notification').hide();
$('#notification').text(result.data.message);
$('#notification').fadeIn();
}
} else {
OC.dialogs.alert(t('files', 'Error moving file'), t('files', 'Error'));
}
td.css('background-image', oldBackgroundImage);
});
});
},
tolerance: 'pointer'
};
function procesSelection() {
var selected = getSelectedFilesTrash();
var selected = Files.getSelectedFiles();
var selectedFiles = selected.filter(function(el) {
return el.type==='file';
});
@ -714,7 +609,7 @@ function procesSelection() {
* if property is set, an array with that property for each file is returnd
* if it's ommited an array of objects with all properties is returned
*/
function getSelectedFilesTrash(property) {
Files.getSelectedFiles = function(property) {
var elements=$('td.filename input:checkbox:checked').parent().parent();
var files=[];
elements.each(function(i,element) {
@ -755,25 +650,30 @@ function getPathForPreview(name) {
return path;
}
/**
* Generates a preview URL based on the URL space.
* @param urlSpec map with {x: width, y: height, file: file path}
* @return preview URL
*/
Files.generatePreviewUrl = function(urlSpec) {
urlSpec = urlSpec || {};
if (!urlSpec.x) {
urlSpec.x = $('#filestable').data('preview-x');
}
if (!urlSpec.y) {
urlSpec.y = $('#filestable').data('preview-y');
}
urlSpec.forceIcon = 0;
return OC.generateUrl('/core/preview.png?') + $.param(urlSpec);
}
Files.lazyLoadPreview = function(path, mime, ready, width, height, etag) {
// get mime icon url
Files.getMimeIcon(mime, function(iconURL) {
var urlSpec = {};
var previewURL;
urlSpec = {};
ready(iconURL); // set mimeicon URL
// now try getting a preview thumbnail URL
if ( ! width ) {
width = $('#filestable').data('preview-x');
}
if ( ! height ) {
height = $('#filestable').data('preview-y');
}
// note: the order of arguments must match the one
// from the server's template so that the browser
// knows it's the same file for caching
urlSpec.x = width;
urlSpec.y = height;
urlSpec.file = Files.fixPath(path);
if (etag){
@ -784,15 +684,9 @@ Files.lazyLoadPreview = function(path, mime, ready, width, height, etag) {
console.warn('Files.lazyLoadPreview(): missing etag argument');
}
if ( $('#isPublic').length ) {
urlSpec.t = $('#dirToken').val();
previewURL = OC.generateUrl('/publicpreview.png?') + $.param(urlSpec);
} else {
previewURL = OC.generateUrl('/core/preview.png?') + $.param(urlSpec);
}
previewURL = Files.generatePreviewUrl(urlSpec);
previewURL = previewURL.replace('(', '%28');
previewURL = previewURL.replace(')', '%29');
previewURL += '&forceIcon=0';
// preload image to prevent delay
// this will make the browser cache the image
@ -841,14 +735,8 @@ function checkTrashStatus() {
});
}
function onClickBreadcrumb(e) {
var $el = $(e.target).closest('.crumb'),
$targetDir = $el.data('dir'),
isPublic = !!$('#isPublic').val();
if ($targetDir !== undefined && !isPublic) {
e.preventDefault();
FileList.changeDirectory(decodeURIComponent($targetDir));
}
// override core's fileDownloadPath (legacy)
function fileDownloadPath(dir, file) {
return Files.getDownloadUrl(file, dir);
}

View File

@ -84,25 +84,7 @@ class App {
) {
// successful rename
$meta = $this->view->getFileInfo($dir . '/' . $newname);
if ($meta['mimetype'] === 'httpd/unix-directory') {
$meta['type'] = 'dir';
}
else {
$meta['type'] = 'file';
}
// these need to be set for determineIcon()
$meta['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($meta['mimetype']);
$meta['directory'] = $dir;
$fileinfo = array(
'id' => $meta['fileid'],
'mime' => $meta['mimetype'],
'size' => $meta['size'],
'etag' => $meta['etag'],
'directory' => $meta['directory'],
'name' => $newname,
'isPreviewAvailable' => $meta['isPreviewAvailable'],
'icon' => \OCA\Files\Helper::determineIcon($meta)
);
$fileinfo = \OCA\Files\Helper::formatFileInfo($meta);
$result['success'] = true;
$result['data'] = $fileinfo;
} else {

View File

@ -19,11 +19,17 @@ class Helper
'usedSpacePercent' => (int)$storageInfo['relative']);
}
/**
* Determine icon for a given file
*
* @param \OC\Files\FileInfo $file file info
* @return string icon URL
*/
public static function determineIcon($file) {
if($file['type'] === 'dir') {
$dir = $file['directory'];
$icon = \OC_Helper::mimetypeIcon('dir');
$absPath = \OC\Files\Filesystem::getView()->getAbsolutePath($dir.'/'.$file['name']);
$absPath = $file->getPath();
$mount = \OC\Files\Filesystem::getMountManager()->find($absPath);
if (!is_null($mount)) {
$sid = $mount->getStorageId();
@ -38,11 +44,7 @@ class Helper
}
}
}else{
if($file['isPreviewAvailable']) {
$pathForPreview = $file['directory'] . '/' . $file['name'];
return \OC_Helper::previewIcon($pathForPreview) . '&c=' . $file['etag'];
}
$icon = \OC_Helper::mimetypeIcon($file['mimetype']);
$icon = \OC_Helper::mimetypeIcon($file->getMimetype());
}
return substr($icon, 0, -3) . 'svg';
@ -69,52 +71,58 @@ class Helper
}
/**
* 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
* Formats the file info to be returned as JSON to the client.
*
* @param \OCP\Files\FileInfo file info
* @return array formatted file info
*/
public static function getFiles($dir) {
$content = \OC\Files\Filesystem::getDirectoryContent($dir);
$files = array();
public static function formatFileInfo($i) {
$entry = 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'] = \OC::$server->getPreviewManager()->isMimeSupported($i['mimetype']);
$i['icon'] = \OCA\Files\Helper::determineIcon($i);
$files[] = $i;
$entry['id'] = $i['fileid'];
$entry['date'] = \OCP\Util::formatDate($i['mtime']);
$entry['mtime'] = $i['mtime'] * 1000;
// only pick out the needed attributes
$entry['icon'] = \OCA\Files\Helper::determineIcon($i);
if (\OC::$server->getPreviewManager()->isMimeSupported($i['mimetype'])) {
$entry['isPreviewAvailable'] = true;
}
$entry['name'] = $i['name'];
$entry['permissions'] = $i['permissions'];
$entry['mimetype'] = $i['mimetype'];
$entry['size'] = $i['size'];
$entry['type'] = $i['type'];
$entry['etag'] = $i['etag'];
if (isset($i['displayname_owner'])) {
$entry['shareOwner'] = $i['displayname_owner'];
}
return $entry;
}
usort($files, array('\OCA\Files\Helper', 'fileCmp'));
/**
* Format file info for JSON
* @param \OCP\Files\FileInfo[] $fileInfos file infos
*/
public static function formatFileInfos($fileInfos) {
$files = array();
foreach ($fileInfos as $i) {
$files[] = self::formatFileInfo($i);
}
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
* Retrieves the contents of the given directory and
* returns it as a sorted array of FileInfo.
*
* @param string $dir path to the directory
* @return \OCP\Files\FileInfo[] files
*/
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;
public static function getFiles($dir) {
$content = \OC\Files\Filesystem::getDirectoryContent($dir);
usort($content, array('\OCA\Files\Helper', 'fileCmp'));
return $content;
}
}

View File

@ -1,6 +1,5 @@
<div id="controls">
<?php print_unescaped($_['breadcrumb']); ?>
<div class="actions creatable <?php if (!$_['isCreatable']):?>hidden<?php endif; ?>">
<div class="actions creatable hidden">
<?php if(!isset($_['dirToken'])):?>
<div id="new" class="button">
<a><?php p($l->t('New'));?></a>
@ -48,20 +47,20 @@
</div>
</div>
<div id="file_action_panel"></div>
<div class="notCreatable notPublic <?php if ($_['isCreatable'] or $_['isPublic'] ):?>hidden<?php endif; ?>">
<div class="notCreatable notPublic hidden">
<?php p($l->t('You dont have permission to upload or create files here'))?>
</div>
<input type="hidden" name="permissions" value="<?php p($_['permissions']); ?>" id="permissions">
</div>
<div id="emptycontent" <?php if (!$_['emptyContent']):?>class="hidden"<?php endif; ?>><?php p($l->t('Nothing in here. Upload something!'))?></div>
<div id="emptycontent" class="hidden"><?php p($l->t('Nothing in here. Upload something!'))?></div>
<input type="hidden" id="disableSharing" data-status="<?php p($_['disableSharing']); ?>" />
<table id="filestable" data-allow-public-upload="<?php p($_['publicUploadEnabled'])?>" data-preview-x="36" data-preview-y="36">
<thead>
<tr>
<th <?php if (!$_['fileHeader']):?>class="hidden"<?php endif; ?> id='headerName'>
<th class="hidden" id='headerName'>
<div id="headerName-container">
<input type="checkbox" id="select_all" />
<label for="select_all"></label>
@ -77,8 +76,8 @@
</span>
</div>
</th>
<th <?php if (!$_['fileHeader']):?>class="hidden"<?php endif; ?> id="headerSize"><?php p($l->t('Size')); ?></th>
<th <?php if (!$_['fileHeader']):?>class="hidden"<?php endif; ?> id="headerDate">
<th class="hidden" id="headerSize"><?php p($l->t('Size')); ?></th>
<th class="hidden" id="headerDate">
<span id="modified"><?php p($l->t( 'Modified' )); ?></span>
<?php if ($_['permissions'] & OCP\PERMISSION_DELETE): ?>
<span class="selectedActions"><a href="" class="delete-selected">
@ -91,7 +90,6 @@
</tr>
</thead>
<tbody id="fileList">
<?php print_unescaped($_['fileList']); ?>
</tbody>
</table>
<div id="editor"></div><!-- FIXME Do not use this div in your app! It is deprecated and will be removed in the future! -->
@ -111,7 +109,6 @@
<!-- config hints for javascript -->
<input type="hidden" name="filesApp" id="filesApp" value="1" />
<input type="hidden" name="ajaxLoad" id="ajaxLoad" value="<?php p($_['ajaxLoad']); ?>" />
<input type="hidden" name="allowZipDownload" id="allowZipDownload" value="<?php p($_['allowZipDownload']); ?>" />
<input type="hidden" name="usedSpacePercent" id="usedSpacePercent" value="<?php p($_['usedSpacePercent']); ?>" />
<?php if (!$_['isPublic']) :?>

View File

@ -1,17 +0,0 @@
<div class="crumb svg <?php if(!count($_["breadcrumb"])) p('last');?>" data-dir=''>
<a href="<?php print_unescaped($_['baseURL']); ?>">
<?php if(isset($_['rootBreadCrumb'])):
echo $_['rootBreadCrumb'];
else:?>
<img src="<?php print_unescaped(OCP\image_path('core', 'places/home.svg'));?>" class="svg" />
<?php endif;?>
</a>
</div>
<?php for($i=0; $i<count($_["breadcrumb"]); $i++):
$crumb = $_["breadcrumb"][$i];
$dir = \OCP\Util::encodePath($crumb["dir"]); ?>
<div class="crumb <?php if($i == count($_["breadcrumb"])-1) p('last');?> svg"
data-dir='<?php p($dir);?>'>
<a href="<?php p($_['baseURL'].$dir); ?>"><?php p($crumb["name"]); ?></a>
</div>
<?php endfor;

View File

@ -1,67 +0,0 @@
<?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);
if($simple_size_color<0) $simple_size_color = 0;
$relative_modified_date = OCP\relative_modified_date($file['mtime']);
// the older the file, the brighter the shade of grey; days*14
$relative_date_color = round((time()-$file['mtime'])/60/60/24*14);
if($relative_date_color>160) $relative_date_color = 160;
$name = \OCP\Util::encodePath($file['name']);
$directory = \OCP\Util::encodePath($file['directory']); ?>
<tr data-id="<?php p($file['fileid']); ?>"
data-file="<?php p($name);?>"
data-type="<?php ($file['type'] == 'dir')?p('dir'):p('file')?>"
data-mime="<?php p($file['mimetype'])?>"
data-size="<?php p($file['size']);?>"
data-etag="<?php p($file['etag']);?>"
data-permissions="<?php p($file['permissions']); ?>"
<?php if(isset($file['displayname_owner'])): ?>
data-share-owner="<?php p($file['displayname_owner']) ?>"
<?php endif; ?>
>
<?php if(isset($file['isPreviewAvailable']) and $file['isPreviewAvailable']): ?>
<td class="filename svg preview-icon"
<?php else: ?>
<td class="filename svg"
<?php endif; ?>
style="background-image:url(<?php print_unescaped($file['icon']); ?>)"
>
<?php if(!isset($_['readonly']) || !$_['readonly']): ?>
<input id="select-<?php p($file['fileid']); ?>" type="checkbox" />
<label for="select-<?php p($file['fileid']); ?>"></label>
<?php endif; ?>
<?php if($file['type'] == 'dir'): ?>
<a class="name" href="<?php p(rtrim($_['baseURL'],'/').'/'.trim($directory,'/').'/'.$name); ?>" title="">
<span class="nametext">
<?php print_unescaped(htmlspecialchars($file['name']));?>
</span>
<span class="uploadtext" currentUploads="0">
</span>
</a>
<?php else: ?>
<a class="name" href="<?php p(rtrim($_['downloadURL'],'/').'/'.trim($directory,'/').'/'.$name); ?>">
<label class="filetext" title="" for="select-<?php p($file['fileid']); ?>"></label>
<span class="nametext"><?php print_unescaped(htmlspecialchars($file['basename']));?><span class='extension'><?php p($file['extension']);?></span></span>
</a>
<?php endif; ?>
</td>
<td class="filesize"
style="color:rgb(<?php p($simple_size_color.','.$simple_size_color.','.$simple_size_color) ?>)">
<?php print_unescaped(OCP\human_file_size($file['size'])); ?>
</td>
<td class="date">
<span class="modified"
title="<?php p($file['date']); ?>"
style="color:rgb(<?php p($relative_date_color.','
.$relative_date_color.','
.$relative_date_color) ?>)">
<?php p($relative_modified_date); ?>
</span>
</td>
</tr>
<?php endforeach;

View File

@ -92,28 +92,32 @@ class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase {
$this->viewMock->expects($this->any())
->method('getFileInfo')
->will($this->returnValue(array(
->will($this->returnValue(new \OC\Files\FileInfo(
'/test',
null,
'/test',
array(
'fileid' => 123,
'type' => 'dir',
'mimetype' => 'httpd/unix-directory',
'mtime' => 0,
'permissions' => 31,
'size' => 18,
'etag' => 'abcdef',
'directory' => '/',
'name' => 'new_name',
)));
))));
$result = $this->files->rename($dir, $oldname, $newname);
$this->assertTrue($result['success']);
$this->assertEquals(123, $result['data']['id']);
$this->assertEquals('new_name', $result['data']['name']);
$this->assertEquals('/test', $result['data']['directory']);
$this->assertEquals(18, $result['data']['size']);
$this->assertEquals('httpd/unix-directory', $result['data']['mime']);
$this->assertEquals('httpd/unix-directory', $result['data']['mimetype']);
$icon = \OC_Helper::mimetypeIcon('dir');
$icon = substr($icon, 0, -3) . 'svg';
$this->assertEquals($icon, $result['data']['icon']);
$this->assertFalse($result['data']['isPreviewAvailable']);
}
/**
@ -148,29 +152,33 @@ class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase {
$this->viewMock->expects($this->any())
->method('getFileInfo')
->will($this->returnValue(array(
->will($this->returnValue(new \OC\Files\FileInfo(
'/',
null,
'/',
array(
'fileid' => 123,
'type' => 'dir',
'mimetype' => 'httpd/unix-directory',
'mtime' => 0,
'permissions' => 31,
'size' => 18,
'etag' => 'abcdef',
'directory' => '/',
'name' => 'new_name',
)));
))));
$result = $this->files->rename($dir, $oldname, $newname);
$this->assertTrue($result['success']);
$this->assertEquals(123, $result['data']['id']);
$this->assertEquals('newname', $result['data']['name']);
$this->assertEquals('/', $result['data']['directory']);
$this->assertEquals('new_name', $result['data']['name']);
$this->assertEquals(18, $result['data']['size']);
$this->assertEquals('httpd/unix-directory', $result['data']['mime']);
$this->assertEquals('httpd/unix-directory', $result['data']['mimetype']);
$this->assertEquals('abcdef', $result['data']['etag']);
$icon = \OC_Helper::mimetypeIcon('dir');
$icon = substr($icon, 0, -3) . 'svg';
$this->assertEquals($icon, $result['data']['icon']);
$this->assertFalse($result['data']['isPreviewAvailable']);
}
/**

View File

@ -0,0 +1,248 @@
/**
* ownCloud
*
* @author Vincent Petry
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* global BreadCrumb */
describe('BreadCrumb tests', function() {
describe('Rendering', function() {
var bc;
beforeEach(function() {
bc = new BreadCrumb({
getCrumbUrl: function(part, index) {
// for testing purposes
return part.dir + '#' + index;
}
});
});
afterEach(function() {
bc = null;
});
it('Renders its own container', function() {
bc.render();
expect(bc.$el.hasClass('breadcrumb')).toEqual(true);
});
it('Renders root by default', function() {
var $crumbs;
bc.render();
$crumbs = bc.$el.find('.crumb');
expect($crumbs.length).toEqual(1);
expect($crumbs.eq(0).find('a').attr('href')).toEqual('/#0');
expect($crumbs.eq(0).find('img').length).toEqual(1);
expect($crumbs.eq(0).attr('data-dir')).toEqual('/');
});
it('Renders root when switching to root', function() {
var $crumbs;
bc.setDirectory('/somedir');
bc.setDirectory('/');
$crumbs = bc.$el.find('.crumb');
expect($crumbs.length).toEqual(1);
expect($crumbs.eq(0).attr('data-dir')).toEqual('/');
});
it('Renders last crumb with "last" class', function() {
bc.setDirectory('/abc/def');
expect(bc.$el.find('.crumb:last').hasClass('last')).toEqual(true);
});
it('Renders single path section', function() {
var $crumbs;
bc.setDirectory('/somedir');
$crumbs = bc.$el.find('.crumb');
expect($crumbs.length).toEqual(2);
expect($crumbs.eq(0).find('a').attr('href')).toEqual('/#0');
expect($crumbs.eq(0).find('img').length).toEqual(1);
expect($crumbs.eq(0).attr('data-dir')).toEqual('/');
expect($crumbs.eq(1).find('a').attr('href')).toEqual('/somedir#1');
expect($crumbs.eq(1).find('img').length).toEqual(0);
expect($crumbs.eq(1).attr('data-dir')).toEqual('/somedir');
});
it('Renders multiple path sections and special chars', function() {
var $crumbs;
bc.setDirectory('/somedir/with space/abc');
$crumbs = bc.$el.find('.crumb');
expect($crumbs.length).toEqual(4);
expect($crumbs.eq(0).find('a').attr('href')).toEqual('/#0');
expect($crumbs.eq(0).find('img').length).toEqual(1);
expect($crumbs.eq(0).attr('data-dir')).toEqual('/');
expect($crumbs.eq(1).find('a').attr('href')).toEqual('/somedir#1');
expect($crumbs.eq(1).find('img').length).toEqual(0);
expect($crumbs.eq(1).attr('data-dir')).toEqual('/somedir');
expect($crumbs.eq(2).find('a').attr('href')).toEqual('/somedir/with space#2');
expect($crumbs.eq(2).find('img').length).toEqual(0);
expect($crumbs.eq(2).attr('data-dir')).toEqual('/somedir/with space');
expect($crumbs.eq(3).find('a').attr('href')).toEqual('/somedir/with space/abc#3');
expect($crumbs.eq(3).find('img').length).toEqual(0);
expect($crumbs.eq(3).attr('data-dir')).toEqual('/somedir/with space/abc');
});
});
describe('Events', function() {
it('Calls onClick handler when clicking on a crumb', function() {
var handler = sinon.stub();
var bc = new BreadCrumb({
onClick: handler
});
bc.setDirectory('/one/two/three/four');
bc.$el.find('.crumb:eq(3)').click();
expect(handler.calledOnce).toEqual(true);
expect(handler.getCall(0).thisValue).toEqual(bc.$el.find('.crumb').get(3));
handler.reset();
bc.$el.find('.crumb:eq(0) a').click();
expect(handler.calledOnce).toEqual(true);
expect(handler.getCall(0).thisValue).toEqual(bc.$el.find('.crumb').get(0));
});
it('Calls onDrop handler when dropping on a crumb', function() {
var droppableStub = sinon.stub($.fn, 'droppable');
var handler = sinon.stub();
var bc = new BreadCrumb({
onDrop: handler
});
bc.setDirectory('/one/two/three/four');
expect(droppableStub.calledOnce).toEqual(true);
expect(droppableStub.getCall(0).args[0].drop).toBeDefined();
// simulate drop
droppableStub.getCall(0).args[0].drop({dummy: true});
expect(handler.calledOnce).toEqual(true);
expect(handler.getCall(0).args[0]).toEqual({dummy: true});
droppableStub.restore();
});
});
describe('Resizing', function() {
var bc, widthStub, dummyDir,
oldUpdateTotalWidth;
beforeEach(function() {
dummyDir = '/short name/longer name/looooooooooooonger/even longer long long long longer long/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/last one';
oldUpdateTotalWidth = BreadCrumb.prototype._updateTotalWidth;
BreadCrumb.prototype._updateTotalWidth = function() {
// need to set display:block for correct offsetWidth (no CSS loaded here)
$('div.crumb').css({
'display': 'block',
'float': 'left'
});
return oldUpdateTotalWidth.apply(this, arguments);
};
bc = new BreadCrumb();
widthStub = sinon.stub($.fn, 'width');
// append dummy navigation and controls
// as they are currently used for measurements
$('#testArea').append(
'<div id="navigation" style="width: 80px"></div>',
'<div id="controls"></div>'
);
// make sure we know the test screen width
$('#testArea').css('width', 1280);
// use test area as we need it for measurements
$('#controls').append(bc.$el);
$('#controls').append('<div class="actions"><div>Dummy action with a given width</div></div>');
});
afterEach(function() {
BreadCrumb.prototype._updateTotalWidth = oldUpdateTotalWidth;
widthStub.restore();
bc = null;
});
it('Hides breadcrumbs to fit window', function() {
var $crumbs;
widthStub.returns(500);
// triggers resize implicitly
bc.setDirectory(dummyDir);
$crumbs = bc.$el.find('.crumb');
// first one is always visible
expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
// second one has ellipsis
expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
expect($crumbs.eq(1).find('.ellipsis').length).toEqual(1);
// there is only one ellipsis in total
expect($crumbs.find('.ellipsis').length).toEqual(1);
// subsequent elements are hidden
expect($crumbs.eq(2).hasClass('hidden')).toEqual(true);
expect($crumbs.eq(3).hasClass('hidden')).toEqual(true);
expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
});
it('Updates ellipsis on window size increase', function() {
var $crumbs;
widthStub.returns(500);
// triggers resize implicitly
bc.setDirectory(dummyDir);
$crumbs = bc.$el.find('.crumb');
// simulate increase
$('#testArea').css('width', 1800);
bc.resize(1800);
// first one is always visible
expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
// second one has ellipsis
expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
expect($crumbs.eq(1).find('.ellipsis').length).toEqual(1);
// there is only one ellipsis in total
expect($crumbs.find('.ellipsis').length).toEqual(1);
// subsequent elements are hidden
expect($crumbs.eq(2).hasClass('hidden')).toEqual(true);
expect($crumbs.eq(3).hasClass('hidden')).toEqual(true);
expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
// the rest is visible
expect($crumbs.eq(5).hasClass('hidden')).toEqual(false);
expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
});
it('Updates ellipsis on window size decrease', function() {
var $crumbs;
$('#testArea').css('width', 2000);
widthStub.returns(2000);
// triggers resize implicitly
bc.setDirectory(dummyDir);
$crumbs = bc.$el.find('.crumb');
// simulate decrease
bc.resize(500);
$('#testArea').css('width', 500);
// first one is always visible
expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
// second one has ellipsis
expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
expect($crumbs.eq(1).find('.ellipsis').length).toEqual(1);
// there is only one ellipsis in total
expect($crumbs.find('.ellipsis').length).toEqual(1);
// subsequent elements are hidden
expect($crumbs.eq(2).hasClass('hidden')).toEqual(true);
expect($crumbs.eq(3).hasClass('hidden')).toEqual(true);
expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
// the rest is visible
expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
});
});
});

View File

@ -22,6 +22,7 @@
/* global OC, FileActions, FileList */
describe('FileActions tests', function() {
var $filesTable;
beforeEach(function() {
// init horrible parameters
var $body = $('body');
@ -34,17 +35,20 @@ describe('FileActions tests', function() {
$('#dir, #permissions, #filestable').remove();
});
it('calling display() sets file actions', function() {
// note: download_url is actually the link target, not the actual download URL...
var $tr = FileList.addFile('testName.txt', 1234, new Date(), false, false, {download_url: 'test/download/url'});
var fileData = {
id: 18,
type: 'file',
name: 'testName.txt',
mimetype: 'plain/text',
size: '1234',
etag: 'a01234c',
mtime: '123456'
};
// no actions before call
expect($tr.find('.action.action-download').length).toEqual(0);
expect($tr.find('.action.action-rename').length).toEqual(0);
expect($tr.find('.action.delete').length).toEqual(0);
// note: FileActions.display() is called implicitly
var $tr = FileList.add(fileData);
FileActions.display($tr.find('td.filename'), true);
// actions defined after cal
// actions defined after call
expect($tr.find('.action.action-download').length).toEqual(1);
expect($tr.find('.action.action-download').attr('data-action')).toEqual('Download');
expect($tr.find('.nametext .action.action-rename').length).toEqual(1);
@ -52,7 +56,16 @@ describe('FileActions tests', function() {
expect($tr.find('.action.delete').length).toEqual(1);
});
it('calling display() twice correctly replaces file actions', function() {
var $tr = FileList.addFile('testName.txt', 1234, new Date(), false, false, {download_url: 'test/download/url'});
var fileData = {
id: 18,
type: 'file',
name: 'testName.txt',
mimetype: 'plain/text',
size: '1234',
etag: 'a01234c',
mtime: '123456'
};
var $tr = FileList.add(fileData);
FileActions.display($tr.find('td.filename'), true);
FileActions.display($tr.find('td.filename'), true);
@ -64,19 +77,36 @@ describe('FileActions tests', function() {
});
it('redirects to download URL when clicking download', function() {
var redirectStub = sinon.stub(OC, 'redirect');
// note: download_url is actually the link target, not the actual download URL...
var $tr = FileList.addFile('test download File.txt', 1234, new Date(), false, false, {download_url: 'test/download/url'});
var fileData = {
id: 18,
type: 'file',
name: 'testName.txt',
mimetype: 'plain/text',
size: '1234',
etag: 'a01234c',
mtime: '123456'
};
var $tr = FileList.add(fileData);
FileActions.display($tr.find('td.filename'), true);
$tr.find('.action-download').click();
expect(redirectStub.calledOnce).toEqual(true);
expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=test%20download%20File.txt');
expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=testName.txt');
redirectStub.restore();
});
it('deletes file when clicking delete', function() {
var deleteStub = sinon.stub(FileList, 'do_delete');
var $tr = FileList.addFile('test delete File.txt', 1234, new Date());
var fileData = {
id: 18,
type: 'file',
name: 'testName.txt',
mimetype: 'plain/text',
size: '1234',
etag: 'a01234c',
mtime: '123456'
};
var $tr = FileList.add(fileData);
FileActions.display($tr.find('td.filename'), true);
$tr.find('.action.delete').click();

View File

@ -21,6 +21,9 @@
/* global OC, FileList */
describe('FileList tests', function() {
var testFiles, alertStub, notificationStub,
pushStateStub;
beforeEach(function() {
// init horrible parameters
var $body = $('body');
@ -28,45 +31,784 @@ describe('FileList tests', function() {
$body.append('<input type="hidden" id="permissions" value="31"></input>');
// dummy files table
$body.append('<table id="filestable"></table>');
// prevents URL changes during tests
pushStateStub = sinon.stub(window.history, 'pushState');
alertStub = sinon.stub(OC.dialogs, 'alert');
notificationStub = sinon.stub(OC.Notification, 'show');
// init parameters and test table elements
$('#testArea').append(
'<input type="hidden" id="dir" value="/subdir"></input>' +
'<input type="hidden" id="permissions" value="31"></input>' +
// dummy controls
'<div id="controls">' +
' <div class="actions creatable"></div>' +
' <div class="notCreatable"></div>' +
'</div>' +
// dummy table
'<table id="filestable">' +
'<thead><tr><th class="hidden">Name</th></tr></thead>' +
'<tbody id="fileList"></tbody>' +
'</table>' +
'<div id="emptycontent">Empty content message</div>'
);
testFiles = [{
id: 1,
type: 'file',
name: 'One.txt',
mimetype: 'text/plain',
size: 12
}, {
id: 2,
type: 'file',
name: 'Two.jpg',
mimetype: 'image/jpeg',
size: 12049
}, {
id: 3,
type: 'file',
name: 'Three.pdf',
mimetype: 'application/pdf',
size: 58009
}, {
id: 4,
type: 'dir',
name: 'somedir',
mimetype: 'httpd/unix-directory',
size: 250
}];
FileList.initialize();
});
afterEach(function() {
testFiles = undefined;
FileList.initialized = false;
FileList.isEmpty = true;
delete FileList._reloadCall;
$('#dir, #permissions, #filestable').remove();
notificationStub.restore();
alertStub.restore();
pushStateStub.restore();
});
it('generates file element with correct attributes when calling addFile', function() {
var lastMod = new Date(10000);
// note: download_url is actually the link target, not the actual download URL...
var $tr = FileList.addFile('testName.txt', 1234, lastMod, false, false, {download_url: 'test/download/url'});
expect($tr).toBeDefined();
expect($tr[0].tagName.toLowerCase()).toEqual('tr');
expect($tr.find('a:first').attr('href')).toEqual('test/download/url');
expect($tr.attr('data-type')).toEqual('file');
expect($tr.attr('data-file')).toEqual('testName.txt');
expect($tr.attr('data-size')).toEqual('1234');
expect($tr.attr('data-permissions')).toEqual('31');
//expect($tr.attr('data-mime')).toEqual('plain/text');
describe('Getters', function() {
it('Returns the current directory', function() {
$('#dir').val('/one/two/three');
expect(FileList.getCurrentDirectory()).toEqual('/one/two/three');
});
it('Returns the directory permissions as int', function() {
$('#permissions').val('23');
expect(FileList.getDirectoryPermissions()).toEqual(23);
});
});
it('generates dir element with correct attributes when calling addDir', function() {
var lastMod = new Date(10000);
var $tr = FileList.addDir('testFolder', 1234, lastMod, false);
describe('Adding files', function() {
var clock, now;
beforeEach(function() {
// to prevent date comparison issues
clock = sinon.useFakeTimers();
now = new Date();
});
afterEach(function() {
clock.restore();
});
it('generates file element with correct attributes when calling add() with file data', function() {
var fileData = {
id: 18,
type: 'file',
name: 'testName.txt',
mimetype: 'plain/text',
size: '1234',
etag: 'a01234c',
mtime: '123456'
};
var $tr = FileList.add(fileData);
expect($tr).toBeDefined();
expect($tr[0].tagName.toLowerCase()).toEqual('tr');
expect($tr.attr('data-type')).toEqual('dir');
expect($tr.attr('data-file')).toEqual('testFolder');
expect($tr.attr('data-size')).toEqual('1234');
expect($tr.attr('data-permissions')).toEqual('31');
//expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
expect($tr).toBeDefined();
expect($tr[0].tagName.toLowerCase()).toEqual('tr');
expect($tr.attr('data-id')).toEqual('18');
expect($tr.attr('data-type')).toEqual('file');
expect($tr.attr('data-file')).toEqual('testName.txt');
expect($tr.attr('data-size')).toEqual('1234');
expect($tr.attr('data-etag')).toEqual('a01234c');
expect($tr.attr('data-permissions')).toEqual('31');
expect($tr.attr('data-mime')).toEqual('plain/text');
expect($tr.attr('data-mtime')).toEqual('123456');
expect($tr.find('a.name').attr('href')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=testName.txt');
expect($tr.find('.filesize').text()).toEqual('1 kB');
expect(FileList.findFileEl('testName.txt')[0]).toEqual($tr[0]);
});
it('generates dir element with correct attributes when calling add() with dir data', function() {
var fileData = {
id: 19,
type: 'dir',
name: 'testFolder',
mimetype: 'httpd/unix-directory',
size: '1234',
etag: 'a01234c',
mtime: '123456'
};
var $tr = FileList.add(fileData);
expect($tr).toBeDefined();
expect($tr[0].tagName.toLowerCase()).toEqual('tr');
expect($tr.attr('data-id')).toEqual('19');
expect($tr.attr('data-type')).toEqual('dir');
expect($tr.attr('data-file')).toEqual('testFolder');
expect($tr.attr('data-size')).toEqual('1234');
expect($tr.attr('data-etag')).toEqual('a01234c');
expect($tr.attr('data-permissions')).toEqual('31');
expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
expect($tr.attr('data-mtime')).toEqual('123456');
expect($tr.find('.filesize').text()).toEqual('1 kB');
expect(FileList.findFileEl('testFolder')[0]).toEqual($tr[0]);
});
it('generates file element with default attributes when calling add() with minimal data', function() {
var fileData = {
type: 'file',
name: 'testFile.txt'
};
clock.tick(123456);
var $tr = FileList.add(fileData);
expect($tr).toBeDefined();
expect($tr[0].tagName.toLowerCase()).toEqual('tr');
expect($tr.attr('data-id')).toEqual(null);
expect($tr.attr('data-type')).toEqual('file');
expect($tr.attr('data-file')).toEqual('testFile.txt');
expect($tr.attr('data-size')).toEqual(null);
expect($tr.attr('data-etag')).toEqual(null);
expect($tr.attr('data-permissions')).toEqual('31');
expect($tr.attr('data-mime')).toEqual(null);
expect($tr.attr('data-mtime')).toEqual('123456');
expect($tr.find('.filesize').text()).toEqual('Pending');
});
it('generates dir element with default attributes when calling add() with minimal data', function() {
var fileData = {
type: 'dir',
name: 'testFolder'
};
clock.tick(123456);
var $tr = FileList.add(fileData);
expect($tr).toBeDefined();
expect($tr[0].tagName.toLowerCase()).toEqual('tr');
expect($tr.attr('data-id')).toEqual(null);
expect($tr.attr('data-type')).toEqual('dir');
expect($tr.attr('data-file')).toEqual('testFolder');
expect($tr.attr('data-size')).toEqual(null);
expect($tr.attr('data-etag')).toEqual(null);
expect($tr.attr('data-permissions')).toEqual('31');
expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
expect($tr.attr('data-mtime')).toEqual('123456');
expect($tr.find('.filesize').text()).toEqual('Pending');
});
it('generates file element with zero size when size is explicitly zero', function() {
var fileData = {
type: 'dir',
name: 'testFolder',
size: '0'
};
var $tr = FileList.add(fileData);
expect($tr.find('.filesize').text()).toEqual('0 B');
});
it('adds new file to the end of the list before the summary', function() {
var fileData = {
type: 'file',
name: 'P comes after O.txt'
};
FileList.setFiles(testFiles);
$tr = FileList.add(fileData);
expect($tr.index()).toEqual(4);
expect($tr.next().hasClass('summary')).toEqual(true);
});
it('adds new file at correct position in insert mode', function() {
var fileData = {
type: 'file',
name: 'P comes after O.txt'
};
FileList.setFiles(testFiles);
$tr = FileList.add(fileData, {insert: true});
// after "One.txt"
expect($tr.index()).toEqual(1);
});
it('removes empty content message and shows summary when adding first file', function() {
var fileData = {
type: 'file',
name: 'first file.txt',
size: 12
};
FileList.setFiles([]);
expect(FileList.isEmpty).toEqual(true);
FileList.add(fileData);
$summary = $('#fileList .summary');
expect($summary.length).toEqual(1);
// yes, ugly...
expect($summary.find('.info').text()).toEqual('0 folders and 1 file');
expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false);
expect($summary.find('.filesize').text()).toEqual('12 B');
expect($('#filestable thead th').hasClass('hidden')).toEqual(false);
expect($('#emptycontent').hasClass('hidden')).toEqual(true);
expect(FileList.isEmpty).toEqual(false);
});
});
describe('Removing files from the list', function() {
it('Removes file from list when calling remove() and updates summary', function() {
var $removedEl;
FileList.setFiles(testFiles);
$removedEl = FileList.remove('One.txt');
expect($removedEl).toBeDefined();
expect($removedEl.attr('data-file')).toEqual('One.txt');
expect($('#fileList tr:not(.summary)').length).toEqual(3);
expect(FileList.findFileEl('One.txt').length).toEqual(0);
$summary = $('#fileList .summary');
expect($summary.length).toEqual(1);
expect($summary.find('.info').text()).toEqual('1 folder and 2 files');
expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(false);
expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false);
expect($summary.find('.filesize').text()).toEqual('69 kB');
expect(FileList.isEmpty).toEqual(false);
});
it('Shows empty content when removing last file', function() {
FileList.setFiles([testFiles[0]]);
FileList.remove('One.txt');
expect($('#fileList tr:not(.summary)').length).toEqual(0);
expect(FileList.findFileEl('One.txt').length).toEqual(0);
$summary = $('#fileList .summary');
expect($summary.length).toEqual(0);
expect($('#filestable thead th').hasClass('hidden')).toEqual(true);
expect($('#emptycontent').hasClass('hidden')).toEqual(false);
expect(FileList.isEmpty).toEqual(true);
});
});
describe('Deleting files', function() {
function doDelete() {
var request, query;
// note: normally called from FileActions
FileList.do_delete(['One.txt', 'Two.jpg']);
expect(fakeServer.requests.length).toEqual(1);
request = fakeServer.requests[0];
expect(request.url).toEqual(OC.webroot + '/index.php/apps/files/ajax/delete.php');
query = fakeServer.requests[0].requestBody;
expect(OC.parseQueryString(query)).toEqual({'dir': '/subdir', files: '["One.txt","Two.jpg"]'});
}
it('calls delete.php, removes the deleted entries and updates summary', function() {
FileList.setFiles(testFiles);
doDelete();
fakeServer.requests[0].respond(
200,
{ 'Content-Type': 'application/json' },
JSON.stringify({status: 'success'})
);
expect(FileList.findFileEl('One.txt').length).toEqual(0);
expect(FileList.findFileEl('Two.jpg').length).toEqual(0);
expect(FileList.findFileEl('Three.pdf').length).toEqual(1);
expect(FileList.$fileList.find('tr:not(.summary)').length).toEqual(2);
$summary = $('#fileList .summary');
expect($summary.length).toEqual(1);
expect($summary.find('.info').text()).toEqual('1 folder and 1 file');
expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(false);
expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false);
expect($summary.find('.filesize').text()).toEqual('57 kB');
expect(FileList.isEmpty).toEqual(false);
expect($('#filestable thead th').hasClass('hidden')).toEqual(false);
expect($('#emptycontent').hasClass('hidden')).toEqual(true);
expect(notificationStub.notCalled).toEqual(true);
});
it('updates summary when deleting last file', function() {
FileList.setFiles([testFiles[0], testFiles[1]]);
doDelete();
fakeServer.requests[0].respond(
200,
{ 'Content-Type': 'application/json' },
JSON.stringify({status: 'success'})
);
expect(FileList.$fileList.find('tr:not(.summary)').length).toEqual(0);
$summary = $('#fileList .summary');
expect($summary.length).toEqual(0);
expect(FileList.isEmpty).toEqual(true);
expect($('#filestable thead th').hasClass('hidden')).toEqual(true);
expect($('#emptycontent').hasClass('hidden')).toEqual(false);
});
it('bring back deleted item when delete call failed', function() {
FileList.setFiles(testFiles);
doDelete();
fakeServer.requests[0].respond(
200,
{ 'Content-Type': 'application/json' },
JSON.stringify({status: 'error', data: {message: 'WOOT'}})
);
// files are still in the list
expect(FileList.findFileEl('One.txt').length).toEqual(1);
expect(FileList.findFileEl('Two.jpg').length).toEqual(1);
expect(FileList.$fileList.find('tr:not(.summary)').length).toEqual(4);
expect(notificationStub.calledOnce).toEqual(true);
});
});
describe('Renaming files', function() {
function doRename() {
var $input, request;
FileList.setFiles(testFiles);
// trigger rename prompt
FileList.rename('One.txt');
$input = FileList.$fileList.find('input.filename');
$input.val('One_renamed.txt').blur();
expect(fakeServer.requests.length).toEqual(1);
var request = fakeServer.requests[0];
expect(request.url.substr(0, request.url.indexOf('?'))).toEqual(OC.webroot + '/index.php/apps/files/ajax/rename.php');
expect(OC.parseQueryString(request.url)).toEqual({'dir': '/subdir', newname: 'One_renamed.txt', file: 'One.txt'});
// element is renamed before the request finishes
expect(FileList.findFileEl('One.txt').length).toEqual(0);
expect(FileList.findFileEl('One_renamed.txt').length).toEqual(1);
// input is gone
expect(FileList.$fileList.find('input.filename').length).toEqual(0);
}
it('Keeps renamed file entry if rename ajax call suceeded', function() {
doRename();
fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
status: 'success',
data: {
name: 'One_renamed.txt'
}
}));
// element stays renamed
expect(FileList.findFileEl('One.txt').length).toEqual(0);
expect(FileList.findFileEl('One_renamed.txt').length).toEqual(1);
expect(alertStub.notCalled).toEqual(true);
});
it('Reverts file entry if rename ajax call failed', function() {
doRename();
fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
status: 'error',
data: {
message: 'Something went wrong'
}
}));
// element was reverted
expect(FileList.findFileEl('One.txt').length).toEqual(1);
expect(FileList.findFileEl('One_renamed.txt').length).toEqual(0);
expect(alertStub.calledOnce).toEqual(true);
});
it('Correctly updates file link after rename', function() {
var $tr;
doRename();
fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
status: 'success',
data: {
name: 'One_renamed.txt'
}
}));
$tr = FileList.findFileEl('One_renamed.txt');
expect($tr.find('a.name').attr('href')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=One_renamed.txt');
});
// FIXME: fix this in the source code!
xit('Correctly updates file link after rename when path has same name', function() {
var $tr;
// evil case: because of buggy code
$('#dir').val('/One.txt/subdir');
doRename();
fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
status: 'success',
data: {
name: 'One_renamed.txt'
}
}));
$tr = FileList.findFileEl('One_renamed.txt');
expect($tr.find('a.name').attr('href')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=One.txt');
});
});
describe('List rendering', function() {
it('renders a list of files using add()', function() {
var addSpy = sinon.spy(FileList, 'add');
FileList.setFiles(testFiles);
expect(addSpy.callCount).toEqual(4);
expect($('#fileList tr:not(.summary)').length).toEqual(4);
addSpy.restore();
});
it('updates summary using the file sizes', function() {
var $summary;
FileList.setFiles(testFiles);
$summary = $('#fileList .summary');
expect($summary.length).toEqual(1);
expect($summary.find('.info').text()).toEqual('1 folder and 3 files');
expect($summary.find('.filesize').text()).toEqual('69 kB');
});
it('shows headers, summary and hide empty content message after setting files', function(){
FileList.setFiles(testFiles);
expect($('#filestable thead th').hasClass('hidden')).toEqual(false);
expect($('#emptycontent').hasClass('hidden')).toEqual(true);
expect(FileList.$fileList.find('.summary').length).toEqual(1);
});
it('hides headers, summary and show empty content message after setting empty file list', function(){
FileList.setFiles([]);
expect($('#filestable thead th').hasClass('hidden')).toEqual(true);
expect($('#emptycontent').hasClass('hidden')).toEqual(false);
expect(FileList.$fileList.find('.summary').length).toEqual(0);
});
it('hides headers, empty content message, and summary when list is empty and user has no creation permission', function(){
$('#permissions').val(0);
FileList.setFiles([]);
expect($('#filestable thead th').hasClass('hidden')).toEqual(true);
expect($('#emptycontent').hasClass('hidden')).toEqual(true);
expect(FileList.$fileList.find('.summary').length).toEqual(0);
});
it('calling findFileEl() can find existing file element', function() {
FileList.setFiles(testFiles);
expect(FileList.findFileEl('Two.jpg').length).toEqual(1);
});
it('calling findFileEl() returns empty when file not found in file', function() {
FileList.setFiles(testFiles);
expect(FileList.findFileEl('unexist.dat').length).toEqual(0);
});
it('only add file if in same current directory', function() {
$('#dir').val('/current dir');
var fileData = {
type: 'file',
name: 'testFile.txt',
directory: '/current dir'
};
var $tr = FileList.add(fileData);
expect(FileList.findFileEl('testFile.txt').length).toEqual(1);
});
it('triggers "fileActionsReady" event after update', function() {
var handler = sinon.stub();
FileList.$fileList.on('fileActionsReady', handler);
FileList.setFiles(testFiles);
expect(handler.calledOnce).toEqual(true);
});
it('triggers "updated" event after update', function() {
var handler = sinon.stub();
FileList.$fileList.on('updated', handler);
FileList.setFiles(testFiles);
expect(handler.calledOnce).toEqual(true);
});
});
describe('file previews', function() {
var previewLoadStub;
function getImageUrl($el) {
// might be slightly different cross-browser
var url = $el.css('background-image');
var r = url.match(/url\(['"]?([^'")]*)['"]?\)/);
if (!r) {
return url;
}
return r[1];
}
beforeEach(function() {
previewLoadStub = sinon.stub(Files, 'lazyLoadPreview');
});
afterEach(function() {
previewLoadStub.restore();
});
it('renders default icon for file when none provided and no preview is available', function() {
var fileData = {
type: 'file',
name: 'testFile.txt'
};
var $tr = FileList.add(fileData);
var $td = $tr.find('td.filename');
expect(getImageUrl($td)).toEqual(OC.webroot + '/core/img/filetypes/file.svg');
expect(previewLoadStub.notCalled).toEqual(true);
});
it('renders default icon for dir when none provided and no preview is available', function() {
var fileData = {
type: 'dir',
name: 'test dir'
};
var $tr = FileList.add(fileData);
var $td = $tr.find('td.filename');
expect(getImageUrl($td)).toEqual(OC.webroot + '/core/img/filetypes/folder.svg');
expect(previewLoadStub.notCalled).toEqual(true);
});
it('renders provided icon for file when provided', function() {
var fileData = {
type: 'file',
name: 'test dir',
icon: OC.webroot + '/core/img/filetypes/application-pdf.svg'
};
var $tr = FileList.add(fileData);
var $td = $tr.find('td.filename');
expect(getImageUrl($td)).toEqual(OC.webroot + '/core/img/filetypes/application-pdf.svg');
expect(previewLoadStub.notCalled).toEqual(true);
});
it('renders preview when no icon was provided and preview is available', function() {
var fileData = {
type: 'file',
name: 'test dir',
isPreviewAvailable: true
};
var $tr = FileList.add(fileData);
var $td = $tr.find('td.filename');
expect(getImageUrl($td)).toEqual(OC.webroot + '/core/img/filetypes/file.svg');
expect(previewLoadStub.calledOnce).toEqual(true);
// third argument is callback
previewLoadStub.getCall(0).args[2](OC.webroot + '/somepath.png');
expect(getImageUrl($td)).toEqual(OC.webroot + '/somepath.png');
});
it('renders default file type icon when no icon was provided and no preview is available', function() {
var fileData = {
type: 'file',
name: 'test dir',
isPreviewAvailable: false
};
var $tr = FileList.add(fileData);
var $td = $tr.find('td.filename');
expect(getImageUrl($td)).toEqual(OC.webroot + '/core/img/filetypes/file.svg');
expect(previewLoadStub.notCalled).toEqual(true);
});
});
describe('viewer mode', function() {
it('enabling viewer mode hides files table and action buttons', function() {
FileList.setViewerMode(true);
expect($('#filestable').hasClass('hidden')).toEqual(true);
expect($('.actions').hasClass('hidden')).toEqual(true);
expect($('.notCreatable').hasClass('hidden')).toEqual(true);
});
it('disabling viewer mode restores files table and action buttons', function() {
FileList.setViewerMode(true);
FileList.setViewerMode(false);
expect($('#filestable').hasClass('hidden')).toEqual(false);
expect($('.actions').hasClass('hidden')).toEqual(false);
expect($('.notCreatable').hasClass('hidden')).toEqual(true);
});
it('disabling viewer mode restores files table and action buttons with correct permissions', function() {
$('#permissions').val(0);
FileList.setViewerMode(true);
FileList.setViewerMode(false);
expect($('#filestable').hasClass('hidden')).toEqual(false);
expect($('.actions').hasClass('hidden')).toEqual(true);
expect($('.notCreatable').hasClass('hidden')).toEqual(false);
});
});
describe('loading file list', function() {
beforeEach(function() {
var data = {
status: 'success',
data: {
files: testFiles,
permissions: 31
}
};
fakeServer.respondWith(/\/index\.php\/apps\/files\/ajax\/list.php\?dir=%2F(subdir|anothersubdir)/, [
200, {
"Content-Type": "application/json"
},
JSON.stringify(data)
]);
});
it('fetches file list from server and renders it when reload() is called', function() {
FileList.reload();
expect(fakeServer.requests.length).toEqual(1);
var url = fakeServer.requests[0].url;
var query = url.substr(url.indexOf('?') + 1);
expect(OC.parseQueryString(query)).toEqual({'dir': '/subdir'});
fakeServer.respond();
expect($('#fileList tr:not(.summary)').length).toEqual(4);
expect(FileList.findFileEl('One.txt').length).toEqual(1);
});
it('switches dir and fetches file list when calling changeDirectory()', function() {
FileList.changeDirectory('/anothersubdir');
expect(FileList.getCurrentDirectory()).toEqual('/anothersubdir');
expect(fakeServer.requests.length).toEqual(1);
var url = fakeServer.requests[0].url;
var query = url.substr(url.indexOf('?') + 1);
expect(OC.parseQueryString(query)).toEqual({'dir': '/anothersubdir'});
fakeServer.respond();
});
it('switches to root dir when current directory does not exist', function() {
fakeServer.respondWith(/\/index\.php\/apps\/files\/ajax\/list.php\?dir=%2funexist/, [
404, {
"Content-Type": "application/json"
},
''
]);
FileList.changeDirectory('/unexist');
fakeServer.respond();
expect(FileList.getCurrentDirectory()).toEqual('/');
});
it('shows mask before loading file list then hides it at the end', function() {
var showMaskStub = sinon.stub(FileList, 'showMask');
var hideMaskStub = sinon.stub(FileList, 'hideMask');
FileList.changeDirectory('/anothersubdir');
expect(showMaskStub.calledOnce).toEqual(true);
expect(hideMaskStub.calledOnce).toEqual(false);
fakeServer.respond();
expect(showMaskStub.calledOnce).toEqual(true);
expect(hideMaskStub.calledOnce).toEqual(true);
showMaskStub.restore();
hideMaskStub.restore();
});
it('changes URL to target dir', function() {
FileList.changeDirectory('/somedir');
expect(pushStateStub.calledOnce).toEqual(true);
expect(pushStateStub.getCall(0).args[0]).toEqual({dir: '/somedir'});
expect(pushStateStub.getCall(0).args[2]).toEqual(OC.webroot + '/index.php/apps/files?dir=/somedir');
});
it('refreshes breadcrumb after update', function() {
var setDirSpy = sinon.spy(FileList.breadcrumb, 'setDirectory');
FileList.changeDirectory('/anothersubdir');
fakeServer.respond();
expect(FileList.breadcrumb.setDirectory.calledOnce).toEqual(true);
expect(FileList.breadcrumb.setDirectory.calledWith('/anothersubdir')).toEqual(true);
setDirSpy.restore();
});
});
describe('breadcrumb events', function() {
beforeEach(function() {
var data = {
status: 'success',
data: {
files: testFiles,
permissions: 31
}
};
fakeServer.respondWith(/\/index\.php\/apps\/files\/ajax\/list.php\?dir=%2Fsubdir/, [
200, {
"Content-Type": "application/json"
},
JSON.stringify(data)
]);
});
it('clicking on root breadcrumb changes directory to root', function() {
FileList.changeDirectory('/subdir/two/three with space/four/five');
fakeServer.respond();
var changeDirStub = sinon.stub(FileList, 'changeDirectory');
FileList.breadcrumb.$el.find('.crumb:eq(0)').click();
expect(changeDirStub.calledOnce).toEqual(true);
expect(changeDirStub.getCall(0).args[0]).toEqual('/');
changeDirStub.restore();
});
it('clicking on breadcrumb changes directory', function() {
FileList.changeDirectory('/subdir/two/three with space/four/five');
fakeServer.respond();
var changeDirStub = sinon.stub(FileList, 'changeDirectory');
FileList.breadcrumb.$el.find('.crumb:eq(3)').click();
expect(changeDirStub.calledOnce).toEqual(true);
expect(changeDirStub.getCall(0).args[0]).toEqual('/subdir/two/three with space');
changeDirStub.restore();
});
it('dropping files on breadcrumb calls move operation', function() {
var request, query, testDir = '/subdir/two/three with space/four/five';
FileList.changeDirectory(testDir);
fakeServer.respond();
var $crumb = FileList.breadcrumb.$el.find('.crumb:eq(3)');
// no idea what this is but is required by the handler
var ui = {
helper: {
find: sinon.stub()
}
};
// returns a list of tr that were dragged
// FIXME: why are their attributes different than the
// regular file trs ?
ui.helper.find.returns([
$('<tr data-filename="One.txt" data-dir="' + testDir + '"></tr>'),
$('<tr data-filename="Two.jpg" data-dir="' + testDir + '"></tr>')
]);
// simulate drop event
FileList._onDropOnBreadCrumb.call($crumb, new $.Event('drop'), ui);
// will trigger two calls to move.php (first one was previous list.php)
expect(fakeServer.requests.length).toEqual(3);
request = fakeServer.requests[1];
expect(request.method).toEqual('POST');
expect(request.url).toEqual(OC.webroot + '/index.php/apps/files/ajax/move.php');
query = OC.parseQueryString(request.requestBody);
expect(query).toEqual({
target: '/subdir/two/three with space',
dir: testDir,
file: 'One.txt'
});
request = fakeServer.requests[2];
expect(request.method).toEqual('POST');
expect(request.url).toEqual(OC.webroot + '/index.php/apps/files/ajax/move.php');
query = OC.parseQueryString(request.requestBody);
expect(query).toEqual({
target: '/subdir/two/three with space',
dir: testDir,
file: 'Two.jpg'
});
});
it('dropping files on same dir breadcrumb does nothing', function() {
var request, query, testDir = '/subdir/two/three with space/four/five';
FileList.changeDirectory(testDir);
fakeServer.respond();
var $crumb = FileList.breadcrumb.$el.find('.crumb:last');
// no idea what this is but is required by the handler
var ui = {
helper: {
find: sinon.stub()
}
};
// returns a list of tr that were dragged
// FIXME: why are their attributes different than the
// regular file trs ?
ui.helper.find.returns([
$('<tr data-filename="One.txt" data-dir="' + testDir + '"></tr>'),
$('<tr data-filename="Two.jpg" data-dir="' + testDir + '"></tr>')
]);
// simulate drop event
FileList._onDropOnBreadCrumb.call($crumb, new $.Event('drop'), ui);
// no extra server request
expect(fakeServer.requests.length).toEqual(1);
});
});
describe('Download Url', function() {
it('returns correct download URL for single files', function() {
expect(FileList.getDownloadUrl('some file.txt')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=some%20file.txt');
expect(FileList.getDownloadUrl('some file.txt', '/anotherpath/abc')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fanotherpath%2Fabc&files=some%20file.txt');
expect(Files.getDownloadUrl('some file.txt')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=some%20file.txt');
expect(Files.getDownloadUrl('some file.txt', '/anotherpath/abc')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fanotherpath%2Fabc&files=some%20file.txt');
$('#dir').val('/');
expect(FileList.getDownloadUrl('some file.txt')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2F&files=some%20file.txt');
expect(Files.getDownloadUrl('some file.txt')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2F&files=some%20file.txt');
});
it('returns correct download URL for multiple files', function() {
expect(FileList.getDownloadUrl(['a b c.txt', 'd e f.txt'])).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=%5B%22a%20b%20c.txt%22%2C%22d%20e%20f.txt%22%5D');
expect(Files.getDownloadUrl(['a b c.txt', 'd e f.txt'])).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=%5B%22a%20b%20c.txt%22%2C%22d%20e%20f.txt%22%5D');
});
it('returns the correct ajax URL', function() {
expect(Files.getAjaxUrl('test', {a:1, b:'x y'})).toEqual(OC.webroot + '/index.php/apps/files/ajax/test.php?a=1&b=x%20y');
});
});
});

View File

@ -0,0 +1,91 @@
<?php
/**
* ownCloud
*
* @author Vincent Petry
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
// only need filesystem apps
$RUNTIME_APPTYPES=array('filesystem');
// Init owncloud
if(!\OC_App::isEnabled('files_sharing')){
exit;
}
if(!isset($_GET['t'])){
\OC_Response::setStatus(400); //400 Bad Request
\OC_Log::write('core-preview', 'No token parameter was passed', \OC_Log::DEBUG);
exit;
}
$token = $_GET['t'];
$password = null;
if (isset($_POST['password'])) {
$password = $_POST['password'];
}
$relativePath = null;
if (isset($_GET['dir'])) {
$relativePath = $_GET['dir'];
}
$data = \OCA\Files_Sharing\Helper::setupFromToken($token, $relativePath, $password);
$linkItem = $data['linkItem'];
// Load the files
$dir = $data['realPath'];
$dir = \OC\Files\Filesystem::normalizePath($dir);
if (!\OC\Files\Filesystem::is_dir($dir . '/')) {
\OC_Response::setStatus(404);
\OCP\JSON::error(array('success' => false));
exit();
}
$data = array();
$baseUrl = OCP\Util::linkTo('files_sharing', 'index.php') . '?t=' . urlencode($token) . '&dir=';
// make filelist
$files = \OCA\Files\Helper::getFiles($dir);
$formattedFiles = array();
foreach ($files as $file) {
$entry = \OCA\Files\Helper::formatFileInfo($file);
unset($entry['directory']); // for now
$entry['permissions'] = \OCP\PERMISSION_READ;
$formattedFiles[] = $entry;
}
$data['directory'] = $relativePath;
$data['files'] = $formattedFiles;
$data['dirToken'] = $linkItem['token'];
$permissions = $linkItem['permissions'];
// if globally disabled
if (OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes') === 'no') {
// only allow reading
$permissions = \OCP\PERMISSION_READ;
}
$data['permissions'] = $permissions;
OCP\JSON::success(array('data' => $data));

View File

@ -35,6 +35,11 @@ body {
background: #fff;
text-align: center;
margin: 45px auto 0;
min-height: 150px;
}
#preview .notCreatable {
display: none;
}
#noPreview {

View File

@ -8,16 +8,7 @@
*
*/
/* global OC, FileList, FileActions */
// Override download path to files_sharing/public.php
function fileDownloadPath(dir, file) {
var url = $('#downloadURL').val();
if (url.indexOf('&path=') != -1) {
url += '/'+file;
}
return url;
}
/* global OC, FileActions, FileList, Files */
$(document).ready(function() {
@ -31,32 +22,44 @@ $(document).ready(function() {
action($('#filename').val());
}
}
FileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function(filename) {
var tr = FileList.findFileEl(filename);
if (tr.length > 0) {
window.location = $(tr).find('a.name').attr('href');
}
});
// override since the format is different
FileList.getDownloadUrl = function(filename, dir) {
if ($.isArray(filename)) {
filename = JSON.stringify(filename);
}
var path = dir || FileList.getCurrentDirectory();
var params = {
service: 'files',
t: $('#sharingToken').val(),
path: path,
download: null
};
if (filename) {
params.files = filename;
}
return OC.filePath('', '', 'public.php') + '?' + OC.buildQueryString(params);
};
}
// override since the format is different
Files.getDownloadUrl = function(filename, dir) {
if ($.isArray(filename)) {
filename = JSON.stringify(filename);
}
var path = dir || FileList.getCurrentDirectory();
var params = {
service: 'files',
t: $('#sharingToken').val(),
path: path,
files: filename,
download: null
};
return OC.filePath('', '', 'public.php') + '?' + OC.buildQueryString(params);
};
Files.getAjaxUrl = function(action, params) {
params = params || {};
params.t = $('#sharingToken').val();
return OC.filePath('files_sharing', 'ajax', action + '.php') + '?' + OC.buildQueryString(params);
};
FileList.linkTo = function(dir) {
var params = {
service: 'files',
t: $('#sharingToken').val(),
dir: dir
};
return OC.filePath('', '', 'public.php') + '?' + OC.buildQueryString(params);
};
Files.generatePreviewUrl = function(urlSpec) {
urlSpec.t = $('#dirToken').val();
return OC.generateUrl('/apps/files_sharing/ajax/publicpreview.php?') + $.param(urlSpec);
};
var file_upload_start = $('#file_upload_start');
file_upload_start.on('fileuploadadd', function(e, data) {
// Add custom data to the upload handler

View File

@ -1,15 +1,35 @@
/*
* Copyright (c) 2014
*
* This file is licensed under the Affero General Public License version 3
* or later.
*
* See the COPYING-README file.
*
*/
/* global OC, t, FileList, FileActions */
$(document).ready(function() {
var disableSharing = $('#disableSharing').data('status'),
sharesLoaded = false;
if (typeof OC.Share !== 'undefined' && typeof FileActions !== 'undefined' && !disableSharing) {
var oldCreateRow = FileList._createRow;
FileList._createRow = function(fileData) {
var tr = oldCreateRow.apply(this, arguments);
if (fileData.shareOwner) {
tr.attr('data-share-owner', fileData.shareOwner);
}
return tr;
};
$('#fileList').on('fileActionsReady',function(){
var allShared = $('#fileList').find('[data-share-owner]').find('[data-Action="Share"]');
var allShared = $('#fileList').find('[data-share-owner] [data-Action="Share"]');
allShared.addClass('permanent');
allShared.find('span').text(function(){
$owner = $(this).closest('tr').attr('data-share-owner');
var $owner = $(this).closest('tr').attr('data-share-owner');
return ' ' + t('files_sharing', 'Shared by {owner}', {owner: $owner});
});

View File

@ -0,0 +1,114 @@
<?php
namespace OCA\Files_Sharing;
class Helper {
/**
* Sets up the filesystem and user for public sharing
* @param string $token string share token
* @param string $relativePath optional path relative to the share
* @param string $password optional password
*/
public static function setupFromToken($token, $relativePath = null, $password = null) {
\OC_User::setIncognitoMode(true);
$linkItem = \OCP\Share::getShareByToken($token);
if($linkItem === false || ($linkItem['item_type'] !== 'file' && $linkItem['item_type'] !== 'folder')) {
\OC_Response::setStatus(404);
\OC_Log::write('core-preview', 'Passed token parameter is not valid', \OC_Log::DEBUG);
exit;
}
if(!isset($linkItem['uid_owner']) || !isset($linkItem['file_source'])) {
\OC_Response::setStatus(500);
\OC_Log::write('core-preview', 'Passed token seems to be valid, but it does not contain all necessary information . ("' . $token . '")', \OC_Log::WARN);
exit;
}
$type = $linkItem['item_type'];
$fileSource = $linkItem['file_source'];
$shareOwner = $linkItem['uid_owner'];
$rootLinkItem = \OCP\Share::resolveReShare($linkItem);
$path = null;
if (isset($rootLinkItem['uid_owner'])) {
\OCP\JSON::checkUserExists($rootLinkItem['uid_owner']);
\OC_Util::tearDownFS();
\OC_Util::setupFS($rootLinkItem['uid_owner']);
$path = \OC\Files\Filesystem::getPath($linkItem['file_source']);
}
if ($path === null) {
\OCP\Util::writeLog('share', 'could not resolve linkItem', \OCP\Util::DEBUG);
\OC_Response::setStatus(404);
\OCP\JSON::error(array('success' => false));
exit();
}
if (!isset($linkItem['item_type'])) {
\OCP\Util::writeLog('share', 'No item type set for share id: ' . $linkItem['id'], \OCP\Util::ERROR);
\OC_Response::setStatus(404);
\OCP\JSON::error(array('success' => false));
exit();
}
if (isset($linkItem['share_with'])) {
if (!self::authenticate($linkItem, $password)) {
\OC_Response::setStatus(403);
\OCP\JSON::error(array('success' => false));
exit();
}
}
$basePath = $path;
$rootName = basename($path);
if ($relativePath !== null && \OC\Files\Filesystem::isReadable($basePath . $relativePath)) {
$path .= \OC\Files\Filesystem::normalizePath($relativePath);
}
return array(
'linkItem' => $linkItem,
'basePath' => $basePath,
'realPath' => $path
);
}
/**
* Authenticate link item with the given password
* or with the session if no password was given.
* @param array $linkItem link item array
* @param string $password optional password
*
* @return true if authorized, false otherwise
*/
public static function authenticate($linkItem, $password) {
if ($password !== null) {
if ($linkItem['share_type'] == \OCP\Share::SHARE_TYPE_LINK) {
// Check Password
$forcePortable = (CRYPT_BLOWFISH != 1);
$hasher = new PasswordHash(8, $forcePortable);
if (!($hasher->CheckPassword($password.OC_Config::getValue('passwordsalt', ''),
$linkItem['share_with']))) {
return false;
} else {
// Save item id in session for future requests
\OC::$session->set('public_link_authenticated', $linkItem['id']);
}
} else {
\OCP\Util::writeLog('share', 'Unknown share type '.$linkItem['share_type']
.' for share id '.$linkItem['id'], \OCP\Util::ERROR);
return false;
}
}
else {
// not authenticated ?
if ( ! \OC::$session->exists('public_link_authenticated')
|| \OC::$session->get('public_link_authenticated') !== $linkItem['id']) {
return false;
}
}
return true;
}
}

View File

@ -11,31 +11,6 @@ if ($appConfig->getValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
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']);
}
}
function determineIcon($file, $sharingRoot, $sharingToken) {
// for folders we simply reuse the files logic
if($file['type'] == 'dir') {
return \OCA\Files\Helper::determineIcon($file);
}
$relativePath = substr($file['path'], 6);
$relativePath = substr($relativePath, strlen($sharingRoot));
if($file['isPreviewAvailable']) {
return OCP\publicPreview_icon($relativePath, $sharingToken) . '&c=' . $file['etag'];
}
$icon = OCP\mimetype_icon($file['mimetype']);
return substr($icon, 0, -3) . 'svg';
}
if (isset($_GET['t'])) {
$token = $_GET['t'];
$linkItem = OCP\Share::getShareByToken($token, false);
@ -153,13 +128,6 @@ if (isset($path)) {
$tmpl->assign('mimetype', \OC\Files\Filesystem::getMimeType($path));
$tmpl->assign('dirToken', $linkItem['token']);
$tmpl->assign('sharingToken', $token);
$allowPublicUploadEnabled = (bool) ($linkItem['permissions'] & OCP\PERMISSION_CREATE);
if ($appConfig->getValue('core', 'shareapi_allow_public_upload', 'yes') === 'no') {
$allowPublicUploadEnabled = false;
}
if ($linkItem['item_type'] !== 'folder') {
$allowPublicUploadEnabled = false;
}
$urlLinkIdentifiers= (isset($token)?'&t='.$token:'')
.(isset($_GET['dir'])?'&dir='.$_GET['dir']:'')
@ -170,64 +138,18 @@ if (isset($path)) {
OCP\Util::addStyle('files', 'files');
OCP\Util::addStyle('files', 'upload');
OCP\Util::addScript('files', 'breadcrumb');
OCP\Util::addScript('files', 'files');
OCP\Util::addScript('files', 'filelist');
OCP\Util::addscript('files', 'keyboardshortcuts');
$files = array();
$rootLength = strlen($basePath) + 1;
$totalSize = 0;
foreach (\OC\Files\Filesystem::getDirectoryContent($path) as $i) {
$totalSize += $i['size'];
$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['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($i['mimetype']);
$i['directory'] = $getPath;
$i['permissions'] = OCP\PERMISSION_READ;
$i['icon'] = determineIcon($i, $basePath, $token);
$files[] = $i;
}
usort($files, "fileCmp");
// Make breadcrumb
$breadcrumb = array();
$pathtohere = '';
foreach (explode('/', $getPath) as $i) {
if ($i != '') {
$pathtohere .= '/' . $i;
$breadcrumb[] = array('dir' => $pathtohere, 'name' => $i);
}
}
$list = new OCP\Template('files', 'part.list', '');
$list->assign('files', $files);
$list->assign('baseURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&path=');
$list->assign('downloadURL',
OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download&path=');
$list->assign('isPublic', true);
$list->assign('sharingtoken', $token);
$list->assign('sharingroot', $basePath);
$breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', '');
$breadcrumbNav->assign('breadcrumb', $breadcrumb);
$breadcrumbNav->assign('rootBreadCrumb', $rootName);
$breadcrumbNav->assign('baseURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&path=');
$maxUploadFilesize=OCP\Util::maxUploadFilesize($path);
$fileHeader = (!isset($files) or count($files) > 0);
$emptyContent = ($allowPublicUploadEnabled and !$fileHeader);
$freeSpace=OCP\Util::freeSpace($path);
$uploadLimit=OCP\Util::uploadLimit();
$folder = new OCP\Template('files', 'index', '');
$folder->assign('fileList', $list->fetchPage());
$folder->assign('breadcrumb', $breadcrumbNav->fetchPage());
$folder->assign('dir', $getPath);
$folder->assign('isCreatable', $allowPublicUploadEnabled);
$folder->assign('dirToken', $linkItem['token']);
$folder->assign('permissions', OCP\PERMISSION_READ);
$folder->assign('isPublic',true);
@ -239,15 +161,11 @@ if (isset($path)) {
$folder->assign('uploadLimit', $uploadLimit); // PHP upload limit
$folder->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true)));
$folder->assign('usedSpacePercent', 0);
$folder->assign('fileHeader', $fileHeader);
$folder->assign('disableSharing', true);
$folder->assign('trash', false);
$folder->assign('emptyContent', $emptyContent);
$folder->assign('ajaxLoad', false);
$tmpl->assign('folder', $folder->fetchPage());
$maxInputFileSize = OCP\Config::getSystemValue('maxZipInputSize', OCP\Util::computerFileSize('800 MB'));
$allowZip = OCP\Config::getSystemValue('allowZipDownload', true)
&& ( $maxInputFileSize === 0 || $totalSize <= $maxInputFileSize);
$allowZip = OCP\Config::getSystemValue('allowZipDownload', true);
$tmpl->assign('allowZipDownload', intval($allowZip));
$tmpl->assign('downloadURL',
OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download&path=' . urlencode($getPath));

View File

@ -4,21 +4,8 @@ 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\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
try {
$files = \OCA\Files_Trashbin\Helper::getTrashFiles($dir);
@ -27,19 +14,11 @@ try {
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();
$data['permissions'] = 0;
$data['directory'] = $dir;
$data['files'] = \OCA\Files_Trashbin\Helper::formatFileInfos($files);
OCP\JSON::success(array('data' => $data));

View File

@ -34,7 +34,7 @@ try{
if ($view->is_dir($file)) {
$mimetype = 'httpd/unix-directory';
} else {
$pathInfo = pathinfo($file);
$pathInfo = pathinfo(ltrim($file, '/'));
$fileName = $pathInfo['basename'];
// if in root dir
if ($pathInfo['dirname'] === '.') {

View File

@ -19,7 +19,7 @@ if (isset($_POST['allfiles']) and $_POST['allfiles'] === 'true') {
foreach (OCA\Files_Trashbin\Helper::getTrashFiles($dir) as $file) {
$fileName = $file['name'];
if (!$dirListing) {
$fileName .= '.d' . $file['timestamp'];
$fileName .= '.d' . $file['mtime'];
}
$list[] = $fileName;
}

View File

@ -1,3 +1,4 @@
#fileList td a.file, #fileList td a.file span {
#fileList tr[data-type="file"] td a.name,
#fileList tr[data-type="file"] td a.name span {
cursor: default;
}

View File

@ -11,6 +11,7 @@ $tmpl = new OCP\Template('files_trashbin', 'index', 'user');
OCP\Util::addStyle('files', 'files');
OCP\Util::addStyle('files_trashbin', 'trash');
OCP\Util::addScript('files', 'breadcrumb');
OCP\Util::addScript('files', 'filelist');
// filelist overrides
OCP\Util::addScript('files_trashbin', 'filelist');
@ -34,48 +35,7 @@ if ($isIE8 && isset($_GET['dir'])){
exit();
}
$ajaxLoad = false;
if (!$isIE8){
try {
$files = \OCA\Files_Trashbin\Helper::getTrashFiles($dir);
} catch (Exception $e) {
header('Location: ' . OCP\Util::linkTo('files_trashbin', 'index.php'));
exit();
}
}
else{
$files = array();
$ajaxLoad = true;
}
$dirlisting = false;
if ($dir && $dir !== '/') {
$dirlisting = true;
}
$breadcrumb = \OCA\Files_Trashbin\Helper::makeBreadcrumb($dir);
$breadcrumbNav = new OCP\Template('files_trashbin', 'part.breadcrumb', '');
$breadcrumbNav->assign('breadcrumb', $breadcrumb);
$breadcrumbNav->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php') . '?dir=');
$breadcrumbNav->assign('home', OCP\Util::linkTo('files', 'index.php'));
$list = new OCP\Template('files_trashbin', 'part.list', '');
$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('dirlisting', $dirlisting);
$list->assign('disableDownloadActions', true);
$tmpl->assign('dirlisting', $dirlisting);
$tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage());
$tmpl->assign('fileList', $list->fetchPage());
$tmpl->assign('files', $files);
$tmpl->assign('dir', $dir);
$tmpl->assign('disableSharing', true);
$tmpl->assign('ajaxLoad', true);
$tmpl->printPage();

View File

@ -1,4 +1,3 @@
/* disable download and sharing actions */
var disableDownloadActions = true;
var disableSharing = true;
var trashBinApp = true;

View File

@ -1,61 +1,78 @@
/* globals OC, FileList, t */
// 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);
/* global OC, t, FileList */
(function() {
FileList.appName = t('files_trashbin', 'Deleted files');
FileList._deletedRegExp = new RegExp(/^(.+)\.d[0-9]+$/);
/**
* Convert a file name in the format filename.d12345 to the real file name.
* This will use basename.
* The name will not be changed if it has no ".d12345" suffix.
* @param name file name
* @return converted file name
*/
FileList.getDeletedFileName = function(name) {
name = OC.basename(name);
var match = FileList._deletedRegExp.exec(name);
if (match && match.length > 1) {
name = match[1];
}
});
};
return name;
};
FileList.appName = t('files_trashbin', 'Deleted files');
var oldSetCurrentDir = FileList._setCurrentDir;
FileList._setCurrentDir = function(targetDir) {
oldSetCurrentDir.apply(this, arguments);
FileList._deletedRegExp = new RegExp(/^(.+)\.d[0-9]+$/);
var baseDir = OC.basename(targetDir);
if (baseDir !== '') {
FileList.setPageTitle(FileList.getDeletedFileName(baseDir));
}
};
/**
* Convert a file name in the format filename.d12345 to the real file name.
* This will use basename.
* The name will not be changed if it has no ".d12345" suffix.
* @param name file name
* @return converted file name
*/
FileList.getDeletedFileName = function(name) {
name = OC.basename(name);
var match = FileList._deletedRegExp.exec(name);
if (match && match.length > 1) {
name = match[1];
}
return name;
};
var oldSetCurrentDir = FileList.setCurrentDir;
FileList.setCurrentDir = function(targetDir) {
oldSetCurrentDir.apply(this, arguments);
var oldCreateRow = FileList._createRow;
FileList._createRow = function() {
// FIXME: MEGAHACK until we find a better solution
var tr = oldCreateRow.apply(this, arguments);
tr.find('td.filesize').remove();
return tr;
};
var baseDir = OC.basename(targetDir);
if (baseDir !== '') {
FileList.setPageTitle(FileList.getDeletedFileName(baseDir));
}
};
FileList._onClickBreadCrumb = function(e) {
var $el = $(e.target).closest('.crumb'),
index = $el.index(),
$targetDir = $el.data('dir');
// first one is home, let the link makes it default action
if (index !== 0) {
e.preventDefault();
FileList.changeDirectory($targetDir);
}
};
FileList.linkTo = function(dir){
return OC.linkTo('files_trashbin', 'index.php')+"?dir="+ encodeURIComponent(dir).replace(/%2F/g, '/');
}
var oldAdd = FileList.add;
FileList.add = function(fileData, options) {
options = options || {};
var dir = FileList.getCurrentDirectory();
var dirListing = dir !== '' && dir !== '/';
// show deleted time as mtime
if (fileData.mtime) {
fileData.mtime = parseInt(fileData.mtime, 10);
}
if (!dirListing) {
fileData.displayName = fileData.name;
fileData.name = fileData.name + '.d' + Math.floor(fileData.mtime / 1000);
}
return oldAdd.call(this, fileData, options);
};
FileList.updateEmptyContent = function(){
var $fileList = $('#fileList');
var exists = $fileList.find('tr:first').exists();
$('#emptycontent').toggleClass('hidden', exists);
$('#filestable th').toggleClass('hidden', !exists);
}
FileList.linkTo = function(dir){
return OC.linkTo('files_trashbin', 'index.php')+"?dir="+ encodeURIComponent(dir).replace(/%2F/g, '/');
};
FileList.updateEmptyContent = function(){
var $fileList = $('#fileList');
var exists = $fileList.find('tr:first').exists();
$('#emptycontent').toggleClass('hidden', exists);
$('#filestable th').toggleClass('hidden', !exists);
};
})();

View File

@ -8,9 +8,26 @@
*
*/
/* global OC, t, FileList, FileActions */
/* global OC, t, BreadCrumb, FileActions, FileList, Files */
$(document).ready(function() {
var deletedRegExp = new RegExp(/^(.+)\.d[0-9]+$/);
/**
* Convert a file name in the format filename.d12345 to the real file name.
* This will use basename.
* The name will not be changed if it has no ".d12345" suffix.
* @param name file name
* @return converted file name
*/
function getDeletedFileName(name) {
name = OC.basename(name);
var match = deletedRegExp.exec(name);
if (match && match.length > 1) {
name = match[1];
}
return name;
}
function removeCallback(result) {
if (result.status !== 'success') {
OC.dialogs.alert(result.data.message, t('core', 'Error'));
@ -18,7 +35,7 @@ $(document).ready(function() {
var files = result.data.success;
for (var i = 0; i < files.length; i++) {
FileList.findFileEl(OC.basename(files[i].filename)).remove();
FileList.remove(OC.basename(files[i].filename), {updateSummary: false});
}
FileList.updateFileSummary();
FileList.updateEmptyContent();
@ -74,7 +91,6 @@ $(document).ready(function() {
}
procesSelection();
});
$('.undelete').click('click', function(event) {
event.preventDefault();
var allFiles = $('#select_all').is(':checked');
@ -89,7 +105,7 @@ $(document).ready(function() {
};
}
else {
files = getSelectedFiles('name');
files = Files.getSelectedFiles('name');
for (var i = 0; i < files.length; i++) {
var deleteAction = FileList.findFileEl(files[i]).children("td.date").children(".action.delete");
deleteAction.removeClass('delete-icon').addClass('progress-icon');
@ -131,7 +147,7 @@ $(document).ready(function() {
};
}
else {
files = getSelectedFiles('name');
files = Files.getSelectedFiles('name');
params = {
files: JSON.stringify(files),
dir: FileList.getCurrentDirectory()
@ -158,7 +174,7 @@ $(document).ready(function() {
}
FileList.hideMask();
// simply remove all files
FileList.update('');
FileList.setFiles([]);
enableActions();
}
else {
@ -191,7 +207,7 @@ $(document).ready(function() {
var filename = $(this).parent().parent().attr('data-file');
var tr = FileList.findFileEl(filename);
var renaming = tr.data('renaming');
if(!renaming && !FileList.isLoading(filename)){
if(!renaming){
if(mime.substr(0, 5) === 'text/'){ //no texteditor for now
return;
}
@ -203,48 +219,62 @@ $(document).ready(function() {
action(filename);
}
}
// event handlers for breadcrumb items
$('#controls').delegate('.crumb:not(.home) a', 'click', onClickBreadcrumb);
});
/**
* Override crumb URL maker (hacky!)
*/
FileList.breadcrumb.getCrumbUrl = function(part, index) {
if (index === 0) {
return OC.linkTo('files', 'index.php');
}
return OC.linkTo('files_trashbin', 'index.php')+"?dir=" + encodeURIComponent(part.dir);
};
Files.generatePreviewUrl = function(urlSpec) {
return OC.generateUrl('/apps/files_trashbin/ajax/preview.php?') + $.param(urlSpec);
};
Files.getDownloadUrl = function(action, params) {
// no downloads
return '#';
};
Files.getAjaxUrl = function(action, params) {
var q = '';
if (params) {
q = '?' + OC.buildQueryString(params);
}
return OC.filePath('files_trashbin', 'ajax', action + '.php') + q;
};
/**
* Override crumb making to add "Deleted Files" entry
* and convert files with ".d" extensions to a more
* user friendly name.
*/
var oldMakeCrumbs = BreadCrumb.prototype._makeCrumbs;
BreadCrumb.prototype._makeCrumbs = function() {
var parts = oldMakeCrumbs.apply(this, arguments);
// duplicate first part
parts.unshift(parts[0]);
parts[1] = {
dir: '/',
name: t('files_trashbin', 'Deleted Files')
};
for (var i = 2; i < parts.length; i++) {
parts[i].name = getDeletedFileName(parts[i].name);
}
return parts;
};
FileActions.actions.dir = {
// only keep 'Open' action for navigation
'Open': FileActions.actions.dir.Open
};
});
/**
* @brief get a list of selected files
* @param string property (option) the property of the file requested
* @return array
*
* possible values for property: name, mime, size and type
* if property is set, an array with that property for each file is returnd
* if it's ommited an array of objects with all properties is returned
*/
function getSelectedFiles(property){
var elements=$('td.filename input:checkbox:checked').parent().parent();
var files=[];
elements.each(function(i,element){
var file={
name:$(element).attr('data-file'),
timestamp:$(element).attr('data-timestamp'),
type:$(element).attr('data-type')
};
if(property){
files.push(file[property]);
}else{
files.push(file);
}
});
return files;
}
function fileDownloadPath(dir, file) {
return OC.filePath('files_trashbin', '', 'download.php') + '?file='+encodeURIComponent(file);
}
function enableActions() {
$(".action").css("display", "inline");
$(":input:checkbox").css("display", "inline");

View File

@ -27,6 +27,10 @@ class Helper
if ($dirContent === false) {
return $result;
}
list($storage, $internalPath) = $view->resolvePath($dir);
$absoluteDir = $view->getAbsolutePath($dir);
if (is_resource($dirContent)) {
while (($entryName = readdir($dirContent)) !== false) {
if (!\OC\Files\Filesystem::isIgnoredDir($entryName)) {
@ -40,76 +44,41 @@ class Helper
$parts = explode('/', ltrim($dir, '/'));
$timestamp = substr(pathinfo($parts[0], PATHINFO_EXTENSION), 1);
}
$result[] = array(
'id' => $id,
'timestamp' => $timestamp,
'mime' => \OC_Helper::getFileNameMimeType($id),
$i = array(
'name' => $id,
'mtime' => $timestamp,
'mimetype' => \OC_Helper::getFileNameMimeType($id),
'type' => $view->is_dir($dir . '/' . $entryName) ? 'dir' : 'file',
'location' => $dir,
'directory' => ($dir === '/') ? '' : $dir,
);
$result[] = new FileInfo($absoluteDir . '/' . $i['name'], $storage, $internalPath . '/' . $i['name'], $i);
}
}
closedir($dirContent);
}
$files = array();
$id = 0;
list($storage, $internalPath) = $view->resolvePath($dir);
$absoluteDir = $view->getAbsolutePath($dir);
foreach ($result as $r) {
$i = array();
$i['id'] = $id++;
$i['name'] = $r['id'];
$i['date'] = \OCP\Util::formatDate($r['timestamp']);
$i['timestamp'] = $r['timestamp'];
$i['etag'] = $r['timestamp']; // add fake etag, it is only needed to identify the preview image
$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;
if (\OCP\App::isEnabled('files_encryption')) {
$i['isPreviewAvailable'] = false;
} else {
$i['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($r['mime']);
}
$i['icon'] = \OCA\Files\Helper::determineIcon($i);
$files[] = new FileInfo($absoluteDir . '/' . $i['name'], $storage, $internalPath . '/' . $i['name'], $i);
}
usort($result, array('\OCA\Files\Helper', 'fileCmp'));
usort($files, array('\OCA\Files\Helper', 'fileCmp'));
return $files;
return $result;
}
/**
* 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
* Format file infos for JSON
* @param \OCP\Files\FileInfo[] $fileInfos file infos
*/
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);
public static function formatFileInfos($fileInfos) {
$files = array();
$id = 0;
foreach ($fileInfos as $i) {
$entry = \OCA\Files\Helper::formatFileInfo($i);
$entry['id'] = $id++;
$entry['etag'] = $entry['mtime']; // add fake etag, it is only needed to identify the preview image
$entry['permissions'] = \OCP\PERMISSION_READ;
if (\OCP\App::isEnabled('files_encryption')) {
$entry['isPreviewAvailable'] = false;
}
$files[] = $entry;
}
return $breadcrumb;
return $files;
}
}

View File

@ -1,13 +1,11 @@
<div id="controls">
<?php print_unescaped($_['breadcrumb']); ?>
<div id="file_action_panel"></div>
<div id="file_action_panel"></div>
</div>
<div id='notification'></div>
<div id="emptycontent" <?php if (!(isset($_['files']) && count($_['files']) === 0 && $_['dirlisting'] === false && !$_['ajaxLoad'])):?>class="hidden"<?php endif; ?>><?php p($l->t('Nothing in here. Your trash bin is empty!'))?></div>
<div id="emptycontent" class="hidden"><?php p($l->t('Nothing in here. Your trash bin is empty!'))?></div>
<input type="hidden" name="ajaxLoad" id="ajaxLoad" value="<?php p($_['ajaxLoad']); ?>" />
<input type="hidden" id="disableSharing" data-status="<?php p($_['disableSharing']); ?>"></input>
<input type="hidden" id="permissions" value="0"></input>
<input type="hidden" name="dir" value="<?php p($_['dir']) ?>" id="dir">
<table id="filestable">
@ -40,6 +38,5 @@
</tr>
</thead>
<tbody id="fileList">
<?php print_unescaped($_['fileList']); ?>
</tbody>
</table>

View File

@ -1,19 +0,0 @@
<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>
<div class="crumb svg"
data-dir='/'>
<a href="<?php p($_['baseURL']); ?>"><?php p($l->t("Deleted Files")); ?></a>
</div>
<?php if(count($_["breadcrumb"])):?>
<?php endif;?>
<?php for($i=0; $i<count($_["breadcrumb"]); $i++):
$crumb = $_["breadcrumb"][$i];
$dir = \OCP\Util::encodePath($crumb["dir"]); ?>
<div class="crumb <?php if($i === count($_["breadcrumb"])-1) p('last');?> svg"
data-dir='<?php p($dir);?>'>
<a href="<?php p($_['baseURL'].$dir); ?>"><?php p($crumb["name"]); ?></a>
</div>
<?php endfor;

View File

@ -1,79 +0,0 @@
<?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
$relative_date_color = round((time()-$file['date'])/60/60/24*14);
if($relative_date_color>200) $relative_date_color = 200;
$name = \OCP\Util::encodePath($file['name']);
$directory = \OCP\Util::encodePath($file['directory']); ?>
<tr data-filename="<?php p($file['name']);?>"
data-type="<?php ($file['type'] === 'dir')?p('dir'):p('file')?>"
data-mime="<?php p($file['mimetype'])?>"
data-permissions='<?php p($file['permissions']); ?>'
<?php if ( $_['dirlisting'] ): ?>
id="<?php p($file['directory'].'/'.$file['name']);?>"
data-file="<?php p($name);?>"
data-timestamp=''
data-dirlisting=1
<?php else: ?>
id="<?php p($file['name'].'.d'.$file['timestamp']);?>"
data-file="<?php p($file['name'].'.d'.$file['timestamp']);?>"
data-timestamp='<?php p($file['timestamp']);?>'
data-dirlisting=0
<?php endif; ?>>
<?php if($file['isPreviewAvailable']): ?>
<td class="filename svg preview-icon"
<?php else: ?>
<td class="filename svg"
<?php endif; ?>
<?php if($file['type'] === 'dir'): ?>
style="background-image:url(<?php print_unescaped(OCP\mimetype_icon('dir')); ?>)"
<?php else: ?>
<?php if($file['isPreviewAvailable']): ?>
style="background-image:url(<?php print_unescaped(OCA\Files_Trashbin\Trashbin::preview_icon(!$_['dirlisting'] ? ($file['name'].'.d'.$file['timestamp']) : ($file['directory'].'/'.$file['name']))); ?>)"
<?php else: ?>
style="background-image:url(<?php print_unescaped(OCP\mimetype_icon($file['mimetype'])); ?>)"
<?php endif; ?>
<?php endif; ?>
>
<?php if(!isset($_['readonly']) || !$_['readonly']): ?>
<input id="select-<?php p($file['id']); ?>" type="checkbox" />
<label for="select-<?php p($file['id']); ?>"></label>
<?php endif; ?>
<?php if($file['type'] === 'dir'): ?>
<?php if( $_['dirlisting'] ): ?>
<a class="name dir" href="<?php p($_['baseURL'].'/'.$name); ?>" title="">
<?php else: ?>
<a class="name dir" href="<?php p($_['baseURL'].'/'.$name.'.d'.$file['timestamp']); ?>" title="">
<?php endif; ?>
<?php else: ?>
<?php if( $_['dirlisting'] ): ?>
<a class="name file" href="<?php p($_['downloadURL'].'/'.$name); ?>" title="">
<?php else: ?>
<a class="name file" href="<?php p($_['downloadURL'].'/'.$name.'.d'.$file['timestamp']);?>" title="">
<?php endif; ?>
<?php endif; ?>
<span class="nametext">
<?php if($file['type'] === 'dir'):?>
<?php print_unescaped(htmlspecialchars($file['name']));?>
<?php else:?>
<?php print_unescaped(htmlspecialchars($file['basename']));?><span
class='extension'><?php p($file['extension']);?></span>
<?php endif;?>
</span>
<?php if($file['type'] === 'dir'):?>
<span class="uploadtext" currentUploads="0">
</span>
<?php endif;?>
</a>
</td>
<td class="date">
<span class="modified"
title="<?php p($file['date']); ?>"
style="color:rgb(<?php p($relative_date_color.','
.$relative_date_color.','
.$relative_date_color) ?>)">
<?php p($relative_deleted_date); ?>
</span>
</td>
</tr>
<?php endforeach;

View File

@ -283,6 +283,10 @@ input[type="submit"].enabled {
padding: 7px 10px
}
#controls .button.hidden {
display: none;
}
#content { position:relative; height:100%; width:100%; }
#content .hascontrols {
position: relative;
@ -922,6 +926,9 @@ div.crumb {
background: url('../img/breadcrumb.svg') no-repeat right center;
height: 44px;
}
div.crumb.hidden {
display: none;
}
div.crumb a,
div.crumb span {
position: relative;

View File

@ -159,6 +159,7 @@ function escapeHTML(s) {
* @param file The filename
* @param dir The directory the file is in - e.g. $('#dir').val()
* @return string
* @deprecated use Files.getDownloadURL() instead
*/
function fileDownloadPath(dir, file) {
return OC.filePath('files', 'ajax', 'download.php')+'?files='+encodeURIComponent(file)+'&dir='+encodeURIComponent(dir);
@ -371,6 +372,7 @@ var OC={
*/
parseQueryString:function(queryString){
var parts,
pos,
components,
result = {},
key,
@ -378,12 +380,25 @@ var OC={
if (!queryString){
return null;
}
if (queryString[0] === '?'){
queryString = queryString.substr(1);
pos = queryString.indexOf('?');
if (pos >= 0){
queryString = queryString.substr(pos + 1);
}
parts = queryString.split('&');
parts = queryString.replace(/\+/g, '%20').split('&');
for (var i = 0; i < parts.length; i++){
components = parts[i].split('=');
// split on first equal sign
var part = parts[i]
pos = part.indexOf('=');
if (pos >= 0) {
components = [
part.substr(0, pos),
part.substr(pos + 1)
]
}
else {
// key only
components = [part];
}
if (!components.length){
continue;
}
@ -391,8 +406,14 @@ var OC={
if (!key){
continue;
}
value = components[1];
result[key] = value && decodeURIComponent(value);
// if equal sign was there, return string
if (components.length > 1) {
result[key] = decodeURIComponent(components[1]);
}
// no equal sign => null value
else {
result[key] = null;
}
}
return result;
},

View File

@ -294,7 +294,7 @@ var OCdialogs = {
conflict.find('.replacement .mtime').text(formatDate(replacement.lastModifiedDate));
}
var path = original.directory + '/' +original.name;
Files.lazyLoadPreview(path, original.mime, function(previewpath){
Files.lazyLoadPreview(path, original.mimetype, function(previewpath){
conflict.find('.original .icon').css('background-image','url('+previewpath+')');
}, 96, 96, original.etag);
getCroppedPreview(replacement).then(
@ -514,7 +514,7 @@ var OCdialogs = {
}
return $.getJSON(
OC.filePath('files', 'ajax', 'rawlist.php'),
OC.filePath('files', 'ajax', 'list.php'),
{
dir: dir,
mimetypes: JSON.stringify(mimeType)
@ -539,7 +539,7 @@ var OCdialogs = {
this.$filelist.empty().addClass('loading');
this.$filePicker.data('path', dir);
$.when(this._getFileList(dir, this.$filePicker.data('mimetype'))).then(function(response) {
$.each(response.data, function(index, file) {
$.each(response.data.files, function(index, file) {
if (file.type === 'dir') {
dirs.push(file);
} else {
@ -555,9 +555,16 @@ var OCdialogs = {
type: entry.type,
dir: dir,
filename: entry.name,
date: OC.mtime2date(entry.mtime)
date: OC.mtime2date(Math.floor(entry.mtime / 1000))
});
$li.find('img').attr('src', entry.mimetype_icon);
$li.find('img').attr('src', entry.icon);
if (entry.isPreviewAvailable) {
var urlSpec = {
file: dir + '/' + entry.name
};
var previewUrl = OC.generateUrl('/core/preview.png?') + $.param(urlSpec);
$li.find('img').attr('src', previewUrl);
}
self.$filelist.append($li);
});

View File

@ -268,7 +268,72 @@ describe('Core base tests', function() {
// still nothing
expect(counter).toEqual(0);
});
});
describe('Parse query string', function() {
it('Parses query string from full URL', function() {
var query = OC.parseQueryString('http://localhost/stuff.php?q=a&b=x');
expect(query).toEqual({q: 'a', b: 'x'});
});
it('Parses query string from query part alone', function() {
var query = OC.parseQueryString('q=a&b=x');
expect(query).toEqual({q: 'a', b: 'x'});
});
it('Returns null hash when empty query', function() {
var query = OC.parseQueryString('');
expect(query).toEqual(null);
});
it('Returns empty hash when empty query with question mark', function() {
var query = OC.parseQueryString('?');
expect(query).toEqual({});
});
it('Decodes regular query strings', function() {
var query = OC.parseQueryString('a=abc&b=def');
expect(query).toEqual({
a: 'abc',
b: 'def'
});
});
it('Ignores empty parts', function() {
var query = OC.parseQueryString('&q=a&&b=x&');
expect(query).toEqual({q: 'a', b: 'x'});
});
it('Ignores lone equal signs', function() {
var query = OC.parseQueryString('&q=a&=&b=x&');
expect(query).toEqual({q: 'a', b: 'x'});
});
it('Includes extra equal signs in value', function() {
var query = OC.parseQueryString('u=a=x&q=a=b');
expect(query).toEqual({u: 'a=x', q: 'a=b'});
});
it('Decodes plus as space', function() {
var query = OC.parseQueryString('space+key=space+value');
expect(query).toEqual({'space key': 'space value'});
});
it('Decodes special characters', function() {
var query = OC.parseQueryString('unicode=%E6%B1%89%E5%AD%97');
expect(query).toEqual({unicode: '汉字'});
query = OC.parseQueryString('b=spaace%20value&space%20key=normalvalue&slash%2Fthis=amp%26ersand');
expect(query).toEqual({
b: 'spaace value',
'space key': 'normalvalue',
'slash/this': 'amp&ersand'
});
});
it('Decodes empty values', function() {
var query = OC.parseQueryString('keywithemptystring=&keywithnostring');
expect(query).toEqual({
'keywithemptystring': '',
'keywithnostring': null
});
});
it('Does not interpret data types', function() {
var query = OC.parseQueryString('booleanfalse=false&booleantrue=true&number=123');
expect(query).toEqual({
'booleanfalse': 'false',
'booleantrue': 'true',
'number': '123'
});
});
});
describe('Generate Url', function() {
it('returns absolute urls', function() {