Use trashbin dav endpoint to list trash in webuitags/v15.0.0beta1
@@ -1,93 +0,0 @@ | |||
<?php | |||
/** | |||
* @copyright Copyright (c) 2016, ownCloud, Inc. | |||
* | |||
* @author Bart Visscher <bartv@thisnet.nl> | |||
* @author Björn Schießle <bjoern@schiessle.org> | |||
* @author Lukas Reschke <lukas@statuscode.ch> | |||
* @author Robin Appelman <robin@icewind.nl> | |||
* @author Roeland Jago Douma <roeland@famdouma.nl> | |||
* @author Vincent Petry <pvince81@owncloud.com> | |||
* | |||
* @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/> | |||
* | |||
*/ | |||
use OCP\ILogger; | |||
\OC_JSON::checkLoggedIn(); | |||
\OC_JSON::callCheck(); | |||
\OC::$server->getSession()->close(); | |||
$folder = isset($_POST['dir']) ? $_POST['dir'] : '/'; | |||
// "empty trash" command | |||
if (isset($_POST['allfiles']) && (string)$_POST['allfiles'] === 'true'){ | |||
$deleteAll = true; | |||
if ($folder === '/' || $folder === '') { | |||
OCA\Files_Trashbin\Trashbin::deleteAll(); | |||
$list = array(); | |||
} else { | |||
$list[] = $folder; | |||
$folder = dirname($folder); | |||
} | |||
} | |||
else { | |||
$deleteAll = false; | |||
$files = (string)$_POST['files']; | |||
$list = json_decode($files); | |||
} | |||
$folder = rtrim($folder, '/') . '/'; | |||
$error = array(); | |||
$success = array(); | |||
$i = 0; | |||
foreach ($list as $file) { | |||
if ($folder === '/') { | |||
$file = ltrim($file, '/'); | |||
$delimiter = strrpos($file, '.d'); | |||
$filename = substr($file, 0, $delimiter); | |||
$timestamp = substr($file, $delimiter+2); | |||
} else { | |||
$filename = $folder . '/' . $file; | |||
$timestamp = null; | |||
} | |||
OCA\Files_Trashbin\Trashbin::delete($filename, \OCP\User::getUser(), $timestamp); | |||
if (OCA\Files_Trashbin\Trashbin::file_exists($filename, $timestamp)) { | |||
$error[] = $filename; | |||
\OCP\Util::writeLog('trashbin','can\'t delete ' . $filename . ' permanently.', ILogger::ERROR); | |||
} | |||
// only list deleted files if not deleting everything | |||
else if (!$deleteAll) { | |||
$success[$i]['filename'] = $file; | |||
$success[$i]['timestamp'] = $timestamp; | |||
$i++; | |||
} | |||
} | |||
if ( $error ) { | |||
$filelist = ''; | |||
foreach ( $error as $e ) { | |||
$filelist .= $e.', '; | |||
} | |||
$l = \OC::$server->getL10N('files_trashbin'); | |||
$message = $l->t("Couldn't delete %s permanently", array(rtrim($filelist, ', '))); | |||
\OC_JSON::error(array("data" => array("message" => $message, | |||
"success" => $success, "error" => $error))); | |||
} else { | |||
\OC_JSON::success(array("data" => array("success" => $success))); | |||
} |
@@ -1,33 +0,0 @@ | |||
<?php | |||
/** | |||
* @copyright Copyright (c) 2016, ownCloud, Inc. | |||
* | |||
* @author Björn Schießle <bjoern@schiessle.org> | |||
* @author Lukas Reschke <lukas@statuscode.ch> | |||
* @author Morris Jobke <hey@morrisjobke.de> | |||
* | |||
* @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/> | |||
* | |||
*/ | |||
\OC_JSON::checkLoggedIn(); | |||
\OC_JSON::callCheck(); | |||
\OC::$server->getSession()->close(); | |||
$trashStatus = OCA\Files_Trashbin\Trashbin::isEmpty(OCP\User::getUser()); | |||
\OC_JSON::success(array("data" => array("isEmpty" => $trashStatus))); | |||
@@ -1,48 +0,0 @@ | |||
<?php | |||
/** | |||
* @copyright Copyright (c) 2016, ownCloud, Inc. | |||
* | |||
* @author Björn Schießle <bjoern@schiessle.org> | |||
* @author Lukas Reschke <lukas@statuscode.ch> | |||
* @author Vincent Petry <pvince81@owncloud.com> | |||
* | |||
* @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/> | |||
* | |||
*/ | |||
\OC_JSON::checkLoggedIn(); | |||
\OC::$server->getSession()->close(); | |||
// Load the files | |||
$dir = isset($_GET['dir']) ? (string)$_GET['dir'] : ''; | |||
$sortAttribute = isset($_GET['sort']) ? (string)$_GET['sort'] : 'name'; | |||
$sortDirection = isset($_GET['sortdirection']) ? ($_GET['sortdirection'] === 'desc') : false; | |||
$data = array(); | |||
// make filelist | |||
try { | |||
$files = \OCA\Files_Trashbin\Helper::getTrashFiles($dir, \OCP\User::getUser(), $sortAttribute, $sortDirection); | |||
} catch (Exception $e) { | |||
http_response_code(404); | |||
exit(); | |||
} | |||
$encodedDir = \OCP\Util::encodePath($dir); | |||
$data['permissions'] = 0; | |||
$data['directory'] = $dir; | |||
$data['files'] = \OCA\Files_Trashbin\Helper::formatFileInfos($files); | |||
\OC_JSON::success(array('data' => $data)); | |||
@@ -1,98 +0,0 @@ | |||
<?php | |||
/** | |||
* @copyright Copyright (c) 2016, ownCloud, Inc. | |||
* | |||
* @author Bart Visscher <bartv@thisnet.nl> | |||
* @author Björn Schießle <bjoern@schiessle.org> | |||
* @author Lukas Reschke <lukas@statuscode.ch> | |||
* @author Morris Jobke <hey@morrisjobke.de> | |||
* @author Robin Appelman <robin@icewind.nl> | |||
* @author Roeland Jago Douma <roeland@famdouma.nl> | |||
* @author Thomas Müller <thomas.mueller@tmit.eu> | |||
* @author Vincent Petry <pvince81@owncloud.com> | |||
* | |||
* @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/> | |||
* | |||
*/ | |||
use OCP\ILogger; | |||
\OC_JSON::checkLoggedIn(); | |||
\OC_JSON::callCheck(); | |||
\OC::$server->getSession()->close(); | |||
$dir = '/'; | |||
if (isset($_POST['dir'])) { | |||
$dir = rtrim((string)$_POST['dir'], '/'). '/'; | |||
} | |||
$allFiles = false; | |||
if (isset($_POST['allfiles']) && (string)$_POST['allfiles'] === 'true') { | |||
$allFiles = true; | |||
$list = array(); | |||
$dirListing = true; | |||
if ($dir === '' || $dir === '/') { | |||
$dirListing = false; | |||
} | |||
foreach (OCA\Files_Trashbin\Helper::getTrashFiles($dir, \OCP\User::getUser()) as $file) { | |||
$fileName = $file['name']; | |||
if (!$dirListing) { | |||
$fileName .= '.d' . $file['mtime']; | |||
} | |||
$list[] = $fileName; | |||
} | |||
} else { | |||
$list = json_decode($_POST['files']); | |||
} | |||
$error = array(); | |||
$success = array(); | |||
$i = 0; | |||
foreach ($list as $file) { | |||
$path = $dir . '/' . $file; | |||
if ($dir === '/') { | |||
$file = ltrim($file, '/'); | |||
$delimiter = strrpos($file, '.d'); | |||
$filename = substr($file, 0, $delimiter); | |||
$timestamp = substr($file, $delimiter+2); | |||
} else { | |||
$path_parts = pathinfo($file); | |||
$filename = $path_parts['basename']; | |||
$timestamp = null; | |||
} | |||
if ( !OCA\Files_Trashbin\Trashbin::restore($path, $filename, $timestamp) ) { | |||
$error[] = $filename; | |||
\OCP\Util::writeLog('trashbin', 'can\'t restore ' . $filename, ILogger::ERROR); | |||
} else { | |||
$success[$i]['filename'] = $file; | |||
$success[$i]['timestamp'] = $timestamp; | |||
$i++; | |||
} | |||
} | |||
if ( $error ) { | |||
$filelist = ''; | |||
foreach ( $error as $e ) { | |||
$filelist .= $e.', '; | |||
} | |||
$l = OC::$server->getL10N('files_trashbin'); | |||
$message = $l->t("Couldn't restore %s", array(rtrim($filelist, ', '))); | |||
\OC_JSON::error(array("data" => array("message" => $message, | |||
"success" => $success, "error" => $error))); | |||
} else { | |||
\OC_JSON::success(array("data" => array("success" => $success))); | |||
} |
@@ -34,13 +34,3 @@ $application->registerRoutes($this, [ | |||
], | |||
], | |||
]); | |||
$this->create('files_trashbin_ajax_delete', 'ajax/delete.php') | |||
->actionInclude('files_trashbin/ajax/delete.php'); | |||
$this->create('files_trashbin_ajax_isEmpty', 'ajax/isEmpty.php') | |||
->actionInclude('files_trashbin/ajax/isEmpty.php'); | |||
$this->create('files_trashbin_ajax_list', 'ajax/list.php') | |||
->actionInclude('files_trashbin/ajax/list.php'); | |||
$this->create('files_trashbin_ajax_undelete', 'ajax/undelete.php') | |||
->actionInclude('files_trashbin/ajax/undelete.php'); | |||
@@ -18,6 +18,7 @@ return array( | |||
'OCA\\Files_Trashbin\\Expiration' => $baseDir . '/../lib/Expiration.php', | |||
'OCA\\Files_Trashbin\\Helper' => $baseDir . '/../lib/Helper.php', | |||
'OCA\\Files_Trashbin\\Hooks' => $baseDir . '/../lib/Hooks.php', | |||
'OCA\\Files_Trashbin\\Sabre\\AbstractTrash' => $baseDir . '/../lib/Sabre/AbstractTrash.php', | |||
'OCA\\Files_Trashbin\\Sabre\\ITrash' => $baseDir . '/../lib/Sabre/ITrash.php', | |||
'OCA\\Files_Trashbin\\Sabre\\PropfindPlugin' => $baseDir . '/../lib/Sabre/PropfindPlugin.php', | |||
'OCA\\Files_Trashbin\\Sabre\\RestoreFolder' => $baseDir . '/../lib/Sabre/RestoreFolder.php', |
@@ -33,6 +33,7 @@ class ComposerStaticInitFiles_Trashbin | |||
'OCA\\Files_Trashbin\\Expiration' => __DIR__ . '/..' . '/../lib/Expiration.php', | |||
'OCA\\Files_Trashbin\\Helper' => __DIR__ . '/..' . '/../lib/Helper.php', | |||
'OCA\\Files_Trashbin\\Hooks' => __DIR__ . '/..' . '/../lib/Hooks.php', | |||
'OCA\\Files_Trashbin\\Sabre\\AbstractTrash' => __DIR__ . '/..' . '/../lib/Sabre/AbstractTrash.php', | |||
'OCA\\Files_Trashbin\\Sabre\\ITrash' => __DIR__ . '/..' . '/../lib/Sabre/ITrash.php', | |||
'OCA\\Files_Trashbin\\Sabre\\PropfindPlugin' => __DIR__ . '/..' . '/../lib/Sabre/PropfindPlugin.php', | |||
'OCA\\Files_Trashbin\\Sabre\\RestoreFolder' => __DIR__ . '/..' . '/../lib/Sabre/RestoreFolder.php', |
@@ -17,12 +17,21 @@ OCA.Trashbin = {}; | |||
*/ | |||
OCA.Trashbin.App = { | |||
_initialized: false, | |||
/** @type {OC.Files.Client} */ | |||
client: null, | |||
initialize: function($el) { | |||
initialize: function ($el) { | |||
if (this._initialized) { | |||
return; | |||
} | |||
this._initialized = true; | |||
this.client = new OC.Files.Client({ | |||
host: OC.getHost(), | |||
port: OC.getPort(), | |||
root: OC.linkToRemoteBase('dav') + '/trashbin/' + OC.getCurrentUser().uid, | |||
useHTTPS: OC.getProtocol() === 'https' | |||
}); | |||
var urlParams = OC.Util.History.parseUrlQuery(); | |||
this.fileList = new OCA.Trashbin.FileList( | |||
$('#app-content-trashbin'), { | |||
@@ -31,22 +40,24 @@ OCA.Trashbin.App = { | |||
scrollTo: urlParams.scrollto, | |||
config: OCA.Files.App.getFilesConfig(), | |||
multiSelectMenu: [ | |||
{ | |||
name: 'restore', | |||
displayName: t('files', 'Restore'), | |||
iconClass: 'icon-history', | |||
}, | |||
{ | |||
name: 'delete', | |||
displayName: t('files', 'Delete'), | |||
iconClass: 'icon-delete', | |||
} | |||
] | |||
{ | |||
name: 'restore', | |||
displayName: t('files', 'Restore'), | |||
iconClass: 'icon-history', | |||
}, | |||
{ | |||
name: 'delete', | |||
displayName: t('files', 'Delete'), | |||
iconClass: 'icon-delete', | |||
} | |||
], | |||
client: this.client | |||
} | |||
); | |||
}, | |||
_createFileActions: function() { | |||
_createFileActions: function () { | |||
var client = this.client; | |||
var fileActions = new OCA.Files.FileActions(); | |||
fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) { | |||
var dir = context.fileList.getCurrentDirectory(); | |||
@@ -62,17 +73,19 @@ OCA.Trashbin.App = { | |||
mime: 'all', | |||
permissions: OC.PERMISSION_READ, | |||
iconClass: 'icon-history', | |||
actionHandler: function(filename, context) { | |||
actionHandler: function (filename, context) { | |||
var fileList = context.fileList; | |||
var tr = fileList.findFileEl(filename); | |||
var deleteAction = tr.children("td.date").children(".action.delete"); | |||
deleteAction.removeClass('icon-delete').addClass('icon-loading-small'); | |||
$.post(OC.filePath('files_trashbin', 'ajax', 'undelete.php'), { | |||
files: JSON.stringify([filename]), | |||
dir: fileList.getCurrentDirectory() | |||
}, | |||
_.bind(fileList._removeCallback, fileList) | |||
); | |||
fileList.showFileBusyState(tr, true); | |||
var dir = context.fileList.getCurrentDirectory(); | |||
client.move(OC.joinPaths('trash', dir, filename), OC.joinPaths('restore', filename), true) | |||
.then( | |||
fileList._removeCallback.bind(fileList, [filename]), | |||
function () { | |||
fileList.showFileBusyState(tr, false); | |||
OC.Notification.show(t('files_trashbin', 'Error while restoring file from trashbin')); | |||
} | |||
); | |||
} | |||
}); | |||
@@ -82,33 +95,35 @@ OCA.Trashbin.App = { | |||
mime: 'all', | |||
permissions: OC.PERMISSION_READ, | |||
iconClass: 'icon-delete', | |||
render: function(actionSpec, isDefault, context) { | |||
render: function (actionSpec, isDefault, context) { | |||
var $actionLink = fileActions._makeActionLink(actionSpec, context); | |||
$actionLink.attr('original-title', t('files_trashbin', 'Delete permanently')); | |||
$actionLink.children('img').attr('alt', t('files_trashbin', 'Delete permanently')); | |||
context.$file.find('td:last').append($actionLink); | |||
return $actionLink; | |||
}, | |||
actionHandler: function(filename, context) { | |||
actionHandler: function (filename, context) { | |||
var fileList = context.fileList; | |||
$('.tipsy').remove(); | |||
var tr = fileList.findFileEl(filename); | |||
var deleteAction = tr.children("td.date").children(".action.delete"); | |||
deleteAction.removeClass('icon-delete').addClass('icon-loading-small'); | |||
$.post(OC.filePath('files_trashbin', 'ajax', 'delete.php'), { | |||
files: JSON.stringify([filename]), | |||
dir: fileList.getCurrentDirectory() | |||
}, | |||
_.bind(fileList._removeCallback, fileList) | |||
); | |||
fileList.showFileBusyState(tr, true); | |||
var dir = context.fileList.getCurrentDirectory(); | |||
client.remove(OC.joinPaths('trash', dir, filename)) | |||
.then( | |||
fileList._removeCallback.bind(fileList, [filename]), | |||
function () { | |||
fileList.showFileBusyState(tr, false); | |||
OC.Notification.show(t('files_trashbin', 'Error while removing file from trashbin')); | |||
} | |||
); | |||
} | |||
}); | |||
return fileActions; | |||
} | |||
}; | |||
$(document).ready(function() { | |||
$('#app-content-trashbin').one('show', function() { | |||
$(document).ready(function () { | |||
$('#app-content-trashbin').one('show', function () { | |||
var App = OCA.Trashbin.App; | |||
App.initialize($('#app-content-trashbin')); | |||
// force breadcrumb init |
@@ -9,6 +9,8 @@ | |||
*/ | |||
(function() { | |||
var DELETED_REGEXP = new RegExp(/^(.+)\.d[0-9]+$/); | |||
var FILENAME_PROP = '{http://nextcloud.org/ns}trashbin-filename'; | |||
var DELETION_TIME_PROP = '{http://nextcloud.org/ns}trashbin-deletion-time'; | |||
/** | |||
* Convert a file name in the format filename.d12345 to the real file name. | |||
@@ -36,17 +38,30 @@ | |||
* @param [options] map of options | |||
*/ | |||
var FileList = function($el, options) { | |||
this.client = options.client; | |||
this.initialize($el, options); | |||
}; | |||
FileList.prototype = _.extend({}, OCA.Files.FileList.prototype, | |||
/** @lends OCA.Trashbin.FileList.prototype */ { | |||
id: 'trashbin', | |||
appName: t('files_trashbin', 'Deleted files'), | |||
/** @type {OC.Files.Client} */ | |||
client: null, | |||
/** | |||
* @private | |||
*/ | |||
initialize: function() { | |||
this.client.addFileInfoParser(function(response, data) { | |||
var props = response.propStat[0].properties; | |||
return { | |||
displayName: props[FILENAME_PROP], | |||
mtime: parseInt(props[DELETION_TIME_PROP], 10) * 1000, | |||
hasPreview: true, | |||
path: data.path.substr(6), // remove leading /trash | |||
} | |||
}); | |||
var result = OCA.Files.FileList.prototype.initialize.apply(this, arguments); | |||
this.$el.find('.undelete').click('click', _.bind(this._onClickRestoreSelected, this)); | |||
@@ -91,23 +106,6 @@ | |||
return tr; | |||
}, | |||
_renderRow: function(fileData, options) { | |||
options = options || {}; | |||
// make a copy to avoid changing original object | |||
fileData = _.extend({}, fileData); | |||
var dir = this.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 OCA.Files.FileList.prototype._renderRow.call(this, fileData, options); | |||
}, | |||
getAjaxUrl: function(action, params) { | |||
var q = ''; | |||
if (params) { | |||
@@ -140,15 +138,10 @@ | |||
this.$el.find('#filestable th').toggleClass('hidden', !exists); | |||
}, | |||
_removeCallback: function(result) { | |||
if (result.status !== 'success') { | |||
OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error')); | |||
} | |||
var files = result.data.success; | |||
_removeCallback: function(files) { | |||
var $el; | |||
for (var i = 0; i < files.length; i++) { | |||
$el = this.remove(OC.basename(files[i].filename), {updateSummary: false}); | |||
$el = this.remove(OC.basename(files[i]), {updateSummary: false}); | |||
this.fileSummary.remove({type: $el.attr('data-type'), size: $el.attr('data-size')}); | |||
} | |||
this.fileSummary.update(); | |||
@@ -158,97 +151,71 @@ | |||
_onClickRestoreSelected: function(event) { | |||
event.preventDefault(); | |||
var self = this; | |||
var allFiles = this.$el.find('.select-all').is(':checked'); | |||
var files = []; | |||
var params = {}; | |||
this.fileMultiSelectMenu.toggleLoading('restore', true); | |||
if (allFiles) { | |||
this.showMask(); | |||
params = { | |||
allfiles: true, | |||
dir: this.getCurrentDirectory() | |||
}; | |||
} | |||
else { | |||
files = _.pluck(this.getSelectedFiles(), 'name'); | |||
for (var i = 0; i < files.length; i++) { | |||
var deleteAction = this.findFileEl(files[i]).children("td.date").children(".action.delete"); | |||
deleteAction.removeClass('icon-delete').addClass('icon-loading-small'); | |||
} | |||
params = { | |||
files: JSON.stringify(files), | |||
dir: this.getCurrentDirectory() | |||
}; | |||
var files = _.pluck(this.getSelectedFiles(), 'name'); | |||
for (var i = 0; i < files.length; i++) { | |||
var tr = this.findFileEl(files[i]); | |||
this.showFileBusyState(tr, true); | |||
} | |||
$.post(OC.filePath('files_trashbin', 'ajax', 'undelete.php'), | |||
params, | |||
function(result) { | |||
if (allFiles) { | |||
if (result.status !== 'success') { | |||
OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error')); | |||
this.fileMultiSelectMenu.toggleLoading('restore', true); | |||
var restorePromises = files.map(function(file) { | |||
return self.client.move(OC.joinPaths('trash', self.getCurrentDirectory(), file), OC.joinPaths('restore', file), true) | |||
.then( | |||
function() { | |||
self._removeCallback([file]); | |||
} | |||
self.hideMask(); | |||
// simply remove all files | |||
self.setFiles([]); | |||
} | |||
else { | |||
self._removeCallback(result); | |||
} | |||
); | |||
}); | |||
return Promise.all(restorePromises).then( | |||
function() { | |||
self.fileMultiSelectMenu.toggleLoading('restore', false); | |||
}, | |||
function() { | |||
OC.Notification.show(t('files_trashbin', 'Error while restoring files from trashbin')); | |||
} | |||
); | |||
event.preventDefault(); | |||
}, | |||
_onClickDeleteSelected: function(event) { | |||
event.preventDefault(); | |||
var self = this; | |||
var allFiles = this.$el.find('.select-all').is(':checked'); | |||
var files = []; | |||
var params = {}; | |||
if (allFiles) { | |||
params = { | |||
allfiles: true, | |||
dir: this.getCurrentDirectory() | |||
}; | |||
} | |||
else { | |||
files = _.pluck(this.getSelectedFiles(), 'name'); | |||
params = { | |||
files: JSON.stringify(files), | |||
dir: this.getCurrentDirectory() | |||
}; | |||
var files = _.pluck(this.getSelectedFiles(), 'name'); | |||
for (var i = 0; i < files.length; i++) { | |||
var tr = this.findFileEl(files[i]); | |||
this.showFileBusyState(tr, true); | |||
} | |||
this.fileMultiSelectMenu.toggleLoading('delete', true); | |||
if (allFiles) { | |||
this.showMask(); | |||
} | |||
else { | |||
for (var i = 0; i < files.length; i++) { | |||
var deleteAction = this.findFileEl(files[i]).children("td.date").children(".action.delete"); | |||
deleteAction.removeClass('icon-delete').addClass('icon-loading-small'); | |||
} | |||
} | |||
$.post(OC.filePath('files_trashbin', 'ajax', 'delete.php'), | |||
params, | |||
function(result) { | |||
if (allFiles) { | |||
if (result.status !== 'success') { | |||
OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error')); | |||
} | |||
return this.client.remove(OC.joinPaths('trash', this.getCurrentDirectory())) | |||
.then( | |||
function() { | |||
self.hideMask(); | |||
// simply remove all files | |||
self.setFiles([]); | |||
}, | |||
function() { | |||
OC.Notification.show(t('files_trashbin', 'Error while emptying trashbin')); | |||
} | |||
else { | |||
self._removeCallback(result); | |||
} | |||
); | |||
} else { | |||
this.fileMultiSelectMenu.toggleLoading('delete', true); | |||
var deletePromises = files.map(function(file) { | |||
return self.client.remove(OC.joinPaths('trash', self.getCurrentDirectory(), file)) | |||
.then( | |||
function() { | |||
self._removeCallback([file]); | |||
} | |||
); | |||
}); | |||
return Promise.all(deletePromises).then( | |||
function() { | |||
self.fileMultiSelectMenu.toggleLoading('delete', false); | |||
}, | |||
function() { | |||
OC.Notification.show(t('files_trashbin', 'Error while removing files from trashbin')); | |||
} | |||
); | |||
); | |||
} | |||
}, | |||
_onClickFile: function(event) { | |||
@@ -277,6 +244,13 @@ | |||
return true; | |||
}, | |||
/** | |||
* Returns list of webdav properties to request | |||
*/ | |||
_getWebdavProperties: function() { | |||
return [FILENAME_PROP, DELETION_TIME_PROP].concat(this.filesClient.getPropfindProperties()); | |||
}, | |||
/** | |||
* Reloads the file list using ajax call | |||
* | |||
@@ -290,39 +264,25 @@ | |||
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.client.getFolderContents( | |||
'trash/' + this.getCurrentDirectory(), { | |||
includeParent: false, | |||
properties: this._getWebdavProperties() | |||
} | |||
}); | |||
); | |||
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.show(result.data.message); | |||
return false; | |||
} | |||
if (result.status === 401) { | |||
if (status === 401) { | |||
return false; | |||
} | |||
// Firewall Blocked request? | |||
if (result.status === 403) { | |||
if (status === 403) { | |||
// Go home | |||
this.changeDirectory('/'); | |||
OC.Notification.show(t('files', 'This operation is forbidden')); | |||
@@ -330,24 +290,24 @@ | |||
} | |||
// Did share service die or something else fail? | |||
if (result.status === 500) { | |||
if (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) { | |||
if (status === 404) { | |||
// go back home | |||
this.changeDirectory('/'); | |||
return false; | |||
} | |||
// aborted ? | |||
if (result.status === 0){ | |||
if (status === 0){ | |||
return true; | |||
} | |||
this.setFiles(result.data.files); | |||
this.setFiles(result); | |||
return true; | |||
}, | |||
@@ -0,0 +1,69 @@ | |||
<?php | |||
/** | |||
* @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License as | |||
* published by the Free Software Foundation, either version 3 of the | |||
* License, or (at your option) any later version. | |||
* | |||
* 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 | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
namespace OCA\Files_Trashbin\Sabre; | |||
use OCP\Files\FileInfo; | |||
abstract class AbstractTrash implements ITrash { | |||
/** @var FileInfo */ | |||
protected $data; | |||
public function __construct(FileInfo $data) { | |||
$this->data = $data; | |||
} | |||
public function getFilename(): string { | |||
return $this->data->getName(); | |||
} | |||
public function getDeletionTime(): int { | |||
return $this->data->getMtime(); | |||
} | |||
public function getFileId(): int { | |||
return $this->data->getId(); | |||
} | |||
public function getFileInfo(): FileInfo { | |||
return $this->data; | |||
} | |||
public function getSize(): int { | |||
return $this->data->getSize(); | |||
} | |||
public function getLastModified(): int { | |||
return $this->data->getMtime(); | |||
} | |||
public function getContentType(): string { | |||
return $this->data->getMimetype(); | |||
} | |||
public function getETag(): string { | |||
return $this->data->getEtag(); | |||
} | |||
public function getName(): string { | |||
return $this->data->getName(); | |||
} | |||
} |
@@ -23,6 +23,8 @@ declare(strict_types=1); | |||
*/ | |||
namespace OCA\Files_Trashbin\Sabre; | |||
use OCP\Files\FileInfo; | |||
interface ITrash { | |||
public function restore(): bool; | |||
@@ -35,4 +37,6 @@ interface ITrash { | |||
public function getSize(); | |||
public function getFileId(): int; | |||
public function getFileInfo(): FileInfo; | |||
} |
@@ -25,6 +25,8 @@ declare(strict_types=1); | |||
namespace OCA\Files_Trashbin\Sabre; | |||
use OCA\DAV\Connector\Sabre\FilesPlugin; | |||
use OCP\Constants; | |||
use OCP\IPreview; | |||
use Sabre\DAV\INode; | |||
use Sabre\DAV\PropFind; | |||
use Sabre\DAV\Server; | |||
@@ -39,7 +41,13 @@ class PropfindPlugin extends ServerPlugin { | |||
/** @var Server */ | |||
private $server; | |||
public function __construct() { | |||
/** @var IPreview */ | |||
private $previewManager; | |||
public function __construct( | |||
IPreview $previewManager | |||
) { | |||
$this->previewManager = $previewManager; | |||
} | |||
public function initialize(Server $server) { | |||
@@ -54,11 +62,11 @@ class PropfindPlugin extends ServerPlugin { | |||
return; | |||
} | |||
$propFind->handle(self::TRASHBIN_FILENAME, function() use ($node) { | |||
$propFind->handle(self::TRASHBIN_FILENAME, function () use ($node) { | |||
return $node->getFilename(); | |||
}); | |||
$propFind->handle(self::TRASHBIN_ORIGINAL_LOCATION, function() use ($node) { | |||
$propFind->handle(self::TRASHBIN_ORIGINAL_LOCATION, function () use ($node) { | |||
return $node->getOriginalLocation(); | |||
}); | |||
@@ -73,6 +81,28 @@ class PropfindPlugin extends ServerPlugin { | |||
$propFind->handle(FilesPlugin::FILEID_PROPERTYNAME, function () use ($node) { | |||
return $node->getFileId(); | |||
}); | |||
$propFind->handle(FilesPlugin::PERMISSIONS_PROPERTYNAME, function () { | |||
return 'GD'; // read + delete | |||
}); | |||
$propFind->handle(FilesPlugin::GETETAG_PROPERTYNAME, function () use ($node) { | |||
// add fake etag, it is only needed to identify the preview image | |||
return $node->getLastModified(); | |||
}); | |||
$propFind->handle(FilesPlugin::INTERNAL_FILEID_PROPERTYNAME, function () use ($node) { | |||
// add fake etag, it is only needed to identify the preview image | |||
return $node->getFileId(); | |||
}); | |||
$propFind->handle(FilesPlugin::HAS_PREVIEW_PROPERTYNAME, function () use ($node) { | |||
return $this->previewManager->isAvailable($node->getFileInfo()); | |||
}); | |||
$propFind->handle(FilesPlugin::MOUNT_TYPE_PROPERTYNAME, function () { | |||
return ''; | |||
}); | |||
} | |||
} |
@@ -27,16 +27,13 @@ use OCP\Files\FileInfo; | |||
use Sabre\DAV\Exception\Forbidden; | |||
use Sabre\DAV\IFile; | |||
class TrashFile implements IFile, ITrash { | |||
class TrashFile extends AbstractTrash implements IFile, ITrash { | |||
/** @var string */ | |||
private $userId; | |||
/** @var FileInfo */ | |||
private $data; | |||
public function __construct(string $userId, FileInfo $data) { | |||
$this->userId = $userId; | |||
$this->data = $data; | |||
parent::__construct($data); | |||
} | |||
public function put($data) { | |||
@@ -47,18 +44,6 @@ class TrashFile implements IFile, ITrash { | |||
return $this->data->getStorage()->fopen($this->data->getInternalPath().'.d'.$this->getLastModified(), 'rb'); | |||
} | |||
public function getContentType(): string { | |||
return $this->data->getMimetype(); | |||
} | |||
public function getETag(): string { | |||
return $this->data->getEtag(); | |||
} | |||
public function getSize(): int { | |||
return $this->data->getSize(); | |||
} | |||
public function delete() { | |||
\OCA\Files_Trashbin\Trashbin::delete($this->data->getName(), $this->userId, $this->getLastModified()); | |||
} | |||
@@ -71,29 +56,11 @@ class TrashFile implements IFile, ITrash { | |||
throw new Forbidden(); | |||
} | |||
public function getLastModified(): int { | |||
return $this->data->getMtime(); | |||
} | |||
public function restore(): bool { | |||
return \OCA\Files_Trashbin\Trashbin::restore($this->getName(), $this->data->getName(), $this->getLastModified()); | |||
} | |||
public function getFilename(): string { | |||
return $this->data->getName(); | |||
} | |||
public function getOriginalLocation(): string { | |||
return $this->data['extraData']; | |||
} | |||
public function getDeletionTime(): int { | |||
return $this->getLastModified(); | |||
} | |||
public function getFileId(): int { | |||
return $this->data->getId(); | |||
} | |||
} |
@@ -28,16 +28,13 @@ use Sabre\DAV\Exception\Forbidden; | |||
use Sabre\DAV\Exception\NotFound; | |||
use Sabre\DAV\ICollection; | |||
class TrashFolder implements ICollection, ITrash { | |||
class TrashFolder extends AbstractTrash implements ICollection, ITrash { | |||
/** @var string */ | |||
private $userId; | |||
/** @var FileInfo */ | |||
private $data; | |||
public function __construct(string $root, string $userId, FileInfo $data) { | |||
$this->userId = $userId; | |||
$this->data = $data; | |||
parent::__construct($data); | |||
} | |||
public function createFile($name, $data = null) { | |||
@@ -100,31 +97,11 @@ class TrashFolder implements ICollection, ITrash { | |||
throw new Forbidden(); | |||
} | |||
public function getLastModified(): int { | |||
return $this->data->getMtime(); | |||
} | |||
public function restore(): bool { | |||
return \OCA\Files_Trashbin\Trashbin::restore($this->getName(), $this->data->getName(), $this->getLastModified()); | |||
} | |||
public function getFilename(): string { | |||
return $this->data->getName(); | |||
} | |||
public function getOriginalLocation(): string { | |||
return $this->data['extraData']; | |||
} | |||
public function getDeletionTime(): int { | |||
return $this->getLastModified(); | |||
} | |||
public function getSize(): int { | |||
return $this->data->getSize(); | |||
} | |||
public function getFileId(): int { | |||
return $this->data->getId(); | |||
} | |||
} |
@@ -27,16 +27,13 @@ use OCP\Files\FileInfo; | |||
use Sabre\DAV\Exception\Forbidden; | |||
use Sabre\DAV\IFile; | |||
class TrashFolderFile implements IFile, ITrash { | |||
class TrashFolderFile extends AbstractTrash implements IFile, ITrash { | |||
/** @var string */ | |||
private $root; | |||
/** @var string */ | |||
private $userId; | |||
/** @var FileInfo */ | |||
private $data; | |||
/** @var string */ | |||
private $location; | |||
@@ -46,8 +43,8 @@ class TrashFolderFile implements IFile, ITrash { | |||
string $location) { | |||
$this->root = $root; | |||
$this->userId = $userId; | |||
$this->data = $data; | |||
$this->location = $location; | |||
parent::__construct($data); | |||
} | |||
public function put($data) { | |||
@@ -58,51 +55,19 @@ class TrashFolderFile implements IFile, ITrash { | |||
return $this->data->getStorage()->fopen($this->data->getInternalPath(), 'rb'); | |||
} | |||
public function getContentType(): string { | |||
return $this->data->getMimetype(); | |||
} | |||
public function getETag(): string { | |||
return $this->data->getEtag(); | |||
} | |||
public function getSize(): int { | |||
return $this->data->getSize(); | |||
} | |||
public function delete() { | |||
\OCA\Files_Trashbin\Trashbin::delete($this->root . '/' . $this->getName(), $this->userId, null); | |||
} | |||
public function getName(): string { | |||
return $this->data->getName(); | |||
} | |||
public function setName($name) { | |||
throw new Forbidden(); | |||
} | |||
public function getLastModified(): int { | |||
return $this->data->getMtime(); | |||
} | |||
public function restore(): bool { | |||
return \OCA\Files_Trashbin\Trashbin::restore($this->root . '/' . $this->getName(), $this->data->getName(), null); | |||
} | |||
public function getFilename(): string { | |||
return $this->data->getName(); | |||
} | |||
public function getOriginalLocation(): string { | |||
return $this->location . '/' . $this->getFilename(); | |||
} | |||
public function getDeletionTime(): int { | |||
return $this->getLastModified(); | |||
} | |||
public function getFileId(): int { | |||
return $this->data->getId(); | |||
} | |||
} |
@@ -28,7 +28,7 @@ use Sabre\DAV\Exception\Forbidden; | |||
use Sabre\DAV\Exception\NotFound; | |||
use Sabre\DAV\ICollection; | |||
class TrashFolderFolder implements ICollection, ITrash { | |||
class TrashFolderFolder extends AbstractTrash implements ICollection, ITrash { | |||
/** @var string */ | |||
private $root; | |||
@@ -36,9 +36,6 @@ class TrashFolderFolder implements ICollection, ITrash { | |||
/** @var string */ | |||
private $userId; | |||
/** @var FileInfo */ | |||
private $data; | |||
/** @var string */ | |||
private $location; | |||
@@ -48,8 +45,8 @@ class TrashFolderFolder implements ICollection, ITrash { | |||
string $location) { | |||
$this->root = $root; | |||
$this->userId = $userId; | |||
$this->data = $data; | |||
$this->location = $location; | |||
parent::__construct($data); | |||
} | |||
public function createFile($name, $data = null) { | |||
@@ -104,40 +101,15 @@ class TrashFolderFolder implements ICollection, ITrash { | |||
\OCA\Files_Trashbin\Trashbin::delete($this->root . '/' . $this->getName(), $this->userId, null); | |||
} | |||
public function getName(): string { | |||
return $this->data->getName(); | |||
} | |||
public function setName($name) { | |||
throw new Forbidden(); | |||
} | |||
public function getLastModified(): int { | |||
return $this->data->getMtime(); | |||
} | |||
public function restore(): bool { | |||
return \OCA\Files_Trashbin\Trashbin::restore($this->root . '/' . $this->getName(), $this->data->getName(), null); | |||
} | |||
public function getFilename(): string { | |||
return $this->data->getName(); | |||
} | |||
public function getOriginalLocation(): string { | |||
return $this->location . '/' . $this->getFilename(); | |||
} | |||
public function getDeletionTime(): int { | |||
return $this->getLastModified(); | |||
} | |||
public function getSize(): int { | |||
return $this->data->getSize(); | |||
} | |||
public function getFileId(): int { | |||
return $this->data->getId(); | |||
} | |||
} |
@@ -1,31 +1,38 @@ | |||
/** | |||
* 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/>. | |||
* | |||
*/ | |||
* 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/>. | |||
* | |||
*/ | |||
describe('OCA.Trashbin.FileList tests', function() { | |||
var testFiles, alertStub, notificationStub, fileList; | |||
describe('OCA.Trashbin.FileList tests', function () { | |||
var testFiles, alertStub, notificationStub, fileList, client; | |||
beforeEach(function() { | |||
beforeEach(function () { | |||
alertStub = sinon.stub(OC.dialogs, 'alert'); | |||
notificationStub = sinon.stub(OC.Notification, 'show'); | |||
client = new OC.Files.Client({ | |||
host: 'localhost', | |||
port: 80, | |||
root: '/remote.php/dav/trashbin/user', | |||
useHTTPS: OC.getProtocol() === 'https' | |||
}); | |||
// init parameters and test table elements | |||
$('#testArea').append( | |||
'<div id="app-content-trashbin">' + | |||
@@ -59,21 +66,24 @@ describe('OCA.Trashbin.FileList tests', function() { | |||
testFiles = [{ | |||
id: 1, | |||
type: 'file', | |||
name: 'One.txt', | |||
name: 'One.txt.d11111', | |||
displayName: 'One.txt', | |||
mtime: 11111000, | |||
mimetype: 'text/plain', | |||
etag: 'abc' | |||
}, { | |||
id: 2, | |||
type: 'file', | |||
name: 'Two.jpg', | |||
name: 'Two.jpg.d22222', | |||
displayName: 'Two.jpg', | |||
mtime: 22222000, | |||
mimetype: 'image/jpeg', | |||
etag: 'def', | |||
}, { | |||
id: 3, | |||
type: 'file', | |||
name: 'Three.pdf', | |||
name: 'Three.pdf.d33333', | |||
displayName: 'Three.pdf', | |||
mtime: 33333000, | |||
mimetype: 'application/pdf', | |||
etag: '123', | |||
@@ -81,7 +91,8 @@ describe('OCA.Trashbin.FileList tests', function() { | |||
id: 4, | |||
type: 'dir', | |||
mtime: 99999000, | |||
name: 'somedir', | |||
name: 'somedir.d99999', | |||
displayName: 'somedir', | |||
mimetype: 'httpd/unix-directory', | |||
etag: '456' | |||
}]; | |||
@@ -92,20 +103,21 @@ describe('OCA.Trashbin.FileList tests', function() { | |||
$('#app-content-trashbin'), { | |||
fileActions: fileActions, | |||
multiSelectMenu: [{ | |||
name: 'restore', | |||
displayName: t('files', 'Restore'), | |||
iconClass: 'icon-history', | |||
}, | |||
name: 'restore', | |||
displayName: t('files', 'Restore'), | |||
iconClass: 'icon-history', | |||
}, | |||
{ | |||
name: 'delete', | |||
displayName: t('files', 'Delete'), | |||
iconClass: 'icon-delete', | |||
} | |||
] | |||
], | |||
client: client | |||
} | |||
); | |||
}); | |||
afterEach(function() { | |||
afterEach(function () { | |||
testFiles = undefined; | |||
fileList.destroy(); | |||
fileList = undefined; | |||
@@ -114,17 +126,17 @@ describe('OCA.Trashbin.FileList tests', function() { | |||
notificationStub.restore(); | |||
alertStub.restore(); | |||
}); | |||
describe('Initialization', function() { | |||
it('Sorts by mtime by default', function() { | |||
describe('Initialization', function () { | |||
it('Sorts by mtime by default', function () { | |||
expect(fileList._sort).toEqual('mtime'); | |||
expect(fileList._sortDirection).toEqual('desc'); | |||
}); | |||
it('Always returns read and delete permission', function() { | |||
it('Always returns read and delete permission', function () { | |||
expect(fileList.getDirectoryPermissions()).toEqual(OC.PERMISSION_READ | OC.PERMISSION_DELETE); | |||
}); | |||
}); | |||
describe('Breadcrumbs', function() { | |||
beforeEach(function() { | |||
describe('Breadcrumbs', function () { | |||
beforeEach(function () { | |||
var data = { | |||
status: 'success', | |||
data: { | |||
@@ -133,13 +145,13 @@ describe('OCA.Trashbin.FileList tests', function() { | |||
} | |||
}; | |||
fakeServer.respondWith(/\/index\.php\/apps\/files_trashbin\/ajax\/list.php\?dir=%2Fsubdir/, [ | |||
200, { | |||
"Content-Type": "application/json" | |||
}, | |||
JSON.stringify(data) | |||
200, { | |||
"Content-Type": "application/json" | |||
}, | |||
JSON.stringify(data) | |||
]); | |||
}); | |||
it('links the breadcrumb to the trashbin view', function() { | |||
it('links the breadcrumb to the trashbin view', function () { | |||
fileList.changeDirectory('/subdir', false, true); | |||
fakeServer.respond(); | |||
var $crumbs = fileList.$el.find('#controls .crumb'); | |||
@@ -152,8 +164,8 @@ describe('OCA.Trashbin.FileList tests', function() { | |||
.toEqual(OC.webroot + '/index.php/apps/files?view=trashbin&dir=/subdir'); | |||
}); | |||
}); | |||
describe('Rendering rows', function() { | |||
it('renders rows with the correct data when in root', function() { | |||
describe('Rendering rows', function () { | |||
it('renders rows with the correct data when in root', function () { | |||
// dir listing is false when in root | |||
$('#dir').val('/'); | |||
fileList.setFiles(testFiles); | |||
@@ -174,7 +186,7 @@ describe('OCA.Trashbin.FileList tests', function() { | |||
expect(fileList.findFileEl('One.txt.d11111')[0]).toEqual($tr[0]); | |||
}); | |||
it('renders rows with the correct data when in root after calling setFiles with the same data set', function() { | |||
it('renders rows with the correct data when in root after calling setFiles with the same data set', function () { | |||
// dir listing is false when in root | |||
$('#dir').val('/'); | |||
fileList.setFiles(testFiles); | |||
@@ -196,11 +208,14 @@ describe('OCA.Trashbin.FileList tests', function() { | |||
expect(fileList.findFileEl('One.txt.d11111')[0]).toEqual($tr[0]); | |||
}); | |||
it('renders rows with the correct data when in subdirectory', function() { | |||
it('renders rows with the correct data when in subdirectory', function () { | |||
// dir listing is true when in a subdir | |||
$('#dir').val('/subdir'); | |||
fileList.setFiles(testFiles); | |||
fileList.setFiles(testFiles.map(function (file) { | |||
file.name = file.displayName; | |||
return file; | |||
})); | |||
var $rows = fileList.$el.find('tbody tr'); | |||
var $tr = $rows.eq(0); | |||
expect($rows.length).toEqual(4); | |||
@@ -218,42 +233,42 @@ describe('OCA.Trashbin.FileList tests', function() { | |||
expect(fileList.findFileEl('One.txt')[0]).toEqual($tr[0]); | |||
}); | |||
it('does not render a size column', function() { | |||
it('does not render a size column', function () { | |||
expect(fileList.$el.find('tbody tr .filesize').length).toEqual(0); | |||
}); | |||
}); | |||
describe('File actions', function() { | |||
describe('Deleting single files', function() { | |||
describe('File actions', function () { | |||
describe('Deleting single files', function () { | |||
// TODO: checks ajax call | |||
// TODO: checks spinner | |||
// TODO: remove item after delete | |||
// TODO: bring back item if delete failed | |||
}); | |||
describe('Restoring single files', function() { | |||
describe('Restoring single files', function () { | |||
// TODO: checks ajax call | |||
// TODO: checks spinner | |||
// TODO: remove item after restore | |||
// TODO: bring back item if restore failed | |||
}); | |||
}); | |||
describe('file previews', function() { | |||
describe('file previews', function () { | |||
// TODO: check that preview URL is going through files_trashbin | |||
}); | |||
describe('loading file list', function() { | |||
describe('loading file list', function () { | |||
// TODO: check that ajax URL is going through files_trashbin | |||
}); | |||
describe('breadcrumbs', function() { | |||
describe('breadcrumbs', function () { | |||
// TODO: test label + URL | |||
}); | |||
describe('elementToFile', function() { | |||
describe('elementToFile', function () { | |||
var $tr; | |||
beforeEach(function() { | |||
beforeEach(function () { | |||
fileList.setFiles(testFiles); | |||
$tr = fileList.findFileEl('One.txt.d11111'); | |||
}); | |||
it('converts data attributes to file info structure', function() { | |||
it('converts data attributes to file info structure', function () { | |||
var fileInfo = fileList.elementToFile($tr); | |||
expect(fileInfo.id).toEqual(1); | |||
expect(fileInfo.name).toEqual('One.txt.d11111'); | |||
@@ -265,8 +280,8 @@ describe('OCA.Trashbin.FileList tests', function() { | |||
expect(fileInfo.type).toEqual('file'); | |||
}); | |||
}); | |||
describe('Global Actions', function() { | |||
beforeEach(function() { | |||
describe('Global Actions', function () { | |||
beforeEach(function () { | |||
fileList.setFiles(testFiles); | |||
fileList.findFileEl('One.txt.d11111').find('input:checkbox').click(); | |||
fileList.findFileEl('Three.pdf.d33333').find('input:checkbox').click(); | |||
@@ -274,12 +289,12 @@ describe('OCA.Trashbin.FileList tests', function() { | |||
fileList.$el.find('.actions-selected').click(); | |||
}); | |||
afterEach(function() { | |||
afterEach(function () { | |||
fileList.$el.find('.actions-selected').click(); | |||
}); | |||
describe('Delete', function() { | |||
it('Shows trashbin actions', function() { | |||
describe('Delete', function () { | |||
it('Shows trashbin actions', function () { | |||
// visible because a few files were selected | |||
expect($('.selectedActions').is(':visible')).toEqual(true); | |||
expect($('.selectedActions .item-delete').is(':visible')).toEqual(true); | |||
@@ -301,99 +316,82 @@ describe('OCA.Trashbin.FileList tests', function() { | |||
expect($('.selectedActions .item-delete').is(':visible')).toEqual(false); | |||
expect($('.selectedActions .item-restore').is(':visible')).toEqual(false); | |||
}); | |||
it('Deletes selected files when "Delete" clicked', function() { | |||
it('Deletes selected files when "Delete" clicked', function () { | |||
var request; | |||
var $deleteLink = $('.selectedActions .filesSelectMenu .delete'); | |||
$deleteLink.click(); | |||
expect($deleteLink.find('.icon-loading-small').length).toEqual(1); | |||
expect(fakeServer.requests.length).toEqual(1); | |||
request = fakeServer.requests[0]; | |||
expect(request.url).toEqual(OC.webroot + '/index.php/apps/files_trashbin/ajax/delete.php'); | |||
expect(OC.parseQueryString(request.requestBody)) | |||
.toEqual({'dir': '/', files: '["One.txt.d11111","Three.pdf.d33333","somedir.d99999"]'}); | |||
fakeServer.requests[0].respond( | |||
200, | |||
{ 'Content-Type': 'application/json' }, | |||
JSON.stringify({ | |||
status: 'success', | |||
data: { | |||
success: [ | |||
{filename: 'One.txt.d11111'}, | |||
{filename: 'Three.pdf.d33333'}, | |||
{filename: 'somedir.d99999'} | |||
] | |||
} | |||
}) | |||
); | |||
expect($deleteLink.find('.icon-loading-small').length).toEqual(0); | |||
expect(fileList.findFileEl('One.txt.d11111').length).toEqual(0); | |||
expect(fileList.findFileEl('Three.pdf.d33333').length).toEqual(0); | |||
expect(fileList.findFileEl('somedir.d99999').length).toEqual(0); | |||
expect(fileList.findFileEl('Two.jpg.d22222').length).toEqual(1); | |||
var promise = fileList._onClickDeleteSelected({ | |||
preventDefault: function () { | |||
} | |||
}); | |||
var files = ["One.txt.d11111", "Three.pdf.d33333", "somedir.d99999"]; | |||
expect(fakeServer.requests.length).toEqual(files.length); | |||
for (var i = 0; i < files.length; i++) { | |||
request = fakeServer.requests[i]; | |||
expect(request.url).toEqual(OC.webroot + '/remote.php/dav/trashbin/user/trash/' + files[i]); | |||
request.respond(200); | |||
} | |||
return promise.then(function () { | |||
expect(fileList.findFileEl('One.txt.d11111').length).toEqual(0); | |||
expect(fileList.findFileEl('Three.pdf.d33333').length).toEqual(0); | |||
expect(fileList.findFileEl('somedir.d99999').length).toEqual(0); | |||
expect(fileList.findFileEl('Two.jpg.d22222').length).toEqual(1); | |||
}); | |||
}); | |||
it('Deletes all files when all selected when "Delete" clicked', function() { | |||
it('Deletes all files when all selected when "Delete" clicked', function () { | |||
var request; | |||
$('.select-all').click(); | |||
$('.selectedActions .filesSelectMenu .delete').click(); | |||
var promise = fileList._onClickDeleteSelected({ | |||
preventDefault: function () { | |||
} | |||
}); | |||
expect(fakeServer.requests.length).toEqual(1); | |||
request = fakeServer.requests[0]; | |||
expect(request.url).toEqual(OC.webroot + '/index.php/apps/files_trashbin/ajax/delete.php'); | |||
expect(OC.parseQueryString(request.requestBody)) | |||
.toEqual({'dir': '/', allfiles: 'true'}); | |||
fakeServer.requests[0].respond( | |||
200, | |||
{ 'Content-Type': 'application/json' }, | |||
JSON.stringify({status: 'success'}) | |||
); | |||
expect(fileList.isEmpty).toEqual(true); | |||
expect(request.url).toEqual(OC.webroot + '/remote.php/dav/trashbin/user/trash'); | |||
request.respond(200); | |||
return promise.then(function () { | |||
expect(fileList.isEmpty).toEqual(true); | |||
}); | |||
}); | |||
}); | |||
describe('Restore', function() { | |||
it('Restores selected files when "Restore" clicked', function() { | |||
describe('Restore', function () { | |||
it('Restores selected files when "Restore" clicked', function () { | |||
var request; | |||
var $restoreLink = $('.selectedActions .filesSelectMenu .restore'); | |||
$restoreLink.click(); | |||
expect($restoreLink.find('.icon-loading-small').length).toEqual(1); | |||
expect(fakeServer.requests.length).toEqual(1); | |||
request = fakeServer.requests[0]; | |||
expect(request.url).toEqual(OC.webroot + '/index.php/apps/files_trashbin/ajax/undelete.php'); | |||
expect(OC.parseQueryString(request.requestBody)) | |||
.toEqual({'dir': '/', files: '["One.txt.d11111","Three.pdf.d33333","somedir.d99999"]'}); | |||
fakeServer.requests[0].respond( | |||
200, | |||
{ 'Content-Type': 'application/json' }, | |||
JSON.stringify({ | |||
status: 'success', | |||
data: { | |||
success: [ | |||
{filename: 'One.txt.d11111'}, | |||
{filename: 'Three.pdf.d33333'}, | |||
{filename: 'somedir.d99999'} | |||
] | |||
} | |||
}) | |||
); | |||
expect($restoreLink.find('.icon-loading-small').length).toEqual(0); | |||
expect(fileList.findFileEl('One.txt.d11111').length).toEqual(0); | |||
expect(fileList.findFileEl('Three.pdf.d33333').length).toEqual(0); | |||
expect(fileList.findFileEl('somedir.d99999').length).toEqual(0); | |||
expect(fileList.findFileEl('Two.jpg.d22222').length).toEqual(1); | |||
var promise = fileList._onClickRestoreSelected({ | |||
preventDefault: function () { | |||
} | |||
}); | |||
var files = ["One.txt.d11111", "Three.pdf.d33333", "somedir.d99999"]; | |||
expect(fakeServer.requests.length).toEqual(files.length); | |||
for (var i = 0; i < files.length; i++) { | |||
request = fakeServer.requests[i]; | |||
expect(request.url).toEqual(OC.webroot + '/remote.php/dav/trashbin/user/trash/' + files[i]); | |||
expect(request.requestHeaders.Destination).toEqual(OC.webroot + '/remote.php/dav/trashbin/user/restore/' + files[i]); | |||
request.respond(200); | |||
} | |||
return promise.then(function() { | |||
expect(fileList.findFileEl('One.txt.d11111').length).toEqual(0); | |||
expect(fileList.findFileEl('Three.pdf.d33333').length).toEqual(0); | |||
expect(fileList.findFileEl('somedir.d99999').length).toEqual(0); | |||
expect(fileList.findFileEl('Two.jpg.d22222').length).toEqual(1); | |||
}); | |||
}); | |||
it('Restores all files when all selected when "Restore" clicked', function() { | |||
it('Restores all files when all selected when "Restore" clicked', function () { | |||
var request; | |||
$('.select-all').click(); | |||
$('.selectedActions .filesSelectMenu .restore').click(); | |||
expect(fakeServer.requests.length).toEqual(1); | |||
request = fakeServer.requests[0]; | |||
expect(request.url).toEqual(OC.webroot + '/index.php/apps/files_trashbin/ajax/undelete.php'); | |||
expect(OC.parseQueryString(request.requestBody)) | |||
.toEqual({'dir': '/', allfiles: 'true'}); | |||
fakeServer.requests[0].respond( | |||
200, | |||
{ 'Content-Type': 'application/json' }, | |||
JSON.stringify({status: 'success'}) | |||
); | |||
expect(fileList.isEmpty).toEqual(true); | |||
var promise = fileList._onClickRestoreSelected({ | |||
preventDefault: function () { | |||
} | |||
}); | |||
var files = ["One.txt.d11111", "Two.jpg.d22222", "Three.pdf.d33333", "somedir.d99999"]; | |||
expect(fakeServer.requests.length).toEqual(files.length); | |||
for (var i = 0; i < files.length; i++) { | |||
request = fakeServer.requests[i]; | |||
expect(request.url).toEqual(OC.webroot + '/remote.php/dav/trashbin/user/trash/' + files[i]); | |||
expect(request.requestHeaders.Destination).toEqual(OC.webroot + '/remote.php/dav/trashbin/user/restore/' + files[i]); | |||
request.respond(200); | |||
} | |||
return promise.then(function() { | |||
expect(fileList.isEmpty).toEqual(true); | |||
}); | |||
}); | |||
}); | |||
}); |
@@ -1,10 +1,10 @@ | |||
default: | |||
autoload: | |||
'': %paths.base%/../features/bootstrap | |||
'': "%paths.base%/../features/bootstrap" | |||
suites: | |||
default: | |||
paths: | |||
- %paths.base%/../features | |||
- "%paths.base%/../features" | |||
contexts: | |||
- FeatureContext: | |||
baseUrl: http://localhost:8080/ocs/ | |||
@@ -27,7 +27,7 @@ default: | |||
ocPath: ../../ | |||
federation: | |||
paths: | |||
- %paths.base%/../federation_features | |||
- "%paths.base%/../federation_features" | |||
contexts: | |||
- FederationContext: | |||
baseUrl: http://localhost:8080/ocs/ | |||
@@ -37,7 +37,7 @@ default: | |||
regular_user_password: 123456 | |||
capabilities: | |||
paths: | |||
- %paths.base%/../capabilities_features | |||
- "%paths.base%/../capabilities_features" | |||
contexts: | |||
- CapabilitiesContext: | |||
baseUrl: http://localhost:8080/ocs/ | |||
@@ -47,7 +47,7 @@ default: | |||
regular_user_password: 123456 | |||
sharees: | |||
paths: | |||
- %paths.base%/../sharees_features | |||
- "%paths.base%/../sharees_features" | |||
contexts: | |||
- ShareesContext: | |||
baseUrl: http://localhost:8080/ocs/ | |||
@@ -57,7 +57,7 @@ default: | |||
regular_user_password: 123456 | |||
setup: | |||
paths: | |||
- %paths.base%/../setup_features | |||
- "%paths.base%/../setup_features" | |||
contexts: | |||
- SetupContext: | |||
baseUrl: http://localhost:8080/ocs/ | |||
@@ -67,7 +67,7 @@ default: | |||
regular_user_password: 123456 | |||
filesdrop: | |||
paths: | |||
- %paths.base%/../filesdrop_features | |||
- "%paths.base%/../filesdrop_features" | |||
contexts: | |||
- FilesDropContext: | |||
baseUrl: http://localhost:8080 | |||
@@ -77,7 +77,7 @@ default: | |||
regular_user_password: 123456 | |||
ldap: | |||
paths: | |||
- %paths.base%/../ldap_features | |||
- "%paths.base%/../ldap_features" | |||
contexts: | |||
- LDAPContext: | |||
baseUrl: http://localhost:8080 | |||
@@ -87,7 +87,7 @@ default: | |||
regular_user_password: what_for | |||
remoteapi: | |||
paths: | |||
- %paths.base%/../remoteapi_features | |||
- "%paths.base%/../remoteapi_features" | |||
contexts: | |||
- FeatureContext: | |||
baseUrl: http://localhost:8080/ocs/ | |||
@@ -100,4 +100,4 @@ default: | |||
extensions: | |||
jarnaiz\JUnitFormatter\JUnitFormatterExtension: | |||
filename: report.xml | |||
outputDir: %paths.base%/../output/ | |||
outputDir: "%paths.base%/../output/" |
@@ -20,8 +20,6 @@ | |||
* | |||
*/ | |||
use GuzzleHttp\Client; | |||
use GuzzleHttp\Message\ResponseInterface; | |||
use PHPUnit\Framework\Assert; | |||
require __DIR__ . '/../../vendor/autoload.php'; | |||
@@ -30,16 +28,46 @@ require __DIR__ . '/../../vendor/autoload.php'; | |||
* Trashbin functions | |||
*/ | |||
trait Trashbin { | |||
use WebDav; | |||
/** | |||
* @When User :user empties trashbin | |||
* @param string $user user | |||
*/ | |||
public function emptyTrashbin($user) { | |||
$this->asAn($user); | |||
$body = new \Behat\Gherkin\Node\TableNode([['allfiles', 'true'], ['dir', '%2F']]); | |||
$this->sendingToWithDirectUrl('POST', "/index.php/apps/files_trashbin/ajax/delete.php", $body); | |||
$this->theHTTPStatusCodeShouldBe('200'); | |||
$client = $this->getSabreClient($user); | |||
$response = $client->request('DELETE', $this->makeSabrePath($user, 'trash', 'trashbin')); | |||
Assert::assertEquals(204, $response['statusCode']); | |||
} | |||
private function findFullTrashname($user, $name) { | |||
$rootListing = $this->listTrashbinFolder($user, '/'); | |||
foreach ($rootListing as $href => $rootItem) { | |||
if ($rootItem['{http://nextcloud.org/ns}trashbin-filename'] === $name) { | |||
return basename($href); | |||
} | |||
} | |||
return null; | |||
} | |||
/** | |||
* Get the full /startofpath.dxxxx/rest/of/path from /startofpath/rest/of/path | |||
*/ | |||
private function getFullTrashPath($user, $path) { | |||
if ($path !== '' && $path !== '/') { | |||
$parts = explode('/', $path); | |||
$fullName = $this->findFullTrashname($user, $parts[1]); | |||
if ($fullName === null) { | |||
Assert::fail("cant find $path in trash"); | |||
return '/dummy_full_path_not_found'; | |||
} | |||
$parts[1] = $fullName; | |||
$path = implode('/', $parts); | |||
} | |||
return $path; | |||
} | |||
/** | |||
@@ -49,74 +77,92 @@ trait Trashbin { | |||
* @param string $path path | |||
* @return array response | |||
*/ | |||
public function listTrashbinFolder($user, $path){ | |||
$this->asAn($user); | |||
$params = '?dir=' . rawurlencode('/' . trim($path, '/')); | |||
$this->sendingToWithDirectUrl('GET', '/index.php/apps/files_trashbin/ajax/list.php' . $params, null); | |||
$this->theHTTPStatusCodeShouldBe('200'); | |||
$response = json_decode($this->response->getBody(), true); | |||
return $response['data']['files']; | |||
public function listTrashbinFolder($user, $path) { | |||
$path = $this->getFullTrashPath($user, $path); | |||
$client = $this->getSabreClient($user); | |||
$results = $client->propfind($this->makeSabrePath($user, 'trash' . $path, 'trashbin'), [ | |||
'{http://nextcloud.org/ns}trashbin-filename', | |||
'{http://nextcloud.org/ns}trashbin-original-location', | |||
'{http://nextcloud.org/ns}trashbin-deletion-time' | |||
], 1); | |||
$results = array_filter($results, function (array $item) { | |||
return isset($item['{http://nextcloud.org/ns}trashbin-filename']); | |||
}); | |||
if ($path !== '' && $path !== '/') { | |||
array_shift($results); | |||
} | |||
return $results; | |||
} | |||
/** | |||
* @Then /^as "([^"]*)" the (file|folder|entry) "([^"]*)" exists in trash$/ | |||
* @Then /^user "([^"]*)" in trash folder "([^"]*)" should have the following elements$/ | |||
* @param string $user | |||
* @param string $entryText | |||
* @param string $path | |||
* @param string $folder | |||
* @param \Behat\Gherkin\Node\TableNode|null $expectedElements | |||
*/ | |||
public function asTheFileOrFolderExistsInTrash($user, $entryText, $path) { | |||
$path = trim($path, '/'); | |||
$sections = explode('/', $path, 2); | |||
$firstEntry = $this->findFirstTrashedEntry($user, trim($sections[0], '/')); | |||
Assert::assertNotNull($firstEntry); | |||
// query was on the main element ? | |||
if (count($sections) === 1) { | |||
// already found, return | |||
return; | |||
} | |||
$subdir = trim(dirname($sections[1]), '/'); | |||
if ($subdir !== '' && $subdir !== '.') { | |||
$subdir = $firstEntry . '/' . $subdir; | |||
} else { | |||
$subdir = $firstEntry; | |||
public function checkTrashContents($user, $folder, $expectedElements) { | |||
$elementList = $this->listTrashbinFolder($user, $folder); | |||
$trashContent = array_filter(array_map(function (array $item) { | |||
return $item['{http://nextcloud.org/ns}trashbin-filename']; | |||
}, $elementList)); | |||
if ($expectedElements instanceof \Behat\Gherkin\Node\TableNode) { | |||
$elementRows = $expectedElements->getRows(); | |||
$elementsSimplified = $this->simplifyArray($elementRows); | |||
foreach ($elementsSimplified as $expectedElement) { | |||
$expectedElement = ltrim($expectedElement, '/'); | |||
if (array_search($expectedElement, $trashContent) === false) { | |||
Assert::fail("$expectedElement" . " is not in trash listing"); | |||
} | |||
} | |||
} | |||
} | |||
$listing = $this->listTrashbinFolder($user, $subdir); | |||
$checkedName = basename($path); | |||
$found = false; | |||
foreach ($listing as $entry) { | |||
if ($entry['name'] === $checkedName) { | |||
$found = true; | |||
break; | |||
} | |||
/** | |||
* @Then /^as "([^"]*)" the (file|folder) "([^"]*)" exists in trash$/ | |||
* @param string $user | |||
* @param string $type | |||
* @param string $file | |||
*/ | |||
public function checkTrashContains($user, $type, $file) { | |||
$parent = dirname($file); | |||
if ($parent === '.') { | |||
$parent = '/'; | |||
} | |||
$name = basename($file); | |||
$elementList = $this->listTrashbinFolder($user, $parent); | |||
$trashContent = array_filter(array_map(function (array $item) { | |||
return $item['{http://nextcloud.org/ns}trashbin-filename']; | |||
}, $elementList)); | |||
Assert::assertTrue($found); | |||
Assert::assertArraySubset([$name], array_values($trashContent)); | |||
} | |||
/** | |||
* Finds the first trashed entry matching the given name | |||
* | |||
* @param string $name | |||
* @return string|null real entry name with timestamp suffix or null if not found | |||
* @Then /^user "([^"]*)" in trash folder "([^"]*)" should have (\d+) elements?$/ | |||
* @param string $user | |||
* @param string $folder | |||
* @param \Behat\Gherkin\Node\TableNode|null $expectedElements | |||
*/ | |||
private function findFirstTrashedEntry($user, $name) { | |||
$listing = $this->listTrashbinFolder($user, '/'); | |||
foreach ($listing as $entry) { | |||
if ($entry['name'] === $name) { | |||
return $entry['name'] . '.d' . ((int)$entry['mtime'] / 1000); | |||
} | |||
} | |||
public function checkTrashSize($user, $folder, $expectedCount) { | |||
$elementList = $this->listTrashbinFolder($user, $folder); | |||
Assert::assertEquals($expectedCount, count($elementList)); | |||
} | |||
return null; | |||
/** | |||
* @When /^user "([^"]*)" in restores "([^"]*)" from trash$/ | |||
* @param string $user | |||
* @param string $file | |||
*/ | |||
public function restoreFromTrash($user, $file) { | |||
$file = $this->getFullTrashPath($user, $file); | |||
$url = $this->makeSabrePath($user, 'trash' . $file, 'trashbin'); | |||
$client = $this->getSabreClient($user); | |||
$response = $client->request('MOVE', $url, null, [ | |||
'Destination' => $this->makeSabrePath($user, 'restore/' . basename($file), 'trashbin'), | |||
]); | |||
Assert::assertEquals(201, $response['statusCode']); | |||
return; | |||
} | |||
} | |||
@@ -423,8 +423,12 @@ trait WebDav { | |||
return $parsedResponse; | |||
} | |||
public function makeSabrePath($user, $path) { | |||
return $this->encodePath($this->getDavFilesPath($user) . $path); | |||
public function makeSabrePath($user, $path, $type = 'files') { | |||
if ($type === 'files') { | |||
return $this->encodePath($this->getDavFilesPath($user) . $path); | |||
} else { | |||
return $this->encodePath($this->davPath . '/' . $type . '/' . $user . '/' . $path); | |||
} | |||
} | |||
public function getSabreClient($user) { |
@@ -1,7 +1,7 @@ | |||
Feature: sharing | |||
Background: | |||
Given using api version "1" | |||
Given using old dav path | |||
Given using new dav path | |||
# See sharing-v1-part2.feature | |||
@@ -295,7 +295,7 @@ Feature: sharing | |||
And user "user0" exists | |||
And User "user0" deletes file "/textfile0.txt" | |||
When User "user0" empties trashbin | |||
Then the HTTP status code should be "200" | |||
Then the HTTP status code should be "204" | |||
Scenario: orphaned shares | |||
Given As an "admin" | |||
@@ -392,4 +392,4 @@ Feature: sharing | |||
And folder "/shared" of user "user0" is shared with user "user1" | |||
When User "user1" moved file "/textfile0.txt" to "/shared/shared_file.txt" | |||
Then as "user1" the file "/shared/shared_file.txt" exists | |||
And as "user0" the file "/shared/shared_file.txt" exists | |||
And as "user0" the file "/shared/shared_file.txt" exists |
@@ -1,7 +1,7 @@ | |||
Feature: trashbin | |||
Background: | |||
Given using api version "1" | |||
And using old dav path | |||
And using new dav path | |||
And As an "admin" | |||
And app "files_trashbin" is enabled | |||
@@ -9,5 +9,73 @@ Feature: trashbin | |||
Given As an "admin" | |||
And user "user0" exists | |||
When User "user0" deletes file "/textfile0.txt" | |||
Then as "user0" the file "/textfile0.txt" exists in trash | |||
Then user "user0" in trash folder "/" should have 1 element | |||
And user "user0" in trash folder "/" should have the following elements | |||
| textfile0.txt | | |||
Scenario: clearing the trashbin | |||
Given As an "admin" | |||
And user "user0" exists | |||
When User "user0" deletes file "/textfile0.txt" | |||
And User "user0" empties trashbin | |||
Then user "user0" in trash folder "/" should have 0 elements | |||
Scenario: restoring file from trashbin | |||
Given As an "admin" | |||
And user "user0" exists | |||
When User "user0" deletes file "/textfile0.txt" | |||
And user "user0" in restores "/textfile0.txt" from trash | |||
Then user "user0" in trash folder "/" should have 0 elements | |||
And as "user0" the file "/textfile0.txt" exists | |||
Scenario: deleting and restoring a folder | |||
Given As an "admin" | |||
And user "user0" exists | |||
When User "user0" created a folder "/testfolder" | |||
And User "user0" moves file "/textfile0.txt" to "/testfolder/textfile0.txt" | |||
And as "user0" the file "/testfolder/textfile0.txt" exists | |||
And User "user0" deletes file "/testfolder" | |||
And user "user0" in trash folder "/" should have 1 element | |||
And user "user0" in trash folder "/" should have the following elements | |||
| testfolder | | |||
And user "user0" in trash folder "/testfolder" should have 1 element | |||
And user "user0" in trash folder "/testfolder" should have the following elements | |||
| textfile0.txt | | |||
And user "user0" in restores "/testfolder" from trash | |||
Then user "user0" in trash folder "/" should have 0 elements | |||
And as "user0" the file "/testfolder/textfile0.txt" exists | |||
Scenario: deleting a file from a subfolder and restoring it moves it back to the subfolder | |||
Given As an "admin" | |||
And user "user0" exists | |||
When User "user0" created a folder "/testfolder" | |||
And User "user0" moves file "/textfile0.txt" to "/testfolder/textfile0.txt" | |||
And as "user0" the file "/testfolder/textfile0.txt" exists | |||
And User "user0" deletes file "/testfolder/textfile0.txt" | |||
And user "user0" in trash folder "/" should have 1 element | |||
And user "user0" in trash folder "/" should have the following elements | |||
| textfile0.txt | | |||
And user "user0" in restores "/textfile0.txt" from trash | |||
Then user "user0" in trash folder "/" should have 0 elements | |||
And as "user0" the file "/textfile0.txt" does not exist | |||
And as "user0" the file "/testfolder/textfile0.txt" exists | |||
Scenario: deleting and a folder and restoring a file inside it | |||
Given As an "admin" | |||
And user "user0" exists | |||
When User "user0" created a folder "/testfolder" | |||
And User "user0" moves file "/textfile0.txt" to "/testfolder/textfile0.txt" | |||
And as "user0" the file "/testfolder/textfile0.txt" exists | |||
And User "user0" deletes file "/testfolder" | |||
And user "user0" in trash folder "/" should have 1 element | |||
And user "user0" in trash folder "/" should have the following elements | |||
| testfolder | | |||
And user "user0" in trash folder "/testfolder" should have 1 element | |||
And user "user0" in trash folder "/testfolder" should have the following elements | |||
| textfile0.txt | | |||
And user "user0" in restores "/testfolder/textfile0.txt" from trash | |||
Then user "user0" in trash folder "/" should have 1 elements | |||
And user "user0" in trash folder "/testfolder" should have 0 element | |||
And as "user0" the file "/textfile0.txt" exists | |||
@@ -61,6 +61,7 @@ | |||
} | |||
this._client = new dav.Client(clientOptions); | |||
this._client.xhrProvider = _.bind(this._xhrProvider, this); | |||
this._fileInfoParsers = []; | |||
}; | |||
Client.NS_OWNCLOUD = 'http://owncloud.org/ns'; | |||
@@ -390,7 +391,7 @@ | |||
// extend the parsed data using the custom parsers | |||
_.each(this._fileInfoParsers, function(parserFunction) { | |||
_.extend(data, parserFunction(response) || {}); | |||
_.extend(data, parserFunction(response, data) || {}); | |||
}); | |||
return new FileInfo(data); |