Browse Source

Make files app use Webdav for most operations

tags/v9.0beta1
Vincent Petry 9 years ago
parent
commit
fa2be0750c

+ 0
- 81
apps/files/ajax/delete.php View File

@@ -1,81 +0,0 @@
<?php
/**
* @author Arthur Schiwon <blizzz@owncloud.com>
* @author Frank Karlitschek <frank@owncloud.org>
* @author Jakob Sack <mail@jakobsack.de>
* @author Joas Schilling <nickvergessen@owncloud.com>
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
* @author Lukas Reschke <lukas@owncloud.com>
* @author Robin Appelman <icewind@owncloud.com>
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Vincent Petry <pvince81@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
OCP\JSON::checkLoggedIn();
OCP\JSON::callCheck();
\OC::$server->getSession()->close();


// Get data
$dir = isset($_POST['dir']) ? (string)$_POST['dir'] : '';
$allFiles = isset($_POST["allfiles"]) ? (string)$_POST["allfiles"] : false;

// delete all files in dir ?
if ($allFiles === 'true') {
$files = array();
$fileList = \OC\Files\Filesystem::getDirectoryContent($dir);
foreach ($fileList as $fileInfo) {
$files[] = $fileInfo['name'];
}
} else {
$files = isset($_POST["file"]) ? (string)$_POST["file"] : (string)$_POST["files"];
$files = json_decode($files);
}
$filesWithError = '';

$success = true;

//Now delete
foreach ($files as $file) {
try {
if (\OC\Files\Filesystem::file_exists($dir . '/' . $file) &&
!(\OC\Files\Filesystem::isDeletable($dir . '/' . $file) &&
\OC\Files\Filesystem::unlink($dir . '/' . $file))
) {
$filesWithError .= $file . "\n";
$success = false;
}
} catch (\Exception $e) {
$filesWithError .= $file . "\n";
$success = false;
}
}

// get array with updated storage stats (e.g. max file size) after upload
try {
$storageStats = \OCA\Files\Helper::buildFileStorageStatistics($dir);
} catch(\OCP\Files\NotFoundException $e) {
OCP\JSON::error(['data' => ['message' => 'File not found']]);
return;
}

if ($success) {
OCP\JSON::success(array("data" => array_merge(array("dir" => $dir, "files" => $files), $storageStats)));
} else {
OCP\JSON::error(array("data" => array_merge(array("message" => "Could not delete:\n" . $filesWithError), $storageStats)));
}

+ 0
- 59
apps/files/ajax/move.php View File

@@ -1,59 +0,0 @@
<?php
/**
* @author Björn Schießle <schiessle@owncloud.com>
* @author Frank Karlitschek <frank@owncloud.org>
* @author Georg Ehrke <georg@owncloud.com>
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
* @author Lukas Reschke <lukas@owncloud.com>
* @author Robin Appelman <icewind@owncloud.com>
* @author Vincent Petry <pvince81@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
OCP\JSON::checkLoggedIn();
OCP\JSON::callCheck();
\OC::$server->getSession()->close();

// Get data
$dir = isset($_POST['dir']) ? (string)$_POST['dir'] : '';
$file = isset($_POST['file']) ? (string)$_POST['file'] : '';
$target = isset($_POST['target']) ? rawurldecode((string)$_POST['target']) : '';

$l = \OC::$server->getL10N('files');

if(\OC\Files\Filesystem::file_exists($target . '/' . $file)) {
OCP\JSON::error(array("data" => array( "message" => $l->t("Could not move %s - File with this name already exists", array($file)) )));
exit;
}

if ($target != '' || strtolower($file) != 'shared') {
$targetFile = \OC\Files\Filesystem::normalizePath($target . '/' . $file);
$sourceFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $file);
try {
if(\OC\Files\Filesystem::rename($sourceFile, $targetFile)) {
OCP\JSON::success(array("data" => array( "dir" => $dir, "files" => $file )));
} else {
OCP\JSON::error(array("data" => array( "message" => $l->t("Could not move %s", array($file)) )));
}
} catch (\OCP\Files\NotPermittedException $e) {
OCP\JSON::error(array("data" => array( "message" => $l->t("Permission denied") )));
} catch (\Exception $e) {
OCP\JSON::error(array("data" => array( "message" => $e->getMessage())));
}
}else{
OCP\JSON::error(array("data" => array( "message" => $l->t("Could not move %s", array($file)) )));
}

+ 0
- 103
apps/files/ajax/newfile.php View File

@@ -1,103 +0,0 @@
<?php
/**
* @author Andreas Fischer <bantu@owncloud.com>
* @author Georg Ehrke <georg@owncloud.com>
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
* @author Lukas Reschke <lukas@owncloud.com>
* @author Robin Appelman <icewind@owncloud.com>
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Vincent Petry <pvince81@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
// Init owncloud
global $eventSource;

\OCP\JSON::checkLoggedIn();
\OCP\JSON::callCheck();

\OC::$server->getSession()->close();

// Get the params
$dir = isset( $_REQUEST['dir'] ) ? '/'.trim((string)$_REQUEST['dir'], '/\\') : '';
$fileName = isset( $_REQUEST['filename'] ) ? trim((string)$_REQUEST['filename'], '/\\') : '';

$l10n = \OC::$server->getL10N('files');

$result = array(
'success' => false,
'data' => NULL
);

try {
\OC\Files\Filesystem::getView()->verifyPath($dir, $fileName);
} catch (\OCP\Files\InvalidPathException $ex) {
$result['data'] = [
'message' => $ex->getMessage()];
OCP\JSON::error($result);
return;
}

if (!\OC\Files\Filesystem::file_exists($dir . '/')) {
$result['data'] = array('message' => (string)$l10n->t(
'The target folder has been moved or deleted.'),
'code' => 'targetnotfound'
);
OCP\JSON::error($result);
exit();
}

$target = $dir.'/'.$fileName;

if (\OC\Files\Filesystem::file_exists($target)) {
$result['data'] = array('message' => (string)$l10n->t(
'The name %s is already used in the folder %s. Please choose a different name.',
array($fileName, $dir))
);
OCP\JSON::error($result);
exit();
}

$success = false;
$templateManager = OC_Helper::getFileTemplateManager();
$mimeType = OC_Helper::getMimetypeDetector()->detectPath($target);
$content = $templateManager->getTemplate($mimeType);

try {
if($content) {
$success = \OC\Files\Filesystem::file_put_contents($target, $content);
} else {
$success = \OC\Files\Filesystem::touch($target);
}
} catch (\Exception $e) {
$result = [
'success' => false,
'data' => [
'message' => $e->getMessage()
]
];
OCP\JSON::error($result);
exit();
}

if($success) {
$meta = \OC\Files\Filesystem::getFileInfo($target);
OCP\JSON::success(array('data' => \OCA\Files\Helper::formatFileInfo($meta)));
return;
}

OCP\JSON::error(array('data' => array( 'message' => $l10n->t('Error when creating the file') )));

+ 0
- 99
apps/files/ajax/newfolder.php View File

@@ -1,99 +0,0 @@
<?php
/**
* @author Arthur Schiwon <blizzz@owncloud.com>
* @author Björn Schießle <schiessle@owncloud.com>
* @author Frank Karlitschek <frank@owncloud.org>
* @author Georg Ehrke <georg@owncloud.com>
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
* @author Lukas Reschke <lukas@owncloud.com>
* @author Robin Appelman <icewind@owncloud.com>
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Vincent Petry <pvince81@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
// Init owncloud


OCP\JSON::checkLoggedIn();
OCP\JSON::callCheck();
\OC::$server->getSession()->close();

// Get the params
$dir = isset($_POST['dir']) ? (string)$_POST['dir'] : '';
$folderName = isset($_POST['foldername']) ?(string) $_POST['foldername'] : '';

$l10n = \OC::$server->getL10N('files');

$result = array(
'success' => false,
'data' => NULL
);

try {
\OC\Files\Filesystem::getView()->verifyPath($dir, $folderName);
} catch (\OCP\Files\InvalidPathException $ex) {
$result['data'] = [
'message' => $ex->getMessage()];
OCP\JSON::error($result);
return;
}

if (!\OC\Files\Filesystem::file_exists($dir . '/')) {
$result['data'] = array('message' => (string)$l10n->t(
'The target folder has been moved or deleted.'),
'code' => 'targetnotfound'
);
OCP\JSON::error($result);
exit();
}

$target = $dir . '/' . $folderName;
if (\OC\Files\Filesystem::file_exists($target)) {
$result['data'] = array('message' => $l10n->t(
'The name %s is already used in the folder %s. Please choose a different name.',
array($folderName, $dir))
);
OCP\JSON::error($result);
exit();
}

try {
if(\OC\Files\Filesystem::mkdir($target)) {
if ( $dir !== '/') {
$path = $dir.'/'.$folderName;
} else {
$path = '/'.$folderName;
}
$meta = \OC\Files\Filesystem::getFileInfo($path);
$meta['type'] = 'dir'; // missing ?!
OCP\JSON::success(array('data' => \OCA\Files\Helper::formatFileInfo($meta)));
exit();
}
} catch (\Exception $e) {
$result = [
'success' => false,
'data' => [
'message' => $e->getMessage()
]
];
OCP\JSON::error($result);
exit();
}

OCP\JSON::error(array('data' => array( 'message' => $l10n->t('Error when creating the folder') )));

+ 0
- 58
apps/files/ajax/rename.php View File

@@ -1,58 +0,0 @@
<?php
/**
* @author Christopher Schäpers <kondou@ts.unde.re>
* @author Frank Karlitschek <frank@owncloud.org>
* @author Jakob Sack <mail@jakobsack.de>
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
* @author Lukas Reschke <lukas@owncloud.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <icewind@owncloud.com>
* @author Vincent Petry <pvince81@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/

OCP\JSON::checkLoggedIn();
OCP\JSON::callCheck();
\OC::$server->getSession()->close();

$l10n = \OC::$server->getL10N('files');

$files = new \OCA\Files\App(
\OC\Files\Filesystem::getView(),
\OC::$server->getL10N('files')
);
try {
$result = $files->rename(
isset($_GET['dir']) ? (string)$_GET['dir'] : '',
isset($_GET['file']) ? (string)$_GET['file'] : '',
isset($_GET['newname']) ? (string)$_GET['newname'] : ''
);
} catch (\Exception $e) {
$result = [
'success' => false,
'data' => [
'message' => $e->getMessage()
]
];
}

if($result['success'] === true){
OCP\JSON::success(['data' => $result['data']]);
} else {
OCP\JSON::error(['data' => $result['data']]);
}

+ 2
- 1
apps/files/js/app.js View File

@@ -71,7 +71,8 @@
folderDropOptions: folderDropOptions,
fileActions: fileActions,
allowLegacyActions: true,
scrollTo: urlParams.scrollto
scrollTo: urlParams.scrollto,
filesClient: OC.Files.getClient()
}
);
this.files.initialize();

+ 2
- 1
apps/files/js/fileactions.js View File

@@ -575,7 +575,8 @@
},
actionHandler: function (filename, context) {
var dir = context.dir || context.fileList.getCurrentDirectory();
var url = context.fileList.getDownloadUrl(filename, dir);
var isDir = context.$file.attr('data-type') === 'dir';
var url = context.fileList.getDownloadUrl(filename, dir, isDir);

var downloadFileaction = $(context.$file).find('.fileactions .action-download');


+ 324
- 248
apps/files/js/filelist.js View File

@@ -22,11 +22,12 @@
*
* @param $el container element with existing markup for the #controls
* and a table
* @param [options] map of options, see other parameters
* @param [options.scrollContainer] scrollable container, defaults to $(window)
* @param [options.dragOptions] drag options, disabled by default
* @param [options.folderDropOptions] folder drop options, disabled by default
* @param [options.detailsViewEnabled=true] whether to enable details view
* @param {Object} [options] map of options, see other parameters
* @param {Object} [options.scrollContainer] scrollable container, defaults to $(window)
* @param {Object} [options.dragOptions] drag options, disabled by default
* @param {Object} [options.folderDropOptions] folder drop options, disabled by default
* @param {boolean} [options.detailsViewEnabled=true] whether to enable details view
* @param {OC.Files.Client} [options.filesClient] files client to use
*/
var FileList = function($el, options) {
this.initialize($el, options);
@@ -73,6 +74,13 @@
*/
_detailsView: null,

/**
* Files client instance
*
* @type OC.Files.Client
*/
filesClient: null,

/**
* Whether the file list was initialized already.
* @type boolean
@@ -92,10 +100,17 @@
* Array of files in the current folder.
* The entries are of file data.
*
* @type Array.<Object>
* @type Array.<OC.Files.FileInfo>
*/
files: [],

/**
* Current directory entry
*
* @type OC.Files.FileInfo
*/
dirInfo: null,

/**
* File actions handler, defaults to OCA.Files.FileActions
* @type OCA.Files.FileActions
@@ -149,7 +164,7 @@
* When false, clicking on a table header will call reload().
* When true, clicking on a table header will simply resort the list.
*/
_clientSideSort: false,
_clientSideSort: true,

/**
* Current directory
@@ -170,6 +185,7 @@
* @param options.dragOptions drag options, disabled by default
* @param options.folderDropOptions folder drop options, disabled by default
* @param options.scrollTo name of file to scroll to after the first load
* @param {OC.Files.Client} [options.filesClient] files API client
* @private
*/
initialize: function($el, options) {
@@ -185,6 +201,12 @@
if (options.folderDropOptions) {
this._folderDropOptions = options.folderDropOptions;
}
if (options.filesClient) {
this.filesClient = options.filesClient;
} else {
// default client if not specified
this.filesClient = OC.Files.getClient();
}

this.$el = $el;
if (options.id) {
@@ -209,6 +231,8 @@
this.files = [];
this._selectedFiles = {};
this._selectionSummary = new OCA.Files.FileSummary();
// dummy root dir info
this.dirInfo = new OC.Files.FileInfo({});

this.fileSummary = this._createSummary();

@@ -359,7 +383,7 @@
var highlightState = $tr.hasClass('highlighted');
$tr = self.updateRow(
$tr,
_.extend({isPreviewAvailable: true}, model.toJSON()),
model.toJSON(),
{updateSummary: true, silent: false, animate: true}
);
$tr.toggleClass('highlighted', highlightState);
@@ -618,7 +642,7 @@
};

OCA.Files.FileActions.updateFileActionSpinner(downloadFileaction, true);
OCA.Files.Files.handleDownload(this.getDownloadUrl(files, dir), disableLoadingState);
OCA.Files.Files.handleDownload(this.getDownloadUrl(files, dir, true), disableLoadingState);
return false;
},

@@ -894,16 +918,39 @@
self.$el.closest('#app-content').trigger(jQuery.Event('apprendered'));
});
},

/**
* Returns the icon URL matching the given file info
*
* @param {OC.Files.FileInfo} fileInfo file info
*
* @return {string} icon URL
*/
_getIconUrl: function(fileInfo) {
var mimeType = fileInfo.mimetype || 'application/octet-stream';
if (mimeType === 'httpd/unix-directory') {
// use default folder icon
if (fileInfo.mountType === 'shared' || fileInfo.mountType === 'shared-root') {
return OC.MimeType.getIconUrl('dir-shared');
} else if (fileInfo.mountType === 'external-root') {
return OC.MimeType.getIconUrl('dir-external');
}
return OC.MimeType.getIconUrl('dir');
}
return OC.MimeType.getIconUrl(mimeType);
},

/**
* Creates a new table row element using the given file data.
* @param {OCA.Files.FileInfo} fileData file info attributes
* @param {OC.Files.FileInfo} fileData file info attributes
* @param options map of attributes
* @return new tr element (not appended to the table)
*/
_createRow: function(fileData, options) {
var td, simpleSize, basename, extension, sizeColor,
icon = OC.MimeType.getIconUrl(fileData.mimetype),
icon = fileData.icon || this._getIconUrl(fileData),
name = fileData.name,
// TODO: get rid of type, only use mime type
type = fileData.type || 'file',
mtime = parseInt(fileData.mtime, 10),
mime = fileData.mimetype,
@@ -943,6 +990,14 @@
}

if (fileData.mountType) {
// FIXME: HACK: detect shared-root
if (fileData.mountType === 'shared' && this.dirInfo.mountType !== 'shared') {
// if parent folder isn't share, assume the displayed folder is a share root
fileData.mountType = 'shared-root';
} else if (fileData.mountType === 'external' && this.dirInfo.mountType !== 'external') {
// if parent folder isn't external, assume the displayed folder is the external storage root
fileData.mountType = 'external-root';
}
tr.attr('data-mounttype', fileData.mountType);
}

@@ -953,24 +1008,16 @@
path = this.getCurrentDirectory();
}

if (type === 'dir') {
// use default folder icon
icon = icon || OC.imagePath('core', 'filetypes/folder');
}
else {
icon = icon || OC.imagePath('core', 'filetypes/file');
}

// filename td
td = $('<td class="filename"></td>');


// linkUrl
if (type === 'dir') {
if (mime === 'httpd/unix-directory') {
linkUrl = this.linkTo(path + '/' + name);
}
else {
linkUrl = this.getDownloadUrl(name, path);
linkUrl = this.getDownloadUrl(name, path, type === 'dir');
}
if (this._allowSelection) {
td.append(
@@ -996,7 +1043,7 @@
basename = '';
extension = name;
// split extension from filename for non dirs
} else if (type !== 'dir' && name.indexOf('.') !== -1) {
} else if (mime !== 'httpd/unix-directory' && name.indexOf('.') !== -1) {
basename = name.substr(0, name.lastIndexOf('.'));
extension = name.substr(name.lastIndexOf('.'));
} else {
@@ -1018,7 +1065,7 @@
nameSpan.tooltip({placement: 'right'});
}
// dirs can show the number of uploaded files
if (type === 'dir') {
if (mime !== 'httpd/unix-directory') {
linkElem.append($('<span></span>').attr({
'class': 'uploadtext',
'currentUploads': 0
@@ -1074,7 +1121,7 @@
* Adds an entry to the files array and also into the DOM
* in a sorted manner.
*
* @param {OCA.Files.FileInfo} fileData map of file attributes
* @param {OC.Files.FileInfo} fileData map of file attributes
* @param {Object} [options] map of attributes
* @param {boolean} [options.updateSummary] true to update the summary
* after adding (default), false otherwise. Defaults to true.
@@ -1147,7 +1194,7 @@
* Creates a new row element based on the given attributes
* and returns it.
*
* @param {OCA.Files.FileInfo} fileData map of file attributes
* @param {OC.Files.FileInfo} fileData map of file attributes
* @param {Object} [options] map of attributes
* @param {int} [options.index] index at which to insert the element
* @param {boolean} [options.updateSummary] true to update the summary
@@ -1182,7 +1229,7 @@
filenameTd.draggable(this._dragOptions);
}
// allow dropping on folders
if (this._folderDropOptions && fileData.type === 'dir') {
if (this._folderDropOptions && mime === 'httpd/unix-directory') {
filenameTd.droppable(this._folderDropOptions);
}

@@ -1193,7 +1240,7 @@
// display actions
this.fileActions.display(filenameTd, !options.silent, this);

if (fileData.isPreviewAvailable && mime !== 'httpd/unix-directory') {
if (mime !== 'httpd/unix-directory') {
var iconDiv = filenameTd.find('.thumbnail');
// lazy load / newly inserted td ?
// the typeof check ensures that the default value of animate is true
@@ -1343,17 +1390,7 @@
this._currentFileModel = null;
this.$el.find('.select-all').prop('checked', false);
this.showMask();
if (this._reloadCall) {
this._reloadCall.abort();
}
this._reloadCall = $.ajax({
url: this.getAjaxUrl('list'),
data: {
dir : this.getCurrentDirectory(),
sort: this._sort,
sortdirection: this._sortDirection
}
});
this._reloadCall = this.filesClient.getFolderContents(this.getCurrentDirectory(), {includeParent: true});
if (this._detailsView) {
// close sidebar
this._updateDetailsView(null);
@@ -1361,24 +1398,19 @@
var callBack = this.reloadCallback.bind(this);
return this._reloadCall.then(callBack, callBack);
},
reloadCallback: function(result) {
reloadCallback: function(status, result) {
delete this._reloadCall;
this.hideMask();

if (!result || result.status === 'error') {
// if the error is not related to folder we're trying to load, reload the page to handle logout etc
if (result.data.error === 'authentication_error' ||
result.data.error === 'token_expired' ||
result.data.error === 'application_not_enabled'
) {
OC.redirect(OC.generateUrl('apps/files'));
}
OC.Notification.showTemporary(result.data.message);
if (status === 401) {
// TODO: append current URL to be able to get back after logging in again
OC.redirect(OC.generateUrl('apps/files'));
OC.Notification.show(result);
return false;
}

// Firewall Blocked request?
if (result.status === 403) {
if (status === 403) {
// Go home
this.changeDirectory('/');
OC.Notification.showTemporary(t('files', 'This operation is forbidden'));
@@ -1386,32 +1418,49 @@
}

// Did share service die or something else fail?
if (result.status === 500) {
if (status === 500) {
// Go home
this.changeDirectory('/');
OC.Notification.showTemporary(t('files', 'This directory is unavailable, please check the logs or contact the administrator'));
OC.Notification.showTemporary(
t('files', 'This directory is unavailable, please check the logs or contact the administrator')
);
return false;
}

if (status === 503) {
// Go home
if (this.getCurrentDirectory() !== '/') {
this.changeDirectory('/');
// TODO: read error message from exception
OC.Notification.showTemporary(
t('files', 'Storage not available')
);
}
return false;
}

if (result.status === 404) {
if (status === 404) {
// go back home
this.changeDirectory('/');
return false;
}
// aborted ?
if (result.status === 0){
if (status === 0){
return true;
}

// TODO: should rather return upload file size through
// the files list ajax call
// TODO: parse remaining quota from PROPFIND response
this.updateStorageStatistics(true);

if (result.data.permissions) {
this.setDirectoryPermissions(result.data.permissions);
// first entry is the root
this.dirInfo = result.shift();

if (this.dirInfo.permissions) {
this.setDirectoryPermissions(this.dirInfo.permissions);
}

this.setFiles(result.data.files);
result.sort(this._sortComparator);
this.setFiles(result);
return true;
},

@@ -1419,12 +1468,15 @@
OCA.Files.Files.updateStorageStatistics(this.getCurrentDirectory(), force);
},

/**
* @deprecated do not use nor override
*/
getAjaxUrl: function(action, params) {
return OCA.Files.Files.getAjaxUrl(action, params);
},

getDownloadUrl: function(files, dir) {
return OCA.Files.Files.getDownloadUrl(files, dir || this.getCurrentDirectory());
getDownloadUrl: function(files, dir, isDir) {
return OCA.Files.Files.getDownloadUrl(files, dir || this.getCurrentDirectory(), isDir);
},

/**
@@ -1489,8 +1541,6 @@
if (etag){
// use etag as cache buster
urlSpec.c = etag;
} else {
console.warn('OCA.Files.FileList.lazyLoadPreview(): missing etag argument');
}

previewURL = self.generatePreviewUrl(urlSpec);
@@ -1514,6 +1564,9 @@
img.src = previewURL;
},

/**
* @deprecated
*/
setDirectoryPermissions: function(permissions) {
var isCreatable = (permissions & OC.PERMISSION_CREATE) !== 0;
this.$el.find('#permissions').val(permissions);
@@ -1610,7 +1663,7 @@
* fileData should be inserted, considering the current
* sorting
*
* @param {OCA.Files.FileInfo} fileData file info
* @param {OC.Files.FileInfo} fileData file info
*/
_findInsertionIndex: function(fileData) {
var index = 0;
@@ -1628,6 +1681,9 @@
move: function(fileNames, targetPath) {
var self = this;
var dir = this.getCurrentDirectory();
if (dir.charAt(dir.length - 1) !== '/') {
dir += '/';
}
var target = OC.basename(targetPath);
if (!_.isArray(fileNames)) {
fileNames = [fileNames];
@@ -1635,46 +1691,42 @@
_.each(fileNames, function(fileName) {
var $tr = self.findFileEl(fileName);
self.showFileBusyState($tr, true);
// TODO: improve performance by sending all file names in a single call
$.post(
OC.filePath('files', 'ajax', 'move.php'),
{
dir: dir,
file: fileName,
target: targetPath
},
function(result) {
if (result) {
if (result.status === 'success') {
// if still viewing the same directory
if (self.getCurrentDirectory() === dir) {
// recalculate folder size
var oldFile = self.findFileEl(target);
var newFile = self.findFileEl(fileName);
var oldSize = oldFile.data('size');
var newSize = oldSize + newFile.data('size');
oldFile.data('size', newSize);
oldFile.find('td.filesize').text(OC.Util.humanFileSize(newSize));

// TODO: also update entry in FileList.files

self.remove(fileName);
}
} else {
OC.Notification.hide();
if (result.status === 'error' && result.data.message) {
OC.Notification.showTemporary(result.data.message);
}
else {
OC.Notification.showTemporary(t('files', 'Error moving file.'));
}
}
if (targetPath.charAt(targetPath.length - 1) !== '/') {
// make sure we move the files into the target dir,
// not overwrite it
targetPath = targetPath + '/';
}
self.filesClient.move(dir + fileName, targetPath + fileName)
.done(function() {
// if still viewing the same directory
if (OC.joinPaths(self.getCurrentDirectory(), '/') === dir) {
// recalculate folder size
var oldFile = self.findFileEl(target);
var newFile = self.findFileEl(fileName);
var oldSize = oldFile.data('size');
var newSize = oldSize + newFile.data('size');
oldFile.data('size', newSize);
oldFile.find('td.filesize').text(OC.Util.humanFileSize(newSize));

// TODO: also update entry in FileList.files
self.remove(fileName);
}
})
.fail(function(status) {
if (status === 412) {
// TODO: some day here we should invoke the conflict dialog
OC.Notification.showTemporary(
t('files', 'Could not move "{file}", target exists', {file: fileName})
);
} else {
OC.dialogs.alert(t('files', 'Error moving file'), t('files', 'Error'));
OC.Notification.showTemporary(
t('files', 'Could not move "{file}"', {file: fileName})
);
}
})
.always(function() {
self.showFileBusyState($tr, false);
}
);
});
});

},
@@ -1700,16 +1752,16 @@
* Triggers file rename input field for the given file name.
* If the user enters a new name, the file will be renamed.
*
* @param oldname file name of the file to rename
* @param oldName file name of the file to rename
*/
rename: function(oldname) {
rename: function(oldName) {
var self = this;
var tr, td, input, form;
tr = this.findFileEl(oldname);
tr = this.findFileEl(oldName);
var oldFileInfo = this.files[tr.index()];
tr.data('renaming',true);
td = tr.children('td.filename');
input = $('<input type="text" class="filename"/>').val(oldname);
input = $('<input type="text" class="filename"/>').val(oldName);
form = $('<form></form>');
form.append(input);
td.children('a.name').hide();
@@ -1724,11 +1776,11 @@
input.selectRange(0, len);
var checkInput = function () {
var filename = input.val();
if (filename !== oldname) {
if (filename !== oldName) {
// Files.isFileNameValid(filename) throws an exception itself
OCA.Files.Files.isFileNameValid(filename);
if (self.inList(filename)) {
throw t('files', '{new_name} already exists', {new_name: filename});
throw t('files', '{newName} already exists', {newName: filename});
}
}
return true;
@@ -1741,6 +1793,14 @@
td.children('a.name').show();
}

function updateInList(fileInfo) {
tr.remove();
tr = self.add(fileInfo, {updateSummary: false, silent: true});
self.$fileList.trigger($.Event('fileActionsReady', {fileList: self, $files: $(tr)}));
self._updateDetailsView(fileInfo.name);
}

// TODO: too many nested blocks, move parts into functions
form.submit(function(event) {
event.stopPropagation();
event.preventDefault();
@@ -1753,7 +1813,7 @@
input.tooltip('hide');
form.remove();

if (newName !== oldname) {
if (newName !== oldName) {
checkInput();
// mark as loading (temp element)
self.showFileBusyState(tr, true);
@@ -1765,34 +1825,46 @@
td.find('a.name span.nametext').text(basename);
td.children('a.name').show();

$.ajax({
url: OC.filePath('files','ajax','rename.php'),
data: {
dir : tr.attr('data-path') || self.getCurrentDirectory(),
newname: newName,
file: oldname
},
success: function(result) {
var fileInfo;
if (!result || result.status === 'error') {
OC.dialogs.alert(result.data.message, t('files', 'Could not rename file'));
fileInfo = oldFileInfo;
if (result.data.code === 'sourcenotfound') {
self.remove(result.data.newname, {updateSummary: true});
return;
}
}
else {
fileInfo = result.data;
var path = tr.attr('data-path') || self.getCurrentDirectory();
self.filesClient.move(path + '/' + oldName, path + '/' + newName)
.done(function() {
var fileInfo = self.files.splice(tr.index(), 1)[0];
fileInfo.name = newName;
updateInList(fileInfo);
})
.fail(function(status) {
// TODO: 409 means current folder does not exist, redirect ?
if (status === 404) {
// source not found, so remove it from the list
OC.Notification.showTemporary(
t(
'files',
'Could not rename "{fileName}", it does not exist any more',
{fileName: oldName}
)
);
self.remove(newName, {updateSummary: true});
return;
} else if (status === 412) {
// target exists
OC.Notification.showTemporary(
t(
'files',
'The name "{targetName}" is already used in the folder "{dir}". Please choose a different name.',
{
targetName: newName,
dir: self.getCurrentDirectory()
}
)
);
} else {
// restore the item to its previous state
OC.Notification.showTemporary(
t('files', 'Could not rename "{fileName}"', {fileName: oldName})
);
}
// reinsert row
self.files.splice(tr.index(), 1);
tr.remove();
tr = self.add(fileInfo, {updateSummary: false, silent: true});
self.$fileList.trigger($.Event('fileActionsReady', {fileList: self, $files: $(tr)}));
self._updateDetailsView(fileInfo.name, false);
}
});
updateInList(oldFileInfo);
});
} else {
// add back the old file info when cancelled
self.files.splice(tr.index(), 1);
@@ -1849,32 +1921,44 @@
var promise = deferred.promise();

OCA.Files.Files.isFileNameValid(name);
name = this.getUniqueName(name);

if (this.lastAction) {
this.lastAction();
}

$.post(
OC.generateUrl('/apps/files/ajax/newfile.php'),
{
dir: this.getCurrentDirectory(),
filename: name
},
function(result) {
if (result.status === 'success') {
self.add(result.data, {animate: true, scrollTo: true});
deferred.resolve(result.status, result.data);
name = this.getUniqueName(name);
var targetPath = this.getCurrentDirectory() + '/' + name;

self.filesClient.putFileContents(
targetPath,
'',
{
contentType: 'text/plain',
overwrite: true
}
)
.done(function() {
// TODO: error handling / conflicts
self.filesClient.getFileInfo(targetPath)
.then(function(status, data) {
self.add(data, {animate: true, scrollTo: true});
deferred.resolve(status, data);
})
.fail(function(status) {
OC.Notification.showTemporary(t('files', 'Could not create file "{file}"', {file: name}));
deferred.reject(status);
});
})
.fail(function(status) {
if (status === 412) {
OC.Notification.showTemporary(
t('files', 'Could not create file "{file}" because it already exists', {file: name})
);
} else {
if (result.data && result.data.message) {
OC.Notification.showTemporary(result.data.message);
} else {
OC.Notification.showTemporary(t('core', 'Could not create file'));
}
deferred.reject(result.status, result.data);
OC.Notification.showTemporary(t('files', 'Could not create file "{file}"', {file: name}));
}
}
);
deferred.reject(status);
});

return promise;
},
@@ -1895,32 +1979,50 @@
var promise = deferred.promise();

OCA.Files.Files.isFileNameValid(name);
name = this.getUniqueName(name);

if (this.lastAction) {
this.lastAction();
}

$.post(
OC.generateUrl('/apps/files/ajax/newfolder.php'),
{
dir: this.getCurrentDirectory(),
foldername: name
},
function(result) {
if (result.status === 'success') {
self.add(result.data, {animate: true, scrollTo: true});
deferred.resolve(result.status, result.data);
name = this.getUniqueName(name);
var targetPath = this.getCurrentDirectory() + '/' + name;

this.filesClient.createDirectory(targetPath)
.done(function(createStatus) {
self.filesClient.getFileInfo(targetPath)
.done(function(status, data) {
self.add(data, {animate: true, scrollTo: true});
deferred.resolve(status, data);
})
.fail(function() {
OC.Notification.showTemporary(t('files', 'Could not create folder "{dir}"', {dir: name}));
deferred.reject(createStatus);
});
})
.fail(function(createStatus) {
// method not allowed, folder might exist already
if (createStatus === 405) {
self.filesClient.getFileInfo(targetPath)
.done(function(status, data) {
// add it to the list, for completeness
self.add(data, {animate: true, scrollTo: true});
OC.Notification.showTemporary(
t('files', 'Could not create folder "{dir}" because it already exists', {dir: name})
);
// still consider a failure
deferred.reject(createStatus, data);
})
.fail(function() {
OC.Notification.showTemporary(
t('files', 'Could not create folder "{dir}"', {dir: name})
);
deferred.reject(status);
});
} else {
if (result.data && result.data.message) {
OC.Notification.showTemporary(result.data.message);
} else {
OC.Notification.showTemporary(t('core', 'Could not create folder'));
}
deferred.reject(result.status);
OC.Notification.showTemporary(t('files', 'Could not create folder "{dir}"', {dir: name}));
deferred.reject(createStatus);
}
}
);
});

return promise;
},
@@ -1981,76 +2083,59 @@
*/
do_delete:function(files, dir) {
var self = this;
var params;
if (files && files.substr) {
files=[files];
}
if (!files) {
// delete all files in directory
files = _.pluck(this.files, 'name');
}
if (files) {
this.showFileBusyState(files, true);
for (var i=0; i<files.length; i++) {
}
}
// Finish any existing actions
if (this.lastAction) {
this.lastAction();
}

params = {
dir: dir || this.getCurrentDirectory()
};
if (files) {
params.files = JSON.stringify(files);
dir = dir || this.getCurrentDirectory();

function removeFromList(file) {
var fileEl = self.remove(file, {updateSummary: false});
// FIXME: not sure why we need this after the
// element isn't even in the DOM any more
fileEl.find('.selectCheckBox').prop('checked', false);
fileEl.removeClass('selected');
self.fileSummary.remove({type: fileEl.attr('data-type'), size: fileEl.attr('data-size')});
// TODO: this info should be returned by the ajax call!
self.updateEmptyContent();
self.fileSummary.update();
self.updateSelectionSummary();
// FIXME: don't repeat this, do it once all files are done
self.updateStorageStatistics();
}
else {
// no files passed, delete all in current dir
params.allfiles = true;
// show spinner for all files
this.showFileBusyState(this.$fileList.find('tr'), true);
}

$.post(OC.filePath('files', 'ajax', 'delete.php'),
params,
function(result) {
if (result.status === 'success') {
if (params.allfiles) {
self.setFiles([]);
}
else {
$.each(files,function(index,file) {
var fileEl = self.remove(file, {updateSummary: false});
// FIXME: not sure why we need this after the
// element isn't even in the DOM any more
fileEl.find('.selectCheckBox').prop('checked', false);
fileEl.removeClass('selected');
self.fileSummary.remove({type: fileEl.attr('data-type'), size: fileEl.attr('data-size')});
});
}
// TODO: this info should be returned by the ajax call!
self.updateEmptyContent();
self.fileSummary.update();
self.updateSelectionSummary();
self.updateStorageStatistics();
// in case there was a "storage full" permanent notification
OC.Notification.hide();

_.each(files, function(file) {
self.filesClient.remove(dir + '/' + file)
.done(function() {
removeFromList(file);
})
.fail(function(status) {
if (status === 404) {
// the file already did not exist, remove it from the list
removeFromList(file);
} else {
if (result.status === 'error' && result.data.message) {
OC.Notification.showTemporary(result.data.message);
}
else {
OC.Notification.showTemporary(t('files', 'Error deleting file.'));
}
if (params.allfiles) {
// reload the page as we don't know what files were deleted
// and which ones remain
self.reload();
}
else {
$.each(files,function(index,file) {
self.showFileBusyState(file, false);
});
}
// only reset the spinner for that one file
OC.Notification.showTemporary(
t('files', 'Error deleting file "{fileName}".', {fileName: file}),
{timeout: 10}
);
var deleteAction = self.findFileEl(file).find('.action.delete');
deleteAction.removeClass('icon-loading-small').addClass('icon-delete');
self.showFileBusyState(files, false);
}
});
});
},
/**
* Creates the file summary section
@@ -2659,8 +2744,8 @@
* Compares two file infos by name, making directories appear
* first.
*
* @param {OCA.Files.FileInfo} fileInfo1 file info
* @param {OCA.Files.FileInfo} fileInfo2 file info
* @param {OC.Files.FileInfo} fileInfo1 file info
* @param {OC.Files.FileInfo} fileInfo2 file info
* @return {int} -1 if the first file must appear before the second one,
* 0 if they are identify, 1 otherwise.
*/
@@ -2676,8 +2761,8 @@
/**
* Compares two file infos by size.
*
* @param {OCA.Files.FileInfo} fileInfo1 file info
* @param {OCA.Files.FileInfo} fileInfo2 file info
* @param {OC.Files.FileInfo} fileInfo1 file info
* @param {OC.Files.FileInfo} fileInfo2 file info
* @return {int} -1 if the first file must appear before the second one,
* 0 if they are identify, 1 otherwise.
*/
@@ -2687,8 +2772,8 @@
/**
* Compares two file infos by timestamp.
*
* @param {OCA.Files.FileInfo} fileInfo1 file info
* @param {OCA.Files.FileInfo} fileInfo2 file info
* @param {OC.Files.FileInfo} fileInfo1 file info
* @param {OC.Files.FileInfo} fileInfo2 file info
* @return {int} -1 if the first file must appear before the second one,
* 0 if they are identify, 1 otherwise.
*/
@@ -2700,23 +2785,14 @@
/**
* File info attributes.
*
* @todo make this a real class in the future
* @typedef {Object} OCA.Files.FileInfo
* @typedef {Object} OC.Files.FileInfo
*
* @lends OC.Files.FileInfo
*
* @deprecated use OC.Files.FileInfo instead
*
* @property {int} id file id
* @property {String} name file name
* @property {String} [path] file path, defaults to the list's current path
* @property {String} mimetype mime type
* @property {String} type "file" for files or "dir" for directories
* @property {int} permissions file permissions
* @property {int} mtime modification time in milliseconds
* @property {boolean} [isShareMountPoint] whether the file is a share mount
* point
* @property {boolean} [isPreviewAvailable] whether a preview is available
* for the given file type
* @property {String} [icon] path to the mime type icon
* @property {String} etag etag of the file
*/
OCA.Files.FileInfo = OC.Files.FileInfo;

OCA.Files.FileList = FileList;
})();

+ 23
- 7
apps/files/js/files.js View File

@@ -136,13 +136,27 @@

/**
* 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
* @param {string} filename string or array of file names to download
* @param {string} [dir] optional directory in which the file name is, defaults to the current directory
* @param {bool} [isDir=false] whether the given filename is a directory and might need a special URL
*/
getDownloadUrl: function(filename, dir) {
if ($.isArray(filename)) {
getDownloadUrl: function(filename, dir, isDir) {
if (!_.isArray(filename) && !isDir) {
var pathSections = dir.split('/');
pathSections.push(filename);
var encodedPath = '';
_.each(pathSections, function(section) {
if (section !== '') {
encodedPath += '/' + encodeURIComponent(section);
}
});
return OC.linkToRemoteBase('webdav') + encodedPath;
}

if (_.isArray(filename)) {
filename = JSON.stringify(filename);
}

var params = {
dir: dir,
files: filename
@@ -356,8 +370,10 @@ scanFiles.scanning=false;

// TODO: move to FileList
var createDragShadow = function(event) {
// FIXME: inject file list instance somehow
/* global FileList, Files */

//select dragged file
var FileList = OCA.Files.App.fileList;
var isDragSelected = $(event.target).parents('tr').find('td input:first').prop('checked');
if (!isDragSelected) {
//select dragged file
@@ -394,7 +410,7 @@ var createDragShadow = function(event) {
.css('background-image', 'url(' + OC.imagePath('core', 'filetypes/folder.png') + ')');
} else {
var path = dir + '/' + elem.name;
OCA.Files.App.files.lazyLoadPreview(path, elem.mime, function(previewpath) {
Files.lazyLoadPreview(path, elem.mimetype, function(previewpath) {
newtr.find('td.filename')
.css('background-image', 'url(' + previewpath + ')');
}, null, null, elem.etag);
@@ -441,7 +457,7 @@ var folderDropOptions = {
hoverClass: "canDrop",
drop: function( event, ui ) {
// don't allow moving a file into a selected folder
var FileList = OCA.Files.App.fileList;
/* global FileList */
if ($(event.target).parents('tr').find('td input:first').prop('checked') === true) {
return false;
}

+ 1
- 87
apps/files/lib/app.php View File

@@ -28,108 +28,22 @@
namespace OCA\Files;

class App {
/**
* @var \OC_L10N
*/
private $l10n;

/**
* @var \OCP\INavigationManager
*/
private static $navigationManager;

/**
* @var \OC\Files\View
*/
private $view;

public function __construct($view, $l10n) {
$this->view = $view;
$this->l10n = $l10n;
}

/**
* Returns the app's navigation manager
*
* @return \OCP\INavigationManager
*/
public static function getNavigationManager() {
// TODO: move this into a service in the Application class
if (self::$navigationManager === null) {
self::$navigationManager = new \OC\NavigationManager();
}
return self::$navigationManager;
}

/**
* rename a file
*
* @param string $dir
* @param string $oldname
* @param string $newname
* @return array
*/
public function rename($dir, $oldname, $newname) {
$result = array(
'success' => false,
'data' => NULL
);

try {
// check if the new name is conform to file name restrictions
$this->view->verifyPath($dir, $newname);
} catch (\OCP\Files\InvalidPathException $ex) {
$result['data'] = array(
'message' => $this->l10n->t($ex->getMessage()),
'code' => 'invalidname',
);
return $result;
}

$normalizedOldPath = \OC\Files\Filesystem::normalizePath($dir . '/' . $oldname);
$normalizedNewPath = \OC\Files\Filesystem::normalizePath($dir . '/' . $newname);

// rename to non-existing folder is denied
if (!$this->view->file_exists($normalizedOldPath)) {
$result['data'] = array(
'message' => $this->l10n->t('%s could not be renamed as it has been deleted', array($oldname)),
'code' => 'sourcenotfound',
'oldname' => $oldname,
'newname' => $newname,
);
}else if (!$this->view->file_exists($dir)) {
$result['data'] = array('message' => (string)$this->l10n->t(
'The target folder has been moved or deleted.',
array($dir)),
'code' => 'targetnotfound'
);
// rename to existing file is denied
} else if ($this->view->file_exists($normalizedNewPath)) {

$result['data'] = array(
'message' => $this->l10n->t(
"The name %s is already used in the folder %s. Please choose a different name.",
array($newname, $dir))
);
} else if (
// rename to "." is denied
$newname !== '.' and
// THEN try to rename
$this->view->rename($normalizedOldPath, $normalizedNewPath)
) {
// successful rename
$meta = $this->view->getFileInfo($normalizedNewPath);
$meta = \OCA\Files\Helper::populateTags(array($meta));
$fileInfo = \OCA\Files\Helper::formatFileInfo(current($meta));
$fileInfo['path'] = dirname($normalizedNewPath);
$result['success'] = true;
$result['data'] = $fileInfo;
} else {
// rename failed
$result['data'] = array(
'message' => $this->l10n->t('%s could not be renamed', array($oldname))
);
}
return $result;
}

}

+ 0
- 3
apps/files/lib/helper.php View File

@@ -139,9 +139,6 @@ class Helper {
$entry['parentId'] = $i['parent'];
$entry['mtime'] = $i['mtime'] * 1000;
// only pick out the needed attributes
if (\OC::$server->getPreviewManager()->isAvailable($i)) {
$entry['isPreviewAvailable'] = true;
}
$entry['name'] = $i->getName();
$entry['permissions'] = $i['permissions'];
$entry['mimetype'] = $i['mimetype'];

+ 0
- 232
apps/files/tests/ajax_rename.php View File

@@ -1,232 +0,0 @@
<?php
/**
* @author Björn Schießle <schiessle@owncloud.com>
* @author Christopher Schäpers <kondou@ts.unde.re>
* @author Joas Schilling <nickvergessen@owncloud.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <icewind@owncloud.com>
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Vincent Petry <pvince81@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/

class Test_OC_Files_App_Rename extends \Test\TestCase {
private static $user;

/**
* @var PHPUnit_Framework_MockObject_MockObject
*/
private $viewMock;

/**
* @var \OCA\Files\App
*/
private $files;

protected function setUp() {
parent::setUp();

// mock OC_L10n
if (!self::$user) {
self::$user = uniqid();
}
\OC_User::createUser(self::$user, 'password');
$this->loginAsUser(self::$user);

$l10nMock = $this->getMock('\OC_L10N', array('t'), array(), '', false);
$l10nMock->expects($this->any())
->method('t')
->will($this->returnArgument(0));
$viewMock = $this->getMock('\OC\Files\View', array('rename', 'normalizePath', 'getFileInfo', 'file_exists'), array(), '', false);
$viewMock->expects($this->any())
->method('normalizePath')
->will($this->returnArgument(0));
$viewMock->expects($this->any())
->method('rename')
->will($this->returnValue(true));
$this->viewMock = $viewMock;
$this->files = new \OCA\Files\App($viewMock, $l10nMock);
}

protected function tearDown() {
$result = \OC_User::deleteUser(self::$user);
$this->assertTrue($result);

$this->logout();
parent::tearDown();
}

/**
* test rename of file/folder
*/
function testRenameFolder() {
$dir = '/';
$oldname = 'oldname';
$newname = 'newname';

$this->viewMock->expects($this->any())
->method('file_exists')
->with($this->anything())
->will($this->returnValueMap(array(
array('/', true),
array('/oldname', true)
)));


$this->viewMock->expects($this->any())
->method('getFileInfo')
->will($this->returnValue(new \OC\Files\FileInfo(
'/new_name',
new \OC\Files\Storage\Local(array('datadir' => '/')),
'/',
array(
'fileid' => 123,
'type' => 'dir',
'mimetype' => 'httpd/unix-directory',
'mtime' => 0,
'permissions' => 31,
'size' => 18,
'etag' => 'abcdef',
'directory' => '/',
'name' => 'new_name',
), null)));

$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(18, $result['data']['size']);
$this->assertEquals('httpd/unix-directory', $result['data']['mimetype']);
$this->assertEquals('abcdef', $result['data']['etag']);
$this->assertFalse(isset($result['data']['tags']));
$this->assertEquals('/', $result['data']['path']);
}

/**
* test rename of file with tag
*/
function testRenameFileWithTag() {
$taggerMock = $this->getMock('\OCP\ITags');
$taggerMock->expects($this->any())
->method('getTagsForObjects')
->with(array(123))
->will($this->returnValue(array(123 => array('tag1', 'tag2'))));
$tagManagerMock = $this->getMock('\OCP\ITagManager');
$tagManagerMock->expects($this->any())
->method('load')
->with('files')
->will($this->returnValue($taggerMock));
$oldTagManager = \OC::$server->query('TagManager');
\OC::$server->registerService('TagManager', function ($c) use ($tagManagerMock) {
return $tagManagerMock;
});

$dir = '/';
$oldname = 'oldname.txt';
$newname = 'newname.txt';

$this->viewMock->expects($this->any())
->method('file_exists')
->with($this->anything())
->will($this->returnValueMap(array(
array('/', true),
array('/oldname.txt', true)
)));


$this->viewMock->expects($this->any())
->method('getFileInfo')
->will($this->returnValue(new \OC\Files\FileInfo(
'/new_name.txt',
new \OC\Files\Storage\Local(array('datadir' => '/')),
'/',
array(
'fileid' => 123,
'type' => 'file',
'mimetype' => 'text/plain',
'mtime' => 0,
'permissions' => 31,
'size' => 18,
'etag' => 'abcdef',
'directory' => '/',
'name' => 'new_name.txt',
), null)));

$result = $this->files->rename($dir, $oldname, $newname);

$this->assertTrue($result['success']);
$this->assertEquals(123, $result['data']['id']);
$this->assertEquals('new_name.txt', $result['data']['name']);
$this->assertEquals(18, $result['data']['size']);
$this->assertEquals('text/plain', $result['data']['mimetype']);
$this->assertEquals('abcdef', $result['data']['etag']);
$this->assertEquals(array('tag1', 'tag2'), $result['data']['tags']);
$this->assertEquals('/', $result['data']['path']);

\OC::$server->registerService('TagManager', function ($c) use ($oldTagManager) {
return $oldTagManager;
});
}

/**
* Test rename inside a folder that doesn't exist any more
*/
function testRenameInNonExistingFolder() {
$dir = '/unexist';
$oldname = 'oldname';
$newname = 'newname';

$this->viewMock->expects($this->at(0))
->method('file_exists')
->with('/unexist/oldname')
->will($this->returnValue(false));

$this->viewMock->expects($this->any())
->method('getFileInfo')
->will($this->returnValue(array(
'fileid' => 123,
'type' => 'dir',
'mimetype' => 'httpd/unix-directory',
'size' => 18,
'etag' => 'abcdef',
'directory' => '/unexist',
'name' => 'new_name',
)));

$result = $this->files->rename($dir, $oldname, $newname);

$this->assertFalse($result['success']);
$this->assertEquals('sourcenotfound', $result['data']['code']);
}

/**
* Test move to invalid name
*/
function testRenameToInvalidName() {
$dir = '/';
$oldname = 'oldname';
$newname = 'abc\\';

$result = $this->files->rename($dir, $oldname, $newname);

$this->assertFalse($result['success']);
$this->assertEquals('File name contains at least one invalid character', $result['data']['message']);
$this->assertEquals('invalidname', $result['data']['code']);
}
}

+ 1
- 2
apps/files/tests/js/favoritesfilelistspec.js View File

@@ -100,8 +100,7 @@ describe('OCA.Files.FavoritesFileList tests', function() {
expect($tr.attr('data-mtime')).toEqual('11111000');
expect($tr.find('a.name').attr('href')).toEqual(
OC.webroot +
'/index.php/apps/files/ajax/download.php' +
'?dir=%2Fsomedir&files=test.txt'
'/remote.php/webdav/somedir/test.txt'
);
expect($tr.find('.nametext').text().trim()).toEqual('test.txt');
});

+ 2
- 0
apps/files/tests/js/fileUploadSpec.js View File

@@ -19,6 +19,8 @@
*
*/

/* global FileList */

describe('OC.Upload tests', function() {
var $dummyUploader;
var testFile;

+ 1
- 1
apps/files/tests/js/fileactionsSpec.js View File

@@ -584,7 +584,7 @@ describe('OCA.Files.FileActions tests', function() {
expect(busyStub.calledWith('testName.txt', true)).toEqual(true);
expect(handleDownloadStub.calledOnce).toEqual(true);
expect(handleDownloadStub.getCall(0).args[0]).toEqual(
OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=testName.txt'
OC.webroot + '/remote.php/webdav/subdir/testName.txt'
);
busyStub.reset();
handleDownloadStub.yield();

+ 3
- 4
apps/files/tests/js/fileactionsmenuSpec.js View File

@@ -237,8 +237,8 @@ describe('OCA.Files.FileActionsMenu tests', function() {
expect(redirectStub.calledOnce).toEqual(true);
expect(redirectStub.getCall(0).args[0]).toContain(
OC.webroot +
'/index.php/apps/files/ajax/download.php' +
'?dir=%2Fsubdir&files=testName.txt');
'/remote.php/webdav/subdir/testName.txt'
);
redirectStub.restore();
});
it('takes the file\'s path into account when clicking download', function() {
@@ -269,8 +269,7 @@ describe('OCA.Files.FileActionsMenu tests', function() {

expect(redirectStub.calledOnce).toEqual(true);
expect(redirectStub.getCall(0).args[0]).toContain(
OC.webroot + '/index.php/apps/files/ajax/download.php' +
'?dir=%2Fanotherpath%2Fthere&files=testName.txt'
OC.webroot + '/remote.php/webdav/anotherpath/there/testName.txt'
);
redirectStub.restore();
});

+ 384
- 459
apps/files/tests/js/filelistSpec.js
File diff suppressed because it is too large
View File


+ 3
- 3
apps/files/tests/js/filesSpec.js View File

@@ -76,11 +76,11 @@ describe('OCA.Files.Files tests', function() {
describe('getDownloadUrl', function() {
it('returns the ajax download URL when filename and dir specified', function() {
var url = Files.getDownloadUrl('test file.txt', '/subdir');
expect(url).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=test%20file.txt');
expect(url).toEqual(OC.webroot + '/remote.php/webdav/subdir/test%20file.txt');
});
it('returns the ajax download URL when filename and root dir specific', function() {
it('returns the webdav download URL when filename and root dir specified', function() {
var url = Files.getDownloadUrl('test file.txt', '/');
expect(url).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2F&files=test%20file.txt');
expect(url).toEqual(OC.webroot + '/remote.php/webdav/test%20file.txt');
});
it('returns the ajax download URL when multiple files specified', function() {
var url = Files.getDownloadUrl(['test file.txt', 'abc.txt'], '/subdir');

+ 1
- 3
apps/files_sharing/js/sharedfilelist.js View File

@@ -231,6 +231,7 @@
files = _.chain(files)
// convert share data to file data
.map(function(share) {
// TODO: use OC.Files.FileInfo
var file = {
id: share.file_source,
icon: OC.MimeType.getIconUrl(share.mimetype),
@@ -242,9 +243,6 @@
}
else {
file.type = 'file';
if (share.isPreviewAvailable) {
file.isPreviewAvailable = true;
}
}
file.share = {
id: share.id,

+ 9
- 17
apps/files_sharing/tests/js/sharedfilelistSpec.js View File

@@ -166,8 +166,7 @@ describe('OCA.Sharing.FileList tests', function() {
expect($tr.attr('data-share-id')).toEqual('7');
expect($tr.find('a.name').attr('href')).toEqual(
OC.webroot +
'/index.php/apps/files/ajax/download.php' +
'?dir=%2Flocal%20path&files=local%20name.txt'
'/remote.php/webdav/local%20path/local%20name.txt'
);
expect($tr.find('.nametext').text().trim()).toEqual('local name.txt');

@@ -185,8 +184,7 @@ describe('OCA.Sharing.FileList tests', function() {
expect($tr.attr('data-share-id')).toEqual('8');
expect($tr.find('a.name').attr('href')).toEqual(
OC.webroot +
'/index.php/apps/files/ajax/download.php' +
'?dir=%2F&files=b.txt'
'/remote.php/webdav/b.txt'
);
expect($tr.find('.nametext').text().trim()).toEqual('b.txt');
});
@@ -338,8 +336,7 @@ describe('OCA.Sharing.FileList tests', function() {
expect($tr.attr('data-share-id')).toEqual('7');
expect($tr.find('a.name').attr('href')).toEqual(
OC.webroot +
'/index.php/apps/files/ajax/download.php' +
'?dir=%2Flocal%20path&files=local%20name.txt'
'/remote.php/webdav/local%20path/local%20name.txt'
);
expect($tr.find('.nametext').text().trim()).toEqual('local name.txt');
});
@@ -429,9 +426,8 @@ describe('OCA.Sharing.FileList tests', function() {
expect($tr.attr('data-share-owner')).not.toBeDefined();
expect($tr.attr('data-share-id')).toEqual('7');
expect($tr.find('a.name').attr('href')).toEqual(
OC.webroot +
'/index.php/apps/files/ajax/download.php' +
'?dir=%2Flocal%20path&files=local%20name.txt');
OC.webroot + '/remote.php/webdav/local%20path/local%20name.txt'
);

expect($tr.find('.nametext').text().trim()).toEqual('local name.txt');
});
@@ -498,9 +494,7 @@ describe('OCA.Sharing.FileList tests', function() {
expect($tr.attr('data-share-owner')).not.toBeDefined();
expect($tr.attr('data-share-id')).toEqual('7,8,9');
expect($tr.find('a.name').attr('href')).toEqual(
OC.webroot +
'/index.php/apps/files/ajax/download.php' +
'?dir=%2Flocal%20path&files=local%20name.txt'
OC.webroot + '/remote.php/webdav/local%20path/local%20name.txt'
);
expect($tr.find('.nametext').text().trim()).toEqual('local name.txt');
});
@@ -592,9 +586,8 @@ describe('OCA.Sharing.FileList tests', function() {
expect($tr.attr('data-share-owner')).not.toBeDefined();
expect($tr.attr('data-share-id')).toEqual('7');
expect($tr.find('a.name').attr('href')).toEqual(
OC.webroot +
'/index.php/apps/files/ajax/download.php' +
'?dir=%2Flocal%20path&files=local%20name.txt');
OC.webroot + '/remote.php/webdav/local%20path/local%20name.txt'
);

expect($tr.find('.nametext').text().trim()).toEqual('local name.txt');
});
@@ -634,8 +627,7 @@ describe('OCA.Sharing.FileList tests', function() {
expect($tr.attr('data-share-id')).toEqual('7');
expect($tr.find('a.name').attr('href')).toEqual(
OC.webroot +
'/index.php/apps/files/ajax/download.php' +
'?dir=%2Flocal%20path&files=local%20name.txt');
'/remote.php/webdav/local%20path/local%20name.txt');

expect($tr.find('.nametext').text().trim()).toEqual('local name.txt');
});

+ 71
- 1
apps/files_trashbin/js/filelist.js View File

@@ -283,7 +283,77 @@

isSelectedDeletable: function() {
return true;
}
},

/**
* Reloads the file list using ajax call
*
* @return ajax call object
*/
reload: function() {
this._selectedFiles = {};
this._selectionSummary.clear();
this.$el.find('.select-all').prop('checked', false);
this.showMask();
if (this._reloadCall) {
this._reloadCall.abort();
}
this._reloadCall = $.ajax({
url: this.getAjaxUrl('list'),
data: {
dir : this.getCurrentDirectory(),
sort: this._sort,
sortdirection: this._sortDirection
}
});
var callBack = this.reloadCallback.bind(this);
return this._reloadCall.then(callBack, callBack);
},
reloadCallback: function(result) {
delete this._reloadCall;
this.hideMask();

if (!result || result.status === 'error') {
// if the error is not related to folder we're trying to load, reload the page to handle logout etc
if (result.data.error === 'authentication_error' ||
result.data.error === 'token_expired' ||
result.data.error === 'application_not_enabled'
) {
OC.redirect(OC.generateUrl('apps/files'));
}
OC.Notification.show(result.data.message);
return false;
}

// Firewall Blocked request?
if (result.status === 403) {
// Go home
this.changeDirectory('/');
OC.Notification.show(t('files', 'This operation is forbidden'));
return false;
}

// Did share service die or something else fail?
if (result.status === 500) {
// Go home
this.changeDirectory('/');
OC.Notification.show(t('files', 'This directory is unavailable, please check the logs or contact the administrator'));
return false;
}

if (result.status === 404) {
// go back home
this.changeDirectory('/');
return false;
}
// aborted ?
if (result.status === 0){
return true;
}

this.setFiles(result.data.files);
return true;
},

});


+ 1
- 1
core/js/oc-dialogs.js View File

@@ -759,7 +759,7 @@ var OCdialogs = {
filename: entry.name,
date: OC.Util.relativeModifiedDate(entry.mtime)
});
if (entry.isPreviewAvailable) {
if (entry.type === 'file') {
var urlSpec = {
file: dir + '/' + entry.name
};

Loading…
Cancel
Save