mirror of
https://github.com/nextcloud/server.git
synced 2026-04-26 08:38:11 -04:00
Merge pull request #37633 from nextcloud/fix/trashbin
Trashbin followup fixes
This commit is contained in:
commit
c6645cbc46
18 changed files with 111 additions and 46 deletions
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 === '/') {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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
4
dist/core-common.js
vendored
File diff suppressed because one or more lines are too long
2
dist/core-common.js.map
vendored
2
dist/core-common.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/files-main.js
vendored
4
dist/files-main.js
vendored
File diff suppressed because one or more lines are too long
2
dist/files-main.js.map
vendored
2
dist/files-main.js.map
vendored
File diff suppressed because one or more lines are too long
19
package-lock.json
generated
19
package-lock.json
generated
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in a new issue