Merge pull request #37633 from nextcloud/fix/trashbin

Trashbin followup fixes
This commit is contained in:
John Molakvoæ 2023-04-11 12:28:36 +02:00 committed by GitHub
commit c6645cbc46
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 111 additions and 46 deletions

View file

@ -21,6 +21,9 @@ import NcBreadcrumb from '@nextcloud/vue/dist/Components/NcBreadcrumb.js'
import NcBreadcrumbs from '@nextcloud/vue/dist/Components/NcBreadcrumbs.js'
import Vue from 'vue'
import { useFilesStore } from '../store/files.ts'
import { usePathsStore } from '../store/paths.ts'
export default Vue.extend({
name: 'BreadCrumbs',
@ -37,7 +40,20 @@ export default Vue.extend({
},
},
setup() {
const filesStore = useFilesStore()
const pathsStore = usePathsStore()
return {
filesStore,
pathsStore,
}
},
computed: {
currentView() {
return this.$navigation.active
},
dirs() {
const cumulativePath = (acc) => (value) => (acc += `${value}/`)
// Generate a cumulative path for each path segment: ['/', '/foo', '/foo/bar', ...] etc
@ -52,7 +68,7 @@ export default Vue.extend({
return {
dir,
exact: true,
name: basename(dir),
name: this.getDirDisplayName(dir),
to,
}
})
@ -60,6 +76,22 @@ export default Vue.extend({
},
methods: {
getNodeFromId(id) {
return this.filesStore.getNode(id)
},
getFileIdFromPath(path) {
return this.pathsStore.getPath(this.currentView?.id, path)
},
getDirDisplayName(path) {
if (path === '/') {
return t('files', 'Home')
}
const fileId = this.getFileIdFromPath(path)
const node = this.getNodeFromId(fileId)
return node?.attributes?.displayName || basename(path)
},
onClick(to) {
if (to?.query?.dir === this.$route.query.dir) {
this.$emit('reload')

View file

@ -63,6 +63,7 @@
<!-- Menu actions -->
<NcActions v-if="active"
ref="actionsMenu"
:disabled="source._loading"
:force-title="true"
:inline="enabledInlineActions.length">
<NcActionButton v-for="action in enabledMenuActions"
@ -433,7 +434,10 @@ export default Vue.extend({
async onActionClick(action) {
const displayName = action.displayName([this.source], this.currentView)
try {
// Set the loading marker
this.loading = action.id
Vue.set(this.source, '_loading', true)
const success = await action.exec(this.source, this.currentView)
if (success) {
showSuccess(this.t('files', '"{displayName}" action executed successfully', { displayName }))
@ -444,7 +448,9 @@ export default Vue.extend({
logger.error('Error while executing action', { action, e })
showError(this.t('files', '"{displayName}" action failed', { displayName }))
} finally {
// Reset the loading marker
this.loading = ''
Vue.set(this.source, '_loading', false)
}
},

View file

@ -22,7 +22,7 @@
<template>
<th class="files-list__column files-list__row-actions-batch" colspan="2">
<NcActions ref="actionsMenu"
:disabled="!!loading"
:disabled="!!loading || areSomeNodesLoading"
:force-title="true"
:inline="3">
<NcActionButton v-for="action in enabledActions"
@ -105,6 +105,10 @@ export default Vue.extend({
.map(fileid => this.getNode(fileid))
.filter(node => node)
},
areSomeNodesLoading() {
return this.nodes.some(node => node._loading)
},
},
methods: {
@ -122,9 +126,16 @@ export default Vue.extend({
const displayName = action.displayName(this.nodes, this.currentView)
const selectionIds = this.selectedNodes
try {
// Set loading markers
this.loading = action.id
this.nodes.forEach(node => {
Vue.set(node, '_loading', true)
})
// Dispatch action execution
const results = await action.execBatch(this.nodes, this.currentView)
// Handle potential failures
if (results.some(result => result !== true)) {
// Remove the failed ids from the selection
const failedIds = selectionIds
@ -142,7 +153,11 @@ export default Vue.extend({
logger.error('Error while executing action', { action, e })
showError(this.t('files', '"{displayName}" action failed', { displayName }))
} finally {
// Remove loading markers
this.loading = null
this.nodes.forEach(node => {
Vue.set(node, '_loading', false)
})
}
},

View file

@ -81,34 +81,26 @@ export const useFilesStore = () => {
Vue.set(this.roots, service, root)
},
onCreatedNode() {
// TODO: do something
},
onDeletedNode(node: Node) {
this.deleteNodes([node])
},
onMovedNode() {
// TODO: do something
},
}
})
const fileStore = store()
// Make sure we only register the listeners once
if (!fileStore.initialized) {
subscribe('files:file:created', fileStore.onCreatedNode)
if (!fileStore._initialized) {
// subscribe('files:file:created', fileStore.onCreatedNode)
subscribe('files:file:deleted', fileStore.onDeletedNode)
subscribe('files:file:moved', fileStore.onMovedNode)
// subscribe('files:file:moved', fileStore.onMovedNode)
// subscribe('files:file:updated', fileStore.onUpdatedNode)
subscribe('files:folder:created', fileStore.onCreatedNode)
// subscribe('files:folder:created', fileStore.onCreatedNode)
subscribe('files:folder:deleted', fileStore.onDeletedNode)
subscribe('files:folder:moved', fileStore.onMovedNode)
// subscribe('files:folder:moved', fileStore.onMovedNode)
// subscribe('files:folder:updated', fileStore.onUpdatedNode)
fileStore.initialized = true
fileStore._initialized = true
}
return fileStore

View file

@ -56,13 +56,13 @@ export const usePathsStore = () => {
const pathsStore = store()
// Make sure we only register the listeners once
if (!pathsStore.initialized) {
if (!pathsStore._initialized) {
// TODO: watch folders to update paths?
// subscribe('files:folder:created', pathsStore.onCreatedNode)
// subscribe('files:folder:deleted', pathsStore.onDeletedNode)
// subscribe('files:folder:moved', pathsStore.onMovedNode)
pathsStore.initialized = true
pathsStore._initialized = true
}
return pathsStore

View file

@ -63,11 +63,11 @@ export const useUserConfigStore = () => {
const userConfigStore = store()
// Make sure we only register the listeners once
if (!userConfigStore.initialized) {
if (!userConfigStore._initialized) {
subscribe('files:config:updated', function({ key, value }: { key: string, value: boolean }) {
userConfigStore.onUpdate(key, value)
})
userConfigStore.initialized = true
userConfigStore._initialized = true
}
return userConfigStore

View file

@ -175,13 +175,13 @@ export default Vue.extend({
// Custom column must provide their own sorting methods
if (customColumn?.sort && typeof customColumn.sort === 'function') {
const results = [...(this.currentFolder?.children || []).map(this.getNode).filter(file => file)]
const results = [...(this.currentFolder?._children || []).map(this.getNode).filter(file => file)]
.sort(customColumn.sort)
return this.isAscSorting ? results : results.reverse()
}
return orderBy(
[...(this.currentFolder?.children || []).map(this.getNode).filter(file => file)],
[...(this.currentFolder?._children || []).map(this.getNode).filter(file => file)],
[
// Sort folders first if sorting by name
...this.sortingMode === 'basename' ? [v => v.type !== 'folder'] : [],
@ -272,7 +272,7 @@ export default Vue.extend({
this.filesStore.updateNodes(contents)
// Define current directory children
folder.children = contents.map(node => node.attributes.fileid)
folder._children = contents.map(node => node.attributes.fileid)
// If we're in the root dir, define the root
if (dir === '/') {

View file

@ -42,7 +42,7 @@ To prevent a user from running out of disk space, the Deleted files app will not
<collection>OCA\Files_Trashbin\Sabre\RootCollection</collection>
</collections>
<plugins>
<plugin>OCA\Files_Trashbin\Sabre\PropfindPlugin</plugin>
<plugin>OCA\Files_Trashbin\Sabre\TrashbinPlugin</plugin>
</plugins>
</sabre>

View file

@ -27,7 +27,6 @@ return array(
'OCA\\Files_Trashbin\\Sabre\\AbstractTrashFile' => $baseDir . '/../lib/Sabre/AbstractTrashFile.php',
'OCA\\Files_Trashbin\\Sabre\\AbstractTrashFolder' => $baseDir . '/../lib/Sabre/AbstractTrashFolder.php',
'OCA\\Files_Trashbin\\Sabre\\ITrash' => $baseDir . '/../lib/Sabre/ITrash.php',
'OCA\\Files_Trashbin\\Sabre\\PropfindPlugin' => $baseDir . '/../lib/Sabre/PropfindPlugin.php',
'OCA\\Files_Trashbin\\Sabre\\RestoreFolder' => $baseDir . '/../lib/Sabre/RestoreFolder.php',
'OCA\\Files_Trashbin\\Sabre\\RootCollection' => $baseDir . '/../lib/Sabre/RootCollection.php',
'OCA\\Files_Trashbin\\Sabre\\TrashFile' => $baseDir . '/../lib/Sabre/TrashFile.php',
@ -36,6 +35,7 @@ return array(
'OCA\\Files_Trashbin\\Sabre\\TrashFolderFolder' => $baseDir . '/../lib/Sabre/TrashFolderFolder.php',
'OCA\\Files_Trashbin\\Sabre\\TrashHome' => $baseDir . '/../lib/Sabre/TrashHome.php',
'OCA\\Files_Trashbin\\Sabre\\TrashRoot' => $baseDir . '/../lib/Sabre/TrashRoot.php',
'OCA\\Files_Trashbin\\Sabre\\TrashbinPlugin' => $baseDir . '/../lib/Sabre/TrashbinPlugin.php',
'OCA\\Files_Trashbin\\Storage' => $baseDir . '/../lib/Storage.php',
'OCA\\Files_Trashbin\\Trash\\BackendNotFoundException' => $baseDir . '/../lib/Trash/BackendNotFoundException.php',
'OCA\\Files_Trashbin\\Trash\\ITrashBackend' => $baseDir . '/../lib/Trash/ITrashBackend.php',

View file

@ -42,7 +42,6 @@ class ComposerStaticInitFiles_Trashbin
'OCA\\Files_Trashbin\\Sabre\\AbstractTrashFile' => __DIR__ . '/..' . '/../lib/Sabre/AbstractTrashFile.php',
'OCA\\Files_Trashbin\\Sabre\\AbstractTrashFolder' => __DIR__ . '/..' . '/../lib/Sabre/AbstractTrashFolder.php',
'OCA\\Files_Trashbin\\Sabre\\ITrash' => __DIR__ . '/..' . '/../lib/Sabre/ITrash.php',
'OCA\\Files_Trashbin\\Sabre\\PropfindPlugin' => __DIR__ . '/..' . '/../lib/Sabre/PropfindPlugin.php',
'OCA\\Files_Trashbin\\Sabre\\RestoreFolder' => __DIR__ . '/..' . '/../lib/Sabre/RestoreFolder.php',
'OCA\\Files_Trashbin\\Sabre\\RootCollection' => __DIR__ . '/..' . '/../lib/Sabre/RootCollection.php',
'OCA\\Files_Trashbin\\Sabre\\TrashFile' => __DIR__ . '/..' . '/../lib/Sabre/TrashFile.php',
@ -51,6 +50,7 @@ class ComposerStaticInitFiles_Trashbin
'OCA\\Files_Trashbin\\Sabre\\TrashFolderFolder' => __DIR__ . '/..' . '/../lib/Sabre/TrashFolderFolder.php',
'OCA\\Files_Trashbin\\Sabre\\TrashHome' => __DIR__ . '/..' . '/../lib/Sabre/TrashHome.php',
'OCA\\Files_Trashbin\\Sabre\\TrashRoot' => __DIR__ . '/..' . '/../lib/Sabre/TrashRoot.php',
'OCA\\Files_Trashbin\\Sabre\\TrashbinPlugin' => __DIR__ . '/..' . '/../lib/Sabre/TrashbinPlugin.php',
'OCA\\Files_Trashbin\\Storage' => __DIR__ . '/..' . '/../lib/Storage.php',
'OCA\\Files_Trashbin\\Trash\\BackendNotFoundException' => __DIR__ . '/..' . '/../lib/Trash/BackendNotFoundException.php',
'OCA\\Files_Trashbin\\Trash\\ITrashBackend' => __DIR__ . '/..' . '/../lib/Trash/ITrashBackend.php',

View file

@ -3,7 +3,7 @@
'name' => '__root__',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => 'd51429a47232bbf46a2be832ecfa711f102da802',
'reference' => '3c99b642938e7810ccd05813cda96643a8ee5da5',
'type' => 'library',
'install_path' => __DIR__ . '/../',
'aliases' => array(),
@ -13,7 +13,7 @@
'__root__' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => 'd51429a47232bbf46a2be832ecfa711f102da802',
'reference' => '3c99b642938e7810ccd05813cda96643a8ee5da5',
'type' => 'library',
'install_path' => __DIR__ . '/../',
'aliases' => array(),

View file

@ -27,14 +27,16 @@ declare(strict_types=1);
*/
namespace OCA\Files_Trashbin\Sabre;
use OCA\DAV\Connector\Sabre\FilesPlugin;
use OCP\IPreview;
use Sabre\DAV\INode;
use Sabre\DAV\PropFind;
use Sabre\DAV\Server;
use Sabre\DAV\PropFind;
use Sabre\DAV\ServerPlugin;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;
use OCA\DAV\Connector\Sabre\FilesPlugin;
class PropfindPlugin extends ServerPlugin {
class TrashbinPlugin extends ServerPlugin {
public const TRASHBIN_FILENAME = '{http://nextcloud.org/ns}trashbin-filename';
public const TRASHBIN_ORIGINAL_LOCATION = '{http://nextcloud.org/ns}trashbin-original-location';
public const TRASHBIN_DELETION_TIME = '{http://nextcloud.org/ns}trashbin-deletion-time';
@ -56,6 +58,7 @@ class PropfindPlugin extends ServerPlugin {
$this->server = $server;
$this->server->on('propFind', [$this, 'propFind']);
$this->server->on('afterMethod:GET', [$this,'httpGet']);
}
@ -110,4 +113,18 @@ class PropfindPlugin extends ServerPlugin {
return '';
});
}
/**
* Set real filename on trashbin download
*
* @param RequestInterface $request
* @param ResponseInterface $response
*/
public function httpGet(RequestInterface $request, ResponseInterface $response): void {
$path = $request->getPath();
$node = $this->server->tree->getNodeForPath($path);
if ($node instanceof ITrash) {
$response->addHeader('Content-Disposition', 'attachment; filename="' . $node->getFilename() . '"');
}
}
}

4
dist/core-common.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/files-main.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

19
package-lock.json generated
View file

@ -19,7 +19,7 @@
"@nextcloud/capabilities": "^1.0.4",
"@nextcloud/dialogs": "^4.0.0-beta.2",
"@nextcloud/event-bus": "^3.0.2",
"@nextcloud/files": "^3.0.0-beta.7",
"@nextcloud/files": "^3.0.0-beta.8",
"@nextcloud/initial-state": "^2.0.0",
"@nextcloud/l10n": "^2.1.0",
"@nextcloud/logger": "^2.5.0",
@ -4134,12 +4134,13 @@
}
},
"node_modules/@nextcloud/files": {
"version": "3.0.0-beta.7",
"license": "AGPL-3.0-or-later",
"version": "3.0.0-beta.8",
"resolved": "https://registry.npmjs.org/@nextcloud/files/-/files-3.0.0-beta.8.tgz",
"integrity": "sha512-qzL5lKhm913mBzu//rFnKobZ5GI8iAQ7GwVUez3Gpmso6TyEm/kdSloK2dRNWHStFotQdQRbl75KoyCyoz4cXg==",
"dependencies": {
"@nextcloud/auth": "^2.0.0",
"@nextcloud/l10n": "^2.0.1",
"@nextcloud/logger": "^2.1.0"
"@nextcloud/l10n": "^2.1.0",
"@nextcloud/logger": "^2.5.0"
},
"engines": {
"node": "^16.0.0",
@ -28346,11 +28347,13 @@
}
},
"@nextcloud/files": {
"version": "3.0.0-beta.7",
"version": "3.0.0-beta.8",
"resolved": "https://registry.npmjs.org/@nextcloud/files/-/files-3.0.0-beta.8.tgz",
"integrity": "sha512-qzL5lKhm913mBzu//rFnKobZ5GI8iAQ7GwVUez3Gpmso6TyEm/kdSloK2dRNWHStFotQdQRbl75KoyCyoz4cXg==",
"requires": {
"@nextcloud/auth": "^2.0.0",
"@nextcloud/l10n": "^2.0.1",
"@nextcloud/logger": "^2.1.0"
"@nextcloud/l10n": "^2.1.0",
"@nextcloud/logger": "^2.5.0"
}
},
"@nextcloud/initial-state": {

View file

@ -44,7 +44,7 @@
"@nextcloud/capabilities": "^1.0.4",
"@nextcloud/dialogs": "^4.0.0-beta.2",
"@nextcloud/event-bus": "^3.0.2",
"@nextcloud/files": "^3.0.0-beta.7",
"@nextcloud/files": "^3.0.0-beta.8",
"@nextcloud/initial-state": "^2.0.0",
"@nextcloud/l10n": "^2.1.0",
"@nextcloud/logger": "^2.5.0",