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\\Expiration' => $baseDir . '/../lib/Expiration.php',
|
||||||
'OCA\\Files_Trashbin\\Helper' => $baseDir . '/../lib/Helper.php',
|
'OCA\\Files_Trashbin\\Helper' => $baseDir . '/../lib/Helper.php',
|
||||||
'OCA\\Files_Trashbin\\Hooks' => $baseDir . '/../lib/Hooks.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\\ITrash' => $baseDir . '/../lib/Sabre/ITrash.php',
|
||||||
'OCA\\Files_Trashbin\\Sabre\\PropfindPlugin' => $baseDir . '/../lib/Sabre/PropfindPlugin.php',
|
'OCA\\Files_Trashbin\\Sabre\\PropfindPlugin' => $baseDir . '/../lib/Sabre/PropfindPlugin.php',
|
||||||
'OCA\\Files_Trashbin\\Sabre\\RestoreFolder' => $baseDir . '/../lib/Sabre/RestoreFolder.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\\Expiration' => __DIR__ . '/..' . '/../lib/Expiration.php',
|
||||||
'OCA\\Files_Trashbin\\Helper' => __DIR__ . '/..' . '/../lib/Helper.php',
|
'OCA\\Files_Trashbin\\Helper' => __DIR__ . '/..' . '/../lib/Helper.php',
|
||||||
'OCA\\Files_Trashbin\\Hooks' => __DIR__ . '/..' . '/../lib/Hooks.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\\ITrash' => __DIR__ . '/..' . '/../lib/Sabre/ITrash.php',
|
||||||
'OCA\\Files_Trashbin\\Sabre\\PropfindPlugin' => __DIR__ . '/..' . '/../lib/Sabre/PropfindPlugin.php',
|
'OCA\\Files_Trashbin\\Sabre\\PropfindPlugin' => __DIR__ . '/..' . '/../lib/Sabre/PropfindPlugin.php',
|
||||||
'OCA\\Files_Trashbin\\Sabre\\RestoreFolder' => __DIR__ . '/..' . '/../lib/Sabre/RestoreFolder.php',
|
'OCA\\Files_Trashbin\\Sabre\\RestoreFolder' => __DIR__ . '/..' . '/../lib/Sabre/RestoreFolder.php',
|
||||||
|
|
|
@ -17,12 +17,21 @@ OCA.Trashbin = {};
|
||||||
*/
|
*/
|
||||||
OCA.Trashbin.App = {
|
OCA.Trashbin.App = {
|
||||||
_initialized: false,
|
_initialized: false,
|
||||||
|
/** @type {OC.Files.Client} */
|
||||||
|
client: null,
|
||||||
|
|
||||||
initialize: function($el) {
|
initialize: function ($el) {
|
||||||
if (this._initialized) {
|
if (this._initialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._initialized = true;
|
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();
|
var urlParams = OC.Util.History.parseUrlQuery();
|
||||||
this.fileList = new OCA.Trashbin.FileList(
|
this.fileList = new OCA.Trashbin.FileList(
|
||||||
$('#app-content-trashbin'), {
|
$('#app-content-trashbin'), {
|
||||||
|
@ -31,22 +40,24 @@ OCA.Trashbin.App = {
|
||||||
scrollTo: urlParams.scrollto,
|
scrollTo: urlParams.scrollto,
|
||||||
config: OCA.Files.App.getFilesConfig(),
|
config: OCA.Files.App.getFilesConfig(),
|
||||||
multiSelectMenu: [
|
multiSelectMenu: [
|
||||||
{
|
{
|
||||||
name: 'restore',
|
name: 'restore',
|
||||||
displayName: t('files', 'Restore'),
|
displayName: t('files', 'Restore'),
|
||||||
iconClass: 'icon-history',
|
iconClass: 'icon-history',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'delete',
|
name: 'delete',
|
||||||
displayName: t('files', 'Delete'),
|
displayName: t('files', 'Delete'),
|
||||||
iconClass: 'icon-delete',
|
iconClass: 'icon-delete',
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
client: this.client
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
_createFileActions: function() {
|
_createFileActions: function () {
|
||||||
|
var client = this.client;
|
||||||
var fileActions = new OCA.Files.FileActions();
|
var fileActions = new OCA.Files.FileActions();
|
||||||
fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) {
|
fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) {
|
||||||
var dir = context.fileList.getCurrentDirectory();
|
var dir = context.fileList.getCurrentDirectory();
|
||||||
|
@ -62,17 +73,19 @@ OCA.Trashbin.App = {
|
||||||
mime: 'all',
|
mime: 'all',
|
||||||
permissions: OC.PERMISSION_READ,
|
permissions: OC.PERMISSION_READ,
|
||||||
iconClass: 'icon-history',
|
iconClass: 'icon-history',
|
||||||
actionHandler: function(filename, context) {
|
actionHandler: function (filename, context) {
|
||||||
var fileList = context.fileList;
|
var fileList = context.fileList;
|
||||||
var tr = fileList.findFileEl(filename);
|
var tr = fileList.findFileEl(filename);
|
||||||
var deleteAction = tr.children("td.date").children(".action.delete");
|
fileList.showFileBusyState(tr, true);
|
||||||
deleteAction.removeClass('icon-delete').addClass('icon-loading-small');
|
var dir = context.fileList.getCurrentDirectory();
|
||||||
$.post(OC.filePath('files_trashbin', 'ajax', 'undelete.php'), {
|
client.move(OC.joinPaths('trash', dir, filename), OC.joinPaths('restore', filename), true)
|
||||||
files: JSON.stringify([filename]),
|
.then(
|
||||||
dir: fileList.getCurrentDirectory()
|
fileList._removeCallback.bind(fileList, [filename]),
|
||||||
},
|
function () {
|
||||||
_.bind(fileList._removeCallback, fileList)
|
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',
|
mime: 'all',
|
||||||
permissions: OC.PERMISSION_READ,
|
permissions: OC.PERMISSION_READ,
|
||||||
iconClass: 'icon-delete',
|
iconClass: 'icon-delete',
|
||||||
render: function(actionSpec, isDefault, context) {
|
render: function (actionSpec, isDefault, context) {
|
||||||
var $actionLink = fileActions._makeActionLink(actionSpec, context);
|
var $actionLink = fileActions._makeActionLink(actionSpec, context);
|
||||||
$actionLink.attr('original-title', t('files_trashbin', 'Delete permanently'));
|
$actionLink.attr('original-title', t('files_trashbin', 'Delete permanently'));
|
||||||
$actionLink.children('img').attr('alt', t('files_trashbin', 'Delete permanently'));
|
$actionLink.children('img').attr('alt', t('files_trashbin', 'Delete permanently'));
|
||||||
context.$file.find('td:last').append($actionLink);
|
context.$file.find('td:last').append($actionLink);
|
||||||
return $actionLink;
|
return $actionLink;
|
||||||
},
|
},
|
||||||
actionHandler: function(filename, context) {
|
actionHandler: function (filename, context) {
|
||||||
var fileList = context.fileList;
|
var fileList = context.fileList;
|
||||||
$('.tipsy').remove();
|
$('.tipsy').remove();
|
||||||
var tr = fileList.findFileEl(filename);
|
var tr = fileList.findFileEl(filename);
|
||||||
var deleteAction = tr.children("td.date").children(".action.delete");
|
fileList.showFileBusyState(tr, true);
|
||||||
deleteAction.removeClass('icon-delete').addClass('icon-loading-small');
|
var dir = context.fileList.getCurrentDirectory();
|
||||||
$.post(OC.filePath('files_trashbin', 'ajax', 'delete.php'), {
|
client.remove(OC.joinPaths('trash', dir, filename))
|
||||||
files: JSON.stringify([filename]),
|
.then(
|
||||||
dir: fileList.getCurrentDirectory()
|
fileList._removeCallback.bind(fileList, [filename]),
|
||||||
},
|
function () {
|
||||||
_.bind(fileList._removeCallback, fileList)
|
fileList.showFileBusyState(tr, false);
|
||||||
);
|
OC.Notification.show(t('files_trashbin', 'Error while removing file from trashbin'));
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return fileActions;
|
return fileActions;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function () {
|
||||||
$('#app-content-trashbin').one('show', function() {
|
$('#app-content-trashbin').one('show', function () {
|
||||||
var App = OCA.Trashbin.App;
|
var App = OCA.Trashbin.App;
|
||||||
App.initialize($('#app-content-trashbin'));
|
App.initialize($('#app-content-trashbin'));
|
||||||
// force breadcrumb init
|
// force breadcrumb init
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
*/
|
*/
|
||||||
(function() {
|
(function() {
|
||||||
var DELETED_REGEXP = new RegExp(/^(.+)\.d[0-9]+$/);
|
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.
|
* Convert a file name in the format filename.d12345 to the real file name.
|
||||||
|
@ -36,17 +38,30 @@
|
||||||
* @param [options] map of options
|
* @param [options] map of options
|
||||||
*/
|
*/
|
||||||
var FileList = function($el, options) {
|
var FileList = function($el, options) {
|
||||||
|
this.client = options.client;
|
||||||
this.initialize($el, options);
|
this.initialize($el, options);
|
||||||
};
|
};
|
||||||
FileList.prototype = _.extend({}, OCA.Files.FileList.prototype,
|
FileList.prototype = _.extend({}, OCA.Files.FileList.prototype,
|
||||||
/** @lends OCA.Trashbin.FileList.prototype */ {
|
/** @lends OCA.Trashbin.FileList.prototype */ {
|
||||||
id: 'trashbin',
|
id: 'trashbin',
|
||||||
appName: t('files_trashbin', 'Deleted files'),
|
appName: t('files_trashbin', 'Deleted files'),
|
||||||
|
/** @type {OC.Files.Client} */
|
||||||
|
client: null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
initialize: function() {
|
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);
|
var result = OCA.Files.FileList.prototype.initialize.apply(this, arguments);
|
||||||
this.$el.find('.undelete').click('click', _.bind(this._onClickRestoreSelected, this));
|
this.$el.find('.undelete').click('click', _.bind(this._onClickRestoreSelected, this));
|
||||||
|
|
||||||
|
@ -91,23 +106,6 @@
|
||||||
return tr;
|
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) {
|
getAjaxUrl: function(action, params) {
|
||||||
var q = '';
|
var q = '';
|
||||||
if (params) {
|
if (params) {
|
||||||
|
@ -140,15 +138,10 @@
|
||||||
this.$el.find('#filestable th').toggleClass('hidden', !exists);
|
this.$el.find('#filestable th').toggleClass('hidden', !exists);
|
||||||
},
|
},
|
||||||
|
|
||||||
_removeCallback: function(result) {
|
_removeCallback: function(files) {
|
||||||
if (result.status !== 'success') {
|
|
||||||
OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error'));
|
|
||||||
}
|
|
||||||
|
|
||||||
var files = result.data.success;
|
|
||||||
var $el;
|
var $el;
|
||||||
for (var i = 0; i < files.length; i++) {
|
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.remove({type: $el.attr('data-type'), size: $el.attr('data-size')});
|
||||||
}
|
}
|
||||||
this.fileSummary.update();
|
this.fileSummary.update();
|
||||||
|
@ -158,97 +151,71 @@
|
||||||
_onClickRestoreSelected: function(event) {
|
_onClickRestoreSelected: function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
var self = this;
|
var self = this;
|
||||||
var allFiles = this.$el.find('.select-all').is(':checked');
|
var files = _.pluck(this.getSelectedFiles(), 'name');
|
||||||
var files = [];
|
for (var i = 0; i < files.length; i++) {
|
||||||
var params = {};
|
var tr = this.findFileEl(files[i]);
|
||||||
this.fileMultiSelectMenu.toggleLoading('restore', true);
|
this.showFileBusyState(tr, 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()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$.post(OC.filePath('files_trashbin', 'ajax', 'undelete.php'),
|
this.fileMultiSelectMenu.toggleLoading('restore', true);
|
||||||
params,
|
var restorePromises = files.map(function(file) {
|
||||||
function(result) {
|
return self.client.move(OC.joinPaths('trash', self.getCurrentDirectory(), file), OC.joinPaths('restore', file), true)
|
||||||
if (allFiles) {
|
.then(
|
||||||
if (result.status !== 'success') {
|
function() {
|
||||||
OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error'));
|
self._removeCallback([file]);
|
||||||
}
|
}
|
||||||
self.hideMask();
|
);
|
||||||
// simply remove all files
|
});
|
||||||
self.setFiles([]);
|
return Promise.all(restorePromises).then(
|
||||||
}
|
function() {
|
||||||
else {
|
|
||||||
self._removeCallback(result);
|
|
||||||
}
|
|
||||||
self.fileMultiSelectMenu.toggleLoading('restore', false);
|
self.fileMultiSelectMenu.toggleLoading('restore', false);
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
OC.Notification.show(t('files_trashbin', 'Error while restoring files from trashbin'));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
event.preventDefault();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onClickDeleteSelected: function(event) {
|
_onClickDeleteSelected: function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
var self = this;
|
var self = this;
|
||||||
var allFiles = this.$el.find('.select-all').is(':checked');
|
var allFiles = this.$el.find('.select-all').is(':checked');
|
||||||
var files = [];
|
var files = _.pluck(this.getSelectedFiles(), 'name');
|
||||||
var params = {};
|
for (var i = 0; i < files.length; i++) {
|
||||||
if (allFiles) {
|
var tr = this.findFileEl(files[i]);
|
||||||
params = {
|
this.showFileBusyState(tr, true);
|
||||||
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) {
|
if (allFiles) {
|
||||||
this.showMask();
|
return this.client.remove(OC.joinPaths('trash', this.getCurrentDirectory()))
|
||||||
}
|
.then(
|
||||||
else {
|
function() {
|
||||||
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'));
|
|
||||||
}
|
|
||||||
self.hideMask();
|
self.hideMask();
|
||||||
// simply remove all files
|
|
||||||
self.setFiles([]);
|
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);
|
self.fileMultiSelectMenu.toggleLoading('delete', false);
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
OC.Notification.show(t('files_trashbin', 'Error while removing files from trashbin'));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onClickFile: function(event) {
|
_onClickFile: function(event) {
|
||||||
|
@ -277,6 +244,13 @@
|
||||||
return true;
|
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
|
* Reloads the file list using ajax call
|
||||||
*
|
*
|
||||||
|
@ -290,39 +264,25 @@
|
||||||
if (this._reloadCall) {
|
if (this._reloadCall) {
|
||||||
this._reloadCall.abort();
|
this._reloadCall.abort();
|
||||||
}
|
}
|
||||||
this._reloadCall = $.ajax({
|
this._reloadCall = this.client.getFolderContents(
|
||||||
url: this.getAjaxUrl('list'),
|
'trash/' + this.getCurrentDirectory(), {
|
||||||
data: {
|
includeParent: false,
|
||||||
dir : this.getCurrentDirectory(),
|
properties: this._getWebdavProperties()
|
||||||
sort: this._sort,
|
|
||||||
sortdirection: this._sortDirection
|
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
var callBack = this.reloadCallback.bind(this);
|
var callBack = this.reloadCallback.bind(this);
|
||||||
return this._reloadCall.then(callBack, callBack);
|
return this._reloadCall.then(callBack, callBack);
|
||||||
},
|
},
|
||||||
reloadCallback: function(result) {
|
reloadCallback: function(status, result) {
|
||||||
delete this._reloadCall;
|
delete this._reloadCall;
|
||||||
this.hideMask();
|
this.hideMask();
|
||||||
|
|
||||||
if (!result || result.status === 'error') {
|
if (status === 401) {
|
||||||
// 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) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Firewall Blocked request?
|
// Firewall Blocked request?
|
||||||
if (result.status === 403) {
|
if (status === 403) {
|
||||||
// Go home
|
// Go home
|
||||||
this.changeDirectory('/');
|
this.changeDirectory('/');
|
||||||
OC.Notification.show(t('files', 'This operation is forbidden'));
|
OC.Notification.show(t('files', 'This operation is forbidden'));
|
||||||
|
@ -330,24 +290,24 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Did share service die or something else fail?
|
// Did share service die or something else fail?
|
||||||
if (result.status === 500) {
|
if (status === 500) {
|
||||||
// Go home
|
// Go home
|
||||||
this.changeDirectory('/');
|
this.changeDirectory('/');
|
||||||
OC.Notification.show(t('files', 'This directory is unavailable, please check the logs or contact the administrator'));
|
OC.Notification.show(t('files', 'This directory is unavailable, please check the logs or contact the administrator'));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.status === 404) {
|
if (status === 404) {
|
||||||
// go back home
|
// go back home
|
||||||
this.changeDirectory('/');
|
this.changeDirectory('/');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// aborted ?
|
// aborted ?
|
||||||
if (result.status === 0){
|
if (status === 0){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setFiles(result.data.files);
|
this.setFiles(result);
|
||||||
return true;
|
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;
|
namespace OCA\Files_Trashbin\Sabre;
|
||||||
|
|
||||||
|
use OCP\Files\FileInfo;
|
||||||
|
|
||||||
interface ITrash {
|
interface ITrash {
|
||||||
public function restore(): bool;
|
public function restore(): bool;
|
||||||
|
|
||||||
|
@ -35,4 +37,6 @@ interface ITrash {
|
||||||
public function getSize();
|
public function getSize();
|
||||||
|
|
||||||
public function getFileId(): int;
|
public function getFileId(): int;
|
||||||
|
|
||||||
|
public function getFileInfo(): FileInfo;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,8 @@ declare(strict_types=1);
|
||||||
namespace OCA\Files_Trashbin\Sabre;
|
namespace OCA\Files_Trashbin\Sabre;
|
||||||
|
|
||||||
use OCA\DAV\Connector\Sabre\FilesPlugin;
|
use OCA\DAV\Connector\Sabre\FilesPlugin;
|
||||||
|
use OCP\Constants;
|
||||||
|
use OCP\IPreview;
|
||||||
use Sabre\DAV\INode;
|
use Sabre\DAV\INode;
|
||||||
use Sabre\DAV\PropFind;
|
use Sabre\DAV\PropFind;
|
||||||
use Sabre\DAV\Server;
|
use Sabre\DAV\Server;
|
||||||
|
@ -39,7 +41,13 @@ class PropfindPlugin extends ServerPlugin {
|
||||||
/** @var Server */
|
/** @var Server */
|
||||||
private $server;
|
private $server;
|
||||||
|
|
||||||
public function __construct() {
|
/** @var IPreview */
|
||||||
|
private $previewManager;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
IPreview $previewManager
|
||||||
|
) {
|
||||||
|
$this->previewManager = $previewManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function initialize(Server $server) {
|
public function initialize(Server $server) {
|
||||||
|
@ -54,11 +62,11 @@ class PropfindPlugin extends ServerPlugin {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$propFind->handle(self::TRASHBIN_FILENAME, function() use ($node) {
|
$propFind->handle(self::TRASHBIN_FILENAME, function () use ($node) {
|
||||||
return $node->getFilename();
|
return $node->getFilename();
|
||||||
});
|
});
|
||||||
|
|
||||||
$propFind->handle(self::TRASHBIN_ORIGINAL_LOCATION, function() use ($node) {
|
$propFind->handle(self::TRASHBIN_ORIGINAL_LOCATION, function () use ($node) {
|
||||||
return $node->getOriginalLocation();
|
return $node->getOriginalLocation();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -73,6 +81,28 @@ class PropfindPlugin extends ServerPlugin {
|
||||||
$propFind->handle(FilesPlugin::FILEID_PROPERTYNAME, function () use ($node) {
|
$propFind->handle(FilesPlugin::FILEID_PROPERTYNAME, function () use ($node) {
|
||||||
return $node->getFileId();
|
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\Exception\Forbidden;
|
||||||
use Sabre\DAV\IFile;
|
use Sabre\DAV\IFile;
|
||||||
|
|
||||||
class TrashFile implements IFile, ITrash {
|
class TrashFile extends AbstractTrash implements IFile, ITrash {
|
||||||
/** @var string */
|
/** @var string */
|
||||||
private $userId;
|
private $userId;
|
||||||
|
|
||||||
/** @var FileInfo */
|
|
||||||
private $data;
|
|
||||||
|
|
||||||
public function __construct(string $userId, FileInfo $data) {
|
public function __construct(string $userId, FileInfo $data) {
|
||||||
$this->userId = $userId;
|
$this->userId = $userId;
|
||||||
$this->data = $data;
|
parent::__construct($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function put($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');
|
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() {
|
public function delete() {
|
||||||
\OCA\Files_Trashbin\Trashbin::delete($this->data->getName(), $this->userId, $this->getLastModified());
|
\OCA\Files_Trashbin\Trashbin::delete($this->data->getName(), $this->userId, $this->getLastModified());
|
||||||
}
|
}
|
||||||
|
@ -71,29 +56,11 @@ class TrashFile implements IFile, ITrash {
|
||||||
throw new Forbidden();
|
throw new Forbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLastModified(): int {
|
|
||||||
return $this->data->getMtime();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function restore(): bool {
|
public function restore(): bool {
|
||||||
return \OCA\Files_Trashbin\Trashbin::restore($this->getName(), $this->data->getName(), $this->getLastModified());
|
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 {
|
public function getOriginalLocation(): string {
|
||||||
return $this->data['extraData'];
|
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\Exception\NotFound;
|
||||||
use Sabre\DAV\ICollection;
|
use Sabre\DAV\ICollection;
|
||||||
|
|
||||||
class TrashFolder implements ICollection, ITrash {
|
class TrashFolder extends AbstractTrash implements ICollection, ITrash {
|
||||||
/** @var string */
|
/** @var string */
|
||||||
private $userId;
|
private $userId;
|
||||||
|
|
||||||
/** @var FileInfo */
|
|
||||||
private $data;
|
|
||||||
|
|
||||||
public function __construct(string $root, string $userId, FileInfo $data) {
|
public function __construct(string $root, string $userId, FileInfo $data) {
|
||||||
$this->userId = $userId;
|
$this->userId = $userId;
|
||||||
$this->data = $data;
|
parent::__construct($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createFile($name, $data = null) {
|
public function createFile($name, $data = null) {
|
||||||
|
@ -100,31 +97,11 @@ class TrashFolder implements ICollection, ITrash {
|
||||||
throw new Forbidden();
|
throw new Forbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLastModified(): int {
|
|
||||||
return $this->data->getMtime();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function restore(): bool {
|
public function restore(): bool {
|
||||||
return \OCA\Files_Trashbin\Trashbin::restore($this->getName(), $this->data->getName(), $this->getLastModified());
|
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 {
|
public function getOriginalLocation(): string {
|
||||||
return $this->data['extraData'];
|
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\Exception\Forbidden;
|
||||||
use Sabre\DAV\IFile;
|
use Sabre\DAV\IFile;
|
||||||
|
|
||||||
class TrashFolderFile implements IFile, ITrash {
|
class TrashFolderFile extends AbstractTrash implements IFile, ITrash {
|
||||||
/** @var string */
|
/** @var string */
|
||||||
private $root;
|
private $root;
|
||||||
|
|
||||||
/** @var string */
|
/** @var string */
|
||||||
private $userId;
|
private $userId;
|
||||||
|
|
||||||
/** @var FileInfo */
|
|
||||||
private $data;
|
|
||||||
|
|
||||||
/** @var string */
|
/** @var string */
|
||||||
private $location;
|
private $location;
|
||||||
|
|
||||||
|
@ -46,8 +43,8 @@ class TrashFolderFile implements IFile, ITrash {
|
||||||
string $location) {
|
string $location) {
|
||||||
$this->root = $root;
|
$this->root = $root;
|
||||||
$this->userId = $userId;
|
$this->userId = $userId;
|
||||||
$this->data = $data;
|
|
||||||
$this->location = $location;
|
$this->location = $location;
|
||||||
|
parent::__construct($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function put($data) {
|
public function put($data) {
|
||||||
|
@ -58,51 +55,19 @@ class TrashFolderFile implements IFile, ITrash {
|
||||||
return $this->data->getStorage()->fopen($this->data->getInternalPath(), 'rb');
|
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() {
|
public function delete() {
|
||||||
\OCA\Files_Trashbin\Trashbin::delete($this->root . '/' . $this->getName(), $this->userId, null);
|
\OCA\Files_Trashbin\Trashbin::delete($this->root . '/' . $this->getName(), $this->userId, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getName(): string {
|
|
||||||
return $this->data->getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setName($name) {
|
public function setName($name) {
|
||||||
throw new Forbidden();
|
throw new Forbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLastModified(): int {
|
|
||||||
return $this->data->getMtime();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function restore(): bool {
|
public function restore(): bool {
|
||||||
return \OCA\Files_Trashbin\Trashbin::restore($this->root . '/' . $this->getName(), $this->data->getName(), null);
|
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 {
|
public function getOriginalLocation(): string {
|
||||||
return $this->location . '/' . $this->getFilename();
|
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\Exception\NotFound;
|
||||||
use Sabre\DAV\ICollection;
|
use Sabre\DAV\ICollection;
|
||||||
|
|
||||||
class TrashFolderFolder implements ICollection, ITrash {
|
class TrashFolderFolder extends AbstractTrash implements ICollection, ITrash {
|
||||||
|
|
||||||
/** @var string */
|
/** @var string */
|
||||||
private $root;
|
private $root;
|
||||||
|
@ -36,9 +36,6 @@ class TrashFolderFolder implements ICollection, ITrash {
|
||||||
/** @var string */
|
/** @var string */
|
||||||
private $userId;
|
private $userId;
|
||||||
|
|
||||||
/** @var FileInfo */
|
|
||||||
private $data;
|
|
||||||
|
|
||||||
/** @var string */
|
/** @var string */
|
||||||
private $location;
|
private $location;
|
||||||
|
|
||||||
|
@ -48,8 +45,8 @@ class TrashFolderFolder implements ICollection, ITrash {
|
||||||
string $location) {
|
string $location) {
|
||||||
$this->root = $root;
|
$this->root = $root;
|
||||||
$this->userId = $userId;
|
$this->userId = $userId;
|
||||||
$this->data = $data;
|
|
||||||
$this->location = $location;
|
$this->location = $location;
|
||||||
|
parent::__construct($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createFile($name, $data = null) {
|
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);
|
\OCA\Files_Trashbin\Trashbin::delete($this->root . '/' . $this->getName(), $this->userId, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getName(): string {
|
|
||||||
return $this->data->getName();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setName($name) {
|
public function setName($name) {
|
||||||
throw new Forbidden();
|
throw new Forbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLastModified(): int {
|
|
||||||
return $this->data->getMtime();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function restore(): bool {
|
public function restore(): bool {
|
||||||
return \OCA\Files_Trashbin\Trashbin::restore($this->root . '/' . $this->getName(), $this->data->getName(), null);
|
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 {
|
public function getOriginalLocation(): string {
|
||||||
return $this->location . '/' . $this->getFilename();
|
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
|
* ownCloud
|
||||||
*
|
*
|
||||||
* @author Vincent Petry
|
* @author Vincent Petry
|
||||||
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
|
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||||
* License as published by the Free Software Foundation; either
|
* License as published by the Free Software Foundation; either
|
||||||
* version 3 of the License, or any later version.
|
* version 3 of the License, or any later version.
|
||||||
*
|
*
|
||||||
* This library is distributed in the hope that it will be useful,
|
* This library is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Affero General Public
|
* 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/>.
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
describe('OCA.Trashbin.FileList tests', function() {
|
describe('OCA.Trashbin.FileList tests', function () {
|
||||||
var testFiles, alertStub, notificationStub, fileList;
|
var testFiles, alertStub, notificationStub, fileList, client;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
alertStub = sinon.stub(OC.dialogs, 'alert');
|
alertStub = sinon.stub(OC.dialogs, 'alert');
|
||||||
notificationStub = sinon.stub(OC.Notification, 'show');
|
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
|
// init parameters and test table elements
|
||||||
$('#testArea').append(
|
$('#testArea').append(
|
||||||
'<div id="app-content-trashbin">' +
|
'<div id="app-content-trashbin">' +
|
||||||
|
@ -59,21 +66,24 @@ describe('OCA.Trashbin.FileList tests', function() {
|
||||||
testFiles = [{
|
testFiles = [{
|
||||||
id: 1,
|
id: 1,
|
||||||
type: 'file',
|
type: 'file',
|
||||||
name: 'One.txt',
|
name: 'One.txt.d11111',
|
||||||
|
displayName: 'One.txt',
|
||||||
mtime: 11111000,
|
mtime: 11111000,
|
||||||
mimetype: 'text/plain',
|
mimetype: 'text/plain',
|
||||||
etag: 'abc'
|
etag: 'abc'
|
||||||
}, {
|
}, {
|
||||||
id: 2,
|
id: 2,
|
||||||
type: 'file',
|
type: 'file',
|
||||||
name: 'Two.jpg',
|
name: 'Two.jpg.d22222',
|
||||||
|
displayName: 'Two.jpg',
|
||||||
mtime: 22222000,
|
mtime: 22222000,
|
||||||
mimetype: 'image/jpeg',
|
mimetype: 'image/jpeg',
|
||||||
etag: 'def',
|
etag: 'def',
|
||||||
}, {
|
}, {
|
||||||
id: 3,
|
id: 3,
|
||||||
type: 'file',
|
type: 'file',
|
||||||
name: 'Three.pdf',
|
name: 'Three.pdf.d33333',
|
||||||
|
displayName: 'Three.pdf',
|
||||||
mtime: 33333000,
|
mtime: 33333000,
|
||||||
mimetype: 'application/pdf',
|
mimetype: 'application/pdf',
|
||||||
etag: '123',
|
etag: '123',
|
||||||
|
@ -81,7 +91,8 @@ describe('OCA.Trashbin.FileList tests', function() {
|
||||||
id: 4,
|
id: 4,
|
||||||
type: 'dir',
|
type: 'dir',
|
||||||
mtime: 99999000,
|
mtime: 99999000,
|
||||||
name: 'somedir',
|
name: 'somedir.d99999',
|
||||||
|
displayName: 'somedir',
|
||||||
mimetype: 'httpd/unix-directory',
|
mimetype: 'httpd/unix-directory',
|
||||||
etag: '456'
|
etag: '456'
|
||||||
}];
|
}];
|
||||||
|
@ -92,20 +103,21 @@ describe('OCA.Trashbin.FileList tests', function() {
|
||||||
$('#app-content-trashbin'), {
|
$('#app-content-trashbin'), {
|
||||||
fileActions: fileActions,
|
fileActions: fileActions,
|
||||||
multiSelectMenu: [{
|
multiSelectMenu: [{
|
||||||
name: 'restore',
|
name: 'restore',
|
||||||
displayName: t('files', 'Restore'),
|
displayName: t('files', 'Restore'),
|
||||||
iconClass: 'icon-history',
|
iconClass: 'icon-history',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'delete',
|
name: 'delete',
|
||||||
displayName: t('files', 'Delete'),
|
displayName: t('files', 'Delete'),
|
||||||
iconClass: 'icon-delete',
|
iconClass: 'icon-delete',
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
client: client
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
afterEach(function() {
|
afterEach(function () {
|
||||||
testFiles = undefined;
|
testFiles = undefined;
|
||||||
fileList.destroy();
|
fileList.destroy();
|
||||||
fileList = undefined;
|
fileList = undefined;
|
||||||
|
@ -114,17 +126,17 @@ describe('OCA.Trashbin.FileList tests', function() {
|
||||||
notificationStub.restore();
|
notificationStub.restore();
|
||||||
alertStub.restore();
|
alertStub.restore();
|
||||||
});
|
});
|
||||||
describe('Initialization', function() {
|
describe('Initialization', function () {
|
||||||
it('Sorts by mtime by default', function() {
|
it('Sorts by mtime by default', function () {
|
||||||
expect(fileList._sort).toEqual('mtime');
|
expect(fileList._sort).toEqual('mtime');
|
||||||
expect(fileList._sortDirection).toEqual('desc');
|
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);
|
expect(fileList.getDirectoryPermissions()).toEqual(OC.PERMISSION_READ | OC.PERMISSION_DELETE);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('Breadcrumbs', function() {
|
describe('Breadcrumbs', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
var data = {
|
var data = {
|
||||||
status: 'success',
|
status: 'success',
|
||||||
data: {
|
data: {
|
||||||
|
@ -133,13 +145,13 @@ describe('OCA.Trashbin.FileList tests', function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
fakeServer.respondWith(/\/index\.php\/apps\/files_trashbin\/ajax\/list.php\?dir=%2Fsubdir/, [
|
fakeServer.respondWith(/\/index\.php\/apps\/files_trashbin\/ajax\/list.php\?dir=%2Fsubdir/, [
|
||||||
200, {
|
200, {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
},
|
},
|
||||||
JSON.stringify(data)
|
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);
|
fileList.changeDirectory('/subdir', false, true);
|
||||||
fakeServer.respond();
|
fakeServer.respond();
|
||||||
var $crumbs = fileList.$el.find('#controls .crumb');
|
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');
|
.toEqual(OC.webroot + '/index.php/apps/files?view=trashbin&dir=/subdir');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('Rendering rows', function() {
|
describe('Rendering rows', function () {
|
||||||
it('renders rows with the correct data when in root', function() {
|
it('renders rows with the correct data when in root', function () {
|
||||||
// dir listing is false when in root
|
// dir listing is false when in root
|
||||||
$('#dir').val('/');
|
$('#dir').val('/');
|
||||||
fileList.setFiles(testFiles);
|
fileList.setFiles(testFiles);
|
||||||
|
@ -174,7 +186,7 @@ describe('OCA.Trashbin.FileList tests', function() {
|
||||||
|
|
||||||
expect(fileList.findFileEl('One.txt.d11111')[0]).toEqual($tr[0]);
|
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 listing is false when in root
|
||||||
$('#dir').val('/');
|
$('#dir').val('/');
|
||||||
fileList.setFiles(testFiles);
|
fileList.setFiles(testFiles);
|
||||||
|
@ -196,11 +208,14 @@ describe('OCA.Trashbin.FileList tests', function() {
|
||||||
|
|
||||||
expect(fileList.findFileEl('One.txt.d11111')[0]).toEqual($tr[0]);
|
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 listing is true when in a subdir
|
||||||
$('#dir').val('/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 $rows = fileList.$el.find('tbody tr');
|
||||||
var $tr = $rows.eq(0);
|
var $tr = $rows.eq(0);
|
||||||
expect($rows.length).toEqual(4);
|
expect($rows.length).toEqual(4);
|
||||||
|
@ -218,42 +233,42 @@ describe('OCA.Trashbin.FileList tests', function() {
|
||||||
|
|
||||||
expect(fileList.findFileEl('One.txt')[0]).toEqual($tr[0]);
|
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);
|
expect(fileList.$el.find('tbody tr .filesize').length).toEqual(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('File actions', function() {
|
describe('File actions', function () {
|
||||||
describe('Deleting single files', function() {
|
describe('Deleting single files', function () {
|
||||||
// TODO: checks ajax call
|
// TODO: checks ajax call
|
||||||
// TODO: checks spinner
|
// TODO: checks spinner
|
||||||
// TODO: remove item after delete
|
// TODO: remove item after delete
|
||||||
// TODO: bring back item if delete failed
|
// TODO: bring back item if delete failed
|
||||||
});
|
});
|
||||||
describe('Restoring single files', function() {
|
describe('Restoring single files', function () {
|
||||||
// TODO: checks ajax call
|
// TODO: checks ajax call
|
||||||
// TODO: checks spinner
|
// TODO: checks spinner
|
||||||
// TODO: remove item after restore
|
// TODO: remove item after restore
|
||||||
// TODO: bring back item if restore failed
|
// 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
|
// 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
|
// TODO: check that ajax URL is going through files_trashbin
|
||||||
});
|
});
|
||||||
describe('breadcrumbs', function() {
|
describe('breadcrumbs', function () {
|
||||||
// TODO: test label + URL
|
// TODO: test label + URL
|
||||||
});
|
});
|
||||||
describe('elementToFile', function() {
|
describe('elementToFile', function () {
|
||||||
var $tr;
|
var $tr;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
fileList.setFiles(testFiles);
|
fileList.setFiles(testFiles);
|
||||||
$tr = fileList.findFileEl('One.txt.d11111');
|
$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);
|
var fileInfo = fileList.elementToFile($tr);
|
||||||
expect(fileInfo.id).toEqual(1);
|
expect(fileInfo.id).toEqual(1);
|
||||||
expect(fileInfo.name).toEqual('One.txt.d11111');
|
expect(fileInfo.name).toEqual('One.txt.d11111');
|
||||||
|
@ -265,8 +280,8 @@ describe('OCA.Trashbin.FileList tests', function() {
|
||||||
expect(fileInfo.type).toEqual('file');
|
expect(fileInfo.type).toEqual('file');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('Global Actions', function() {
|
describe('Global Actions', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
fileList.setFiles(testFiles);
|
fileList.setFiles(testFiles);
|
||||||
fileList.findFileEl('One.txt.d11111').find('input:checkbox').click();
|
fileList.findFileEl('One.txt.d11111').find('input:checkbox').click();
|
||||||
fileList.findFileEl('Three.pdf.d33333').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();
|
fileList.$el.find('.actions-selected').click();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function () {
|
||||||
fileList.$el.find('.actions-selected').click();
|
fileList.$el.find('.actions-selected').click();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Delete', function() {
|
describe('Delete', function () {
|
||||||
it('Shows trashbin actions', function() {
|
it('Shows trashbin actions', function () {
|
||||||
// visible because a few files were selected
|
// visible because a few files were selected
|
||||||
expect($('.selectedActions').is(':visible')).toEqual(true);
|
expect($('.selectedActions').is(':visible')).toEqual(true);
|
||||||
expect($('.selectedActions .item-delete').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-delete').is(':visible')).toEqual(false);
|
||||||
expect($('.selectedActions .item-restore').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 request;
|
||||||
var $deleteLink = $('.selectedActions .filesSelectMenu .delete');
|
var promise = fileList._onClickDeleteSelected({
|
||||||
$deleteLink.click();
|
preventDefault: function () {
|
||||||
expect($deleteLink.find('.icon-loading-small').length).toEqual(1);
|
}
|
||||||
expect(fakeServer.requests.length).toEqual(1);
|
});
|
||||||
request = fakeServer.requests[0];
|
var files = ["One.txt.d11111", "Three.pdf.d33333", "somedir.d99999"];
|
||||||
expect(request.url).toEqual(OC.webroot + '/index.php/apps/files_trashbin/ajax/delete.php');
|
expect(fakeServer.requests.length).toEqual(files.length);
|
||||||
expect(OC.parseQueryString(request.requestBody))
|
for (var i = 0; i < files.length; i++) {
|
||||||
.toEqual({'dir': '/', files: '["One.txt.d11111","Three.pdf.d33333","somedir.d99999"]'});
|
request = fakeServer.requests[i];
|
||||||
fakeServer.requests[0].respond(
|
expect(request.url).toEqual(OC.webroot + '/remote.php/dav/trashbin/user/trash/' + files[i]);
|
||||||
200,
|
request.respond(200);
|
||||||
{ 'Content-Type': 'application/json' },
|
}
|
||||||
JSON.stringify({
|
return promise.then(function () {
|
||||||
status: 'success',
|
expect(fileList.findFileEl('One.txt.d11111').length).toEqual(0);
|
||||||
data: {
|
expect(fileList.findFileEl('Three.pdf.d33333').length).toEqual(0);
|
||||||
success: [
|
expect(fileList.findFileEl('somedir.d99999').length).toEqual(0);
|
||||||
{filename: 'One.txt.d11111'},
|
expect(fileList.findFileEl('Two.jpg.d22222').length).toEqual(1);
|
||||||
{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);
|
|
||||||
});
|
});
|
||||||
it('Deletes all files when all selected when "Delete" clicked', function() {
|
it('Deletes all files when all selected when "Delete" clicked', function () {
|
||||||
var request;
|
var request;
|
||||||
$('.select-all').click();
|
$('.select-all').click();
|
||||||
$('.selectedActions .filesSelectMenu .delete').click();
|
var promise = fileList._onClickDeleteSelected({
|
||||||
|
preventDefault: function () {
|
||||||
|
}
|
||||||
|
});
|
||||||
expect(fakeServer.requests.length).toEqual(1);
|
expect(fakeServer.requests.length).toEqual(1);
|
||||||
request = fakeServer.requests[0];
|
request = fakeServer.requests[0];
|
||||||
expect(request.url).toEqual(OC.webroot + '/index.php/apps/files_trashbin/ajax/delete.php');
|
expect(request.url).toEqual(OC.webroot + '/remote.php/dav/trashbin/user/trash');
|
||||||
expect(OC.parseQueryString(request.requestBody))
|
request.respond(200);
|
||||||
.toEqual({'dir': '/', allfiles: 'true'});
|
return promise.then(function () {
|
||||||
fakeServer.requests[0].respond(
|
expect(fileList.isEmpty).toEqual(true);
|
||||||
200,
|
});
|
||||||
{ 'Content-Type': 'application/json' },
|
|
||||||
JSON.stringify({status: 'success'})
|
|
||||||
);
|
|
||||||
expect(fileList.isEmpty).toEqual(true);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('Restore', function() {
|
describe('Restore', function () {
|
||||||
it('Restores selected files when "Restore" clicked', function() {
|
it('Restores selected files when "Restore" clicked', function () {
|
||||||
var request;
|
var request;
|
||||||
var $restoreLink = $('.selectedActions .filesSelectMenu .restore');
|
var promise = fileList._onClickRestoreSelected({
|
||||||
$restoreLink.click();
|
preventDefault: function () {
|
||||||
expect($restoreLink.find('.icon-loading-small').length).toEqual(1);
|
}
|
||||||
expect(fakeServer.requests.length).toEqual(1);
|
});
|
||||||
request = fakeServer.requests[0];
|
var files = ["One.txt.d11111", "Three.pdf.d33333", "somedir.d99999"];
|
||||||
expect(request.url).toEqual(OC.webroot + '/index.php/apps/files_trashbin/ajax/undelete.php');
|
expect(fakeServer.requests.length).toEqual(files.length);
|
||||||
expect(OC.parseQueryString(request.requestBody))
|
for (var i = 0; i < files.length; i++) {
|
||||||
.toEqual({'dir': '/', files: '["One.txt.d11111","Three.pdf.d33333","somedir.d99999"]'});
|
request = fakeServer.requests[i];
|
||||||
fakeServer.requests[0].respond(
|
expect(request.url).toEqual(OC.webroot + '/remote.php/dav/trashbin/user/trash/' + files[i]);
|
||||||
200,
|
expect(request.requestHeaders.Destination).toEqual(OC.webroot + '/remote.php/dav/trashbin/user/restore/' + files[i]);
|
||||||
{ 'Content-Type': 'application/json' },
|
request.respond(200);
|
||||||
JSON.stringify({
|
}
|
||||||
status: 'success',
|
return promise.then(function() {
|
||||||
data: {
|
expect(fileList.findFileEl('One.txt.d11111').length).toEqual(0);
|
||||||
success: [
|
expect(fileList.findFileEl('Three.pdf.d33333').length).toEqual(0);
|
||||||
{filename: 'One.txt.d11111'},
|
expect(fileList.findFileEl('somedir.d99999').length).toEqual(0);
|
||||||
{filename: 'Three.pdf.d33333'},
|
expect(fileList.findFileEl('Two.jpg.d22222').length).toEqual(1);
|
||||||
{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);
|
|
||||||
});
|
});
|
||||||
it('Restores all files when all selected when "Restore" clicked', function() {
|
it('Restores all files when all selected when "Restore" clicked', function () {
|
||||||
var request;
|
var request;
|
||||||
$('.select-all').click();
|
$('.select-all').click();
|
||||||
$('.selectedActions .filesSelectMenu .restore').click();
|
var promise = fileList._onClickRestoreSelected({
|
||||||
expect(fakeServer.requests.length).toEqual(1);
|
preventDefault: function () {
|
||||||
request = fakeServer.requests[0];
|
}
|
||||||
expect(request.url).toEqual(OC.webroot + '/index.php/apps/files_trashbin/ajax/undelete.php');
|
});
|
||||||
expect(OC.parseQueryString(request.requestBody))
|
var files = ["One.txt.d11111", "Two.jpg.d22222", "Three.pdf.d33333", "somedir.d99999"];
|
||||||
.toEqual({'dir': '/', allfiles: 'true'});
|
expect(fakeServer.requests.length).toEqual(files.length);
|
||||||
fakeServer.requests[0].respond(
|
for (var i = 0; i < files.length; i++) {
|
||||||
200,
|
request = fakeServer.requests[i];
|
||||||
{ 'Content-Type': 'application/json' },
|
expect(request.url).toEqual(OC.webroot + '/remote.php/dav/trashbin/user/trash/' + files[i]);
|
||||||
JSON.stringify({status: 'success'})
|
expect(request.requestHeaders.Destination).toEqual(OC.webroot + '/remote.php/dav/trashbin/user/restore/' + files[i]);
|
||||||
);
|
request.respond(200);
|
||||||
expect(fileList.isEmpty).toEqual(true);
|
}
|
||||||
|
return promise.then(function() {
|
||||||
|
expect(fileList.isEmpty).toEqual(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
default:
|
default:
|
||||||
autoload:
|
autoload:
|
||||||
'': %paths.base%/../features/bootstrap
|
'': "%paths.base%/../features/bootstrap"
|
||||||
suites:
|
suites:
|
||||||
default:
|
default:
|
||||||
paths:
|
paths:
|
||||||
- %paths.base%/../features
|
- "%paths.base%/../features"
|
||||||
contexts:
|
contexts:
|
||||||
- FeatureContext:
|
- FeatureContext:
|
||||||
baseUrl: http://localhost:8080/ocs/
|
baseUrl: http://localhost:8080/ocs/
|
||||||
|
@ -27,7 +27,7 @@ default:
|
||||||
ocPath: ../../
|
ocPath: ../../
|
||||||
federation:
|
federation:
|
||||||
paths:
|
paths:
|
||||||
- %paths.base%/../federation_features
|
- "%paths.base%/../federation_features"
|
||||||
contexts:
|
contexts:
|
||||||
- FederationContext:
|
- FederationContext:
|
||||||
baseUrl: http://localhost:8080/ocs/
|
baseUrl: http://localhost:8080/ocs/
|
||||||
|
@ -37,7 +37,7 @@ default:
|
||||||
regular_user_password: 123456
|
regular_user_password: 123456
|
||||||
capabilities:
|
capabilities:
|
||||||
paths:
|
paths:
|
||||||
- %paths.base%/../capabilities_features
|
- "%paths.base%/../capabilities_features"
|
||||||
contexts:
|
contexts:
|
||||||
- CapabilitiesContext:
|
- CapabilitiesContext:
|
||||||
baseUrl: http://localhost:8080/ocs/
|
baseUrl: http://localhost:8080/ocs/
|
||||||
|
@ -47,7 +47,7 @@ default:
|
||||||
regular_user_password: 123456
|
regular_user_password: 123456
|
||||||
sharees:
|
sharees:
|
||||||
paths:
|
paths:
|
||||||
- %paths.base%/../sharees_features
|
- "%paths.base%/../sharees_features"
|
||||||
contexts:
|
contexts:
|
||||||
- ShareesContext:
|
- ShareesContext:
|
||||||
baseUrl: http://localhost:8080/ocs/
|
baseUrl: http://localhost:8080/ocs/
|
||||||
|
@ -57,7 +57,7 @@ default:
|
||||||
regular_user_password: 123456
|
regular_user_password: 123456
|
||||||
setup:
|
setup:
|
||||||
paths:
|
paths:
|
||||||
- %paths.base%/../setup_features
|
- "%paths.base%/../setup_features"
|
||||||
contexts:
|
contexts:
|
||||||
- SetupContext:
|
- SetupContext:
|
||||||
baseUrl: http://localhost:8080/ocs/
|
baseUrl: http://localhost:8080/ocs/
|
||||||
|
@ -67,7 +67,7 @@ default:
|
||||||
regular_user_password: 123456
|
regular_user_password: 123456
|
||||||
filesdrop:
|
filesdrop:
|
||||||
paths:
|
paths:
|
||||||
- %paths.base%/../filesdrop_features
|
- "%paths.base%/../filesdrop_features"
|
||||||
contexts:
|
contexts:
|
||||||
- FilesDropContext:
|
- FilesDropContext:
|
||||||
baseUrl: http://localhost:8080
|
baseUrl: http://localhost:8080
|
||||||
|
@ -77,7 +77,7 @@ default:
|
||||||
regular_user_password: 123456
|
regular_user_password: 123456
|
||||||
ldap:
|
ldap:
|
||||||
paths:
|
paths:
|
||||||
- %paths.base%/../ldap_features
|
- "%paths.base%/../ldap_features"
|
||||||
contexts:
|
contexts:
|
||||||
- LDAPContext:
|
- LDAPContext:
|
||||||
baseUrl: http://localhost:8080
|
baseUrl: http://localhost:8080
|
||||||
|
@ -87,7 +87,7 @@ default:
|
||||||
regular_user_password: what_for
|
regular_user_password: what_for
|
||||||
remoteapi:
|
remoteapi:
|
||||||
paths:
|
paths:
|
||||||
- %paths.base%/../remoteapi_features
|
- "%paths.base%/../remoteapi_features"
|
||||||
contexts:
|
contexts:
|
||||||
- FeatureContext:
|
- FeatureContext:
|
||||||
baseUrl: http://localhost:8080/ocs/
|
baseUrl: http://localhost:8080/ocs/
|
||||||
|
@ -100,4 +100,4 @@ default:
|
||||||
extensions:
|
extensions:
|
||||||
jarnaiz\JUnitFormatter\JUnitFormatterExtension:
|
jarnaiz\JUnitFormatter\JUnitFormatterExtension:
|
||||||
filename: report.xml
|
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;
|
use PHPUnit\Framework\Assert;
|
||||||
|
|
||||||
require __DIR__ . '/../../vendor/autoload.php';
|
require __DIR__ . '/../../vendor/autoload.php';
|
||||||
|
@ -30,16 +28,46 @@ require __DIR__ . '/../../vendor/autoload.php';
|
||||||
* Trashbin functions
|
* Trashbin functions
|
||||||
*/
|
*/
|
||||||
trait Trashbin {
|
trait Trashbin {
|
||||||
|
use WebDav;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @When User :user empties trashbin
|
* @When User :user empties trashbin
|
||||||
* @param string $user user
|
* @param string $user user
|
||||||
*/
|
*/
|
||||||
public function emptyTrashbin($user) {
|
public function emptyTrashbin($user) {
|
||||||
$this->asAn($user);
|
$client = $this->getSabreClient($user);
|
||||||
$body = new \Behat\Gherkin\Node\TableNode([['allfiles', 'true'], ['dir', '%2F']]);
|
$response = $client->request('DELETE', $this->makeSabrePath($user, 'trash', 'trashbin'));
|
||||||
$this->sendingToWithDirectUrl('POST', "/index.php/apps/files_trashbin/ajax/delete.php", $body);
|
Assert::assertEquals(204, $response['statusCode']);
|
||||||
$this->theHTTPStatusCodeShouldBe('200');
|
}
|
||||||
|
|
||||||
|
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
|
* @param string $path path
|
||||||
* @return array response
|
* @return array response
|
||||||
*/
|
*/
|
||||||
public function listTrashbinFolder($user, $path){
|
public function listTrashbinFolder($user, $path) {
|
||||||
$this->asAn($user);
|
$path = $this->getFullTrashPath($user, $path);
|
||||||
$params = '?dir=' . rawurlencode('/' . trim($path, '/'));
|
$client = $this->getSabreClient($user);
|
||||||
$this->sendingToWithDirectUrl('GET', '/index.php/apps/files_trashbin/ajax/list.php' . $params, null);
|
|
||||||
$this->theHTTPStatusCodeShouldBe('200');
|
|
||||||
|
|
||||||
$response = json_decode($this->response->getBody(), true);
|
$results = $client->propfind($this->makeSabrePath($user, 'trash' . $path, 'trashbin'), [
|
||||||
|
'{http://nextcloud.org/ns}trashbin-filename',
|
||||||
return $response['data']['files'];
|
'{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 $user
|
||||||
* @param string $entryText
|
* @param string $folder
|
||||||
* @param string $path
|
* @param \Behat\Gherkin\Node\TableNode|null $expectedElements
|
||||||
*/
|
*/
|
||||||
public function asTheFileOrFolderExistsInTrash($user, $entryText, $path) {
|
public function checkTrashContents($user, $folder, $expectedElements) {
|
||||||
$path = trim($path, '/');
|
$elementList = $this->listTrashbinFolder($user, $folder);
|
||||||
$sections = explode('/', $path, 2);
|
$trashContent = array_filter(array_map(function (array $item) {
|
||||||
|
return $item['{http://nextcloud.org/ns}trashbin-filename'];
|
||||||
$firstEntry = $this->findFirstTrashedEntry($user, trim($sections[0], '/'));
|
}, $elementList));
|
||||||
|
if ($expectedElements instanceof \Behat\Gherkin\Node\TableNode) {
|
||||||
Assert::assertNotNull($firstEntry);
|
$elementRows = $expectedElements->getRows();
|
||||||
|
$elementsSimplified = $this->simplifyArray($elementRows);
|
||||||
// query was on the main element ?
|
foreach ($elementsSimplified as $expectedElement) {
|
||||||
if (count($sections) === 1) {
|
$expectedElement = ltrim($expectedElement, '/');
|
||||||
// already found, return
|
if (array_search($expectedElement, $trashContent) === false) {
|
||||||
return;
|
Assert::fail("$expectedElement" . " is not in trash listing");
|
||||||
}
|
}
|
||||||
|
|
||||||
$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
|
* @Then /^as "([^"]*)" the (file|folder) "([^"]*)" exists in trash$/
|
||||||
*
|
* @param string $user
|
||||||
* @param string $name
|
* @param string $type
|
||||||
* @return string|null real entry name with timestamp suffix or null if not found
|
* @param string $file
|
||||||
*/
|
*/
|
||||||
private function findFirstTrashedEntry($user, $name) {
|
public function checkTrashContains($user, $type, $file) {
|
||||||
$listing = $this->listTrashbinFolder($user, '/');
|
$parent = dirname($file);
|
||||||
|
if ($parent === '.') {
|
||||||
foreach ($listing as $entry) {
|
$parent = '/';
|
||||||
if ($entry['name'] === $name) {
|
|
||||||
return $entry['name'] . '.d' . ((int)$entry['mtime'] / 1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
$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));
|
||||||
|
|
||||||
return null;
|
Assert::assertArraySubset([$name], array_values($trashContent));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -423,8 +423,12 @@ trait WebDav {
|
||||||
return $parsedResponse;
|
return $parsedResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function makeSabrePath($user, $path) {
|
public function makeSabrePath($user, $path, $type = 'files') {
|
||||||
return $this->encodePath($this->getDavFilesPath($user) . $path);
|
if ($type === 'files') {
|
||||||
|
return $this->encodePath($this->getDavFilesPath($user) . $path);
|
||||||
|
} else {
|
||||||
|
return $this->encodePath($this->davPath . '/' . $type . '/' . $user . '/' . $path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSabreClient($user) {
|
public function getSabreClient($user) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
Feature: sharing
|
Feature: sharing
|
||||||
Background:
|
Background:
|
||||||
Given using api version "1"
|
Given using api version "1"
|
||||||
Given using old dav path
|
Given using new dav path
|
||||||
|
|
||||||
# See sharing-v1-part2.feature
|
# See sharing-v1-part2.feature
|
||||||
|
|
||||||
|
@ -295,7 +295,7 @@ Feature: sharing
|
||||||
And user "user0" exists
|
And user "user0" exists
|
||||||
And User "user0" deletes file "/textfile0.txt"
|
And User "user0" deletes file "/textfile0.txt"
|
||||||
When User "user0" empties trashbin
|
When User "user0" empties trashbin
|
||||||
Then the HTTP status code should be "200"
|
Then the HTTP status code should be "204"
|
||||||
|
|
||||||
Scenario: orphaned shares
|
Scenario: orphaned shares
|
||||||
Given As an "admin"
|
Given As an "admin"
|
||||||
|
@ -392,4 +392,4 @@ Feature: sharing
|
||||||
And folder "/shared" of user "user0" is shared with user "user1"
|
And folder "/shared" of user "user0" is shared with user "user1"
|
||||||
When User "user1" moved file "/textfile0.txt" to "/shared/shared_file.txt"
|
When User "user1" moved file "/textfile0.txt" to "/shared/shared_file.txt"
|
||||||
Then as "user1" the file "/shared/shared_file.txt" exists
|
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
|
Feature: trashbin
|
||||||
Background:
|
Background:
|
||||||
Given using api version "1"
|
Given using api version "1"
|
||||||
And using old dav path
|
And using new dav path
|
||||||
And As an "admin"
|
And As an "admin"
|
||||||
And app "files_trashbin" is enabled
|
And app "files_trashbin" is enabled
|
||||||
|
|
||||||
|
@ -9,5 +9,73 @@ Feature: trashbin
|
||||||
Given As an "admin"
|
Given As an "admin"
|
||||||
And user "user0" exists
|
And user "user0" exists
|
||||||
When User "user0" deletes file "/textfile0.txt"
|
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 = new dav.Client(clientOptions);
|
||||||
this._client.xhrProvider = _.bind(this._xhrProvider, this);
|
this._client.xhrProvider = _.bind(this._xhrProvider, this);
|
||||||
|
this._fileInfoParsers = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
Client.NS_OWNCLOUD = 'http://owncloud.org/ns';
|
Client.NS_OWNCLOUD = 'http://owncloud.org/ns';
|
||||||
|
@ -390,7 +391,7 @@
|
||||||
|
|
||||||
// extend the parsed data using the custom parsers
|
// extend the parsed data using the custom parsers
|
||||||
_.each(this._fileInfoParsers, function(parserFunction) {
|
_.each(this._fileInfoParsers, function(parserFunction) {
|
||||||
_.extend(data, parserFunction(response) || {});
|
_.extend(data, parserFunction(response, data) || {});
|
||||||
});
|
});
|
||||||
|
|
||||||
return new FileInfo(data);
|
return new FileInfo(data);
|
||||||
|
|
Loading…
Reference in New Issue