Merge pull request #11037 from nextcloud/trash-webui-dav
Use trashbin dav endpoint to list trash in webui
This commit is contained in:
commit
d867f9f091
|
@ -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) {
|
||||
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'), {
|
||||
|
@ -41,12 +50,14 @@ OCA.Trashbin.App = {
|
|||
displayName: t('files', 'Delete'),
|
||||
iconClass: 'icon-delete',
|
||||
}
|
||||
]
|
||||
],
|
||||
client: this.client
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
_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();
|
||||
|
@ -65,13 +76,15 @@ OCA.Trashbin.App = {
|
|||
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'));
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -93,13 +106,15 @@ OCA.Trashbin.App = {
|
|||
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'));
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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');
|
||||
var 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 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'));
|
||||
}
|
||||
self.hideMask();
|
||||
// simply remove all files
|
||||
self.setFiles([]);
|
||||
}
|
||||
else {
|
||||
self._removeCallback(result);
|
||||
}
|
||||
self.fileMultiSelectMenu.toggleLoading('restore', false);
|
||||
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]);
|
||||
}
|
||||
);
|
||||
});
|
||||
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()
|
||||
};
|
||||
}
|
||||
|
||||
this.fileMultiSelectMenu.toggleLoading('delete', true);
|
||||
if (allFiles) {
|
||||
this.showMask();
|
||||
}
|
||||
else {
|
||||
var 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');
|
||||
}
|
||||
var tr = this.findFileEl(files[i]);
|
||||
this.showFileBusyState(tr, true);
|
||||
}
|
||||
|
||||
$.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([]);
|
||||
}
|
||||
else {
|
||||
self._removeCallback(result);
|
||||
}
|
||||
self.fileMultiSelectMenu.toggleLoading('delete', false);
|
||||
},
|
||||
function() {
|
||||
OC.Notification.show(t('files_trashbin', 'Error while emptying trashbin'));
|
||||
}
|
||||
);
|
||||
} 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) {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,12 +20,19 @@
|
|||
*/
|
||||
|
||||
describe('OCA.Trashbin.FileList tests', function () {
|
||||
var testFiles, alertStub, notificationStub, fileList;
|
||||
var testFiles, alertStub, notificationStub, fileList, client;
|
||||
|
||||
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'
|
||||
}];
|
||||
|
@ -101,7 +112,8 @@ describe('OCA.Trashbin.FileList tests', function() {
|
|||
displayName: t('files', 'Delete'),
|
||||
iconClass: 'icon-delete',
|
||||
}
|
||||
]
|
||||
],
|
||||
client: client
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -200,7 +212,10 @@ describe('OCA.Trashbin.FileList tests', 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);
|
||||
|
@ -303,98 +318,81 @@ describe('OCA.Trashbin.FileList tests', 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'}
|
||||
]
|
||||
var promise = fileList._onClickDeleteSelected({
|
||||
preventDefault: function () {
|
||||
}
|
||||
})
|
||||
);
|
||||
expect($deleteLink.find('.icon-loading-small').length).toEqual(0);
|
||||
});
|
||||
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 () {
|
||||
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(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 () {
|
||||
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'}
|
||||
]
|
||||
var promise = fileList._onClickRestoreSelected({
|
||||
preventDefault: function () {
|
||||
}
|
||||
})
|
||||
);
|
||||
expect($restoreLink.find('.icon-loading-small').length).toEqual(0);
|
||||
});
|
||||
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 () {
|
||||
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'})
|
||||
);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,73 +78,91 @@ trait Trashbin {
|
|||
* @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');
|
||||
$path = $this->getFullTrashPath($user, $path);
|
||||
$client = $this->getSabreClient($user);
|
||||
|
||||
$response = json_decode($this->response->getBody(), true);
|
||||
|
||||
return $response['data']['files'];
|
||||
$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);
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$firstEntry = $this->findFirstTrashedEntry($user, trim($sections[0], '/'));
|
||||
/**
|
||||
* @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::assertNotNull($firstEntry);
|
||||
Assert::assertArraySubset([$name], array_values($trashContent));
|
||||
}
|
||||
|
||||
// query was on the main element ?
|
||||
if (count($sections) === 1) {
|
||||
// already found, return
|
||||
/**
|
||||
* @Then /^user "([^"]*)" in trash folder "([^"]*)" should have (\d+) elements?$/
|
||||
* @param string $user
|
||||
* @param string $folder
|
||||
* @param \Behat\Gherkin\Node\TableNode|null $expectedElements
|
||||
*/
|
||||
public function checkTrashSize($user, $folder, $expectedCount) {
|
||||
$elementList = $this->listTrashbinFolder($user, $folder);
|
||||
Assert::assertEquals($expectedCount, count($elementList));
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
$subdir = trim(dirname($sections[1]), '/');
|
||||
if ($subdir !== '' && $subdir !== '.') {
|
||||
$subdir = $firstEntry . '/' . $subdir;
|
||||
} else {
|
||||
$subdir = $firstEntry;
|
||||
}
|
||||
|
||||
$listing = $this->listTrashbinFolder($user, $subdir);
|
||||
$checkedName = basename($path);
|
||||
|
||||
$found = false;
|
||||
foreach ($listing as $entry) {
|
||||
if ($entry['name'] === $checkedName) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Assert::assertTrue($found);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -423,8 +423,12 @@ trait WebDav {
|
|||
return $parsedResponse;
|
||||
}
|
||||
|
||||
public function makeSabrePath($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"
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue