mirror of
https://github.com/nextcloud/server.git
synced 2026-04-15 22:11:17 -04:00
fix(files): add mount root property and adjust delete wording
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
This commit is contained in:
parent
414a4caa65
commit
6cf4fe19a0
5 changed files with 167 additions and 24 deletions
|
|
@ -79,6 +79,7 @@ class FilesPlugin extends ServerPlugin {
|
|||
public const DATA_FINGERPRINT_PROPERTYNAME = '{http://owncloud.org/ns}data-fingerprint';
|
||||
public const HAS_PREVIEW_PROPERTYNAME = '{http://nextcloud.org/ns}has-preview';
|
||||
public const MOUNT_TYPE_PROPERTYNAME = '{http://nextcloud.org/ns}mount-type';
|
||||
public const MOUNT_ROOT_PROPERTYNAME = '{http://nextcloud.org/ns}is-mount-root';
|
||||
public const IS_ENCRYPTED_PROPERTYNAME = '{http://nextcloud.org/ns}is-encrypted';
|
||||
public const METADATA_ETAG_PROPERTYNAME = '{http://nextcloud.org/ns}metadata_etag';
|
||||
public const UPLOAD_TIME_PROPERTYNAME = '{http://nextcloud.org/ns}upload_time';
|
||||
|
|
@ -361,6 +362,16 @@ class FilesPlugin extends ServerPlugin {
|
|||
return $node->getFileInfo()->getMountPoint()->getMountType();
|
||||
});
|
||||
|
||||
/**
|
||||
* This is a special property which is used to determine if a node
|
||||
* is a mount root or not, e.g. a shared folder.
|
||||
* If so, then the node can only be unshared and not deleted.
|
||||
* @see https://github.com/nextcloud/server/blob/cc75294eb6b16b916a342e69998935f89222619d/lib/private/Files/View.php#L696-L698
|
||||
*/
|
||||
$propFind->handle(self::MOUNT_ROOT_PROPERTYNAME, function () use ($node) {
|
||||
return $node->getNode()->getInternalPath() === '' ? 'true' : 'false';
|
||||
});
|
||||
|
||||
$propFind->handle(self::SHARE_NOTE, function () use ($node, $httpRequest): ?string {
|
||||
$user = $this->userSession->getUser();
|
||||
if ($user === null) {
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@
|
|||
import { action } from './deleteAction'
|
||||
import { expect } from '@jest/globals'
|
||||
import { File, Folder, Permission, View, FileAction } from '@nextcloud/files'
|
||||
import * as auth from '@nextcloud/auth'
|
||||
import * as eventBus from '@nextcloud/event-bus'
|
||||
import axios from '@nextcloud/axios'
|
||||
|
||||
import logger from '../logger'
|
||||
|
||||
const view = {
|
||||
|
|
@ -50,36 +50,81 @@ describe('Delete action conditions tests', () => {
|
|||
permissions: Permission.ALL,
|
||||
})
|
||||
|
||||
// const file2 = new File({
|
||||
// id: 1,
|
||||
// source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt',
|
||||
// owner: 'admin',
|
||||
// mime: 'text/plain',
|
||||
// permissions: Permission.ALL,
|
||||
// })
|
||||
const file2 = new File({
|
||||
id: 1,
|
||||
source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt',
|
||||
owner: 'admin',
|
||||
mime: 'text/plain',
|
||||
permissions: Permission.ALL,
|
||||
attributes: {
|
||||
'is-mount-root': true,
|
||||
'mount-type': 'shared',
|
||||
},
|
||||
})
|
||||
|
||||
const folder = new Folder({
|
||||
id: 1,
|
||||
source: 'https://cloud.domain.com/remote.php/dav/files/admin/Foo',
|
||||
owner: 'admin',
|
||||
mime: 'text/plain',
|
||||
permissions: Permission.ALL,
|
||||
})
|
||||
|
||||
const folder2 = new Folder({
|
||||
id: 1,
|
||||
source: 'https://cloud.domain.com/remote.php/dav/files/admin/Foo',
|
||||
owner: 'admin',
|
||||
mime: 'text/plain',
|
||||
permissions: Permission.ALL,
|
||||
attributes: {
|
||||
'is-mount-root': true,
|
||||
'mount-type': 'shared',
|
||||
},
|
||||
})
|
||||
|
||||
const folder3 = new Folder({
|
||||
id: 1,
|
||||
source: 'https://cloud.domain.com/remote.php/dav/files/admin/Foo',
|
||||
owner: 'admin',
|
||||
mime: 'text/plain',
|
||||
permissions: Permission.ALL,
|
||||
attributes: {
|
||||
'is-mount-root': true,
|
||||
'mount-type': 'external',
|
||||
},
|
||||
})
|
||||
|
||||
test('Default values', () => {
|
||||
expect(action).toBeInstanceOf(FileAction)
|
||||
expect(action.id).toBe('delete')
|
||||
expect(action.displayName([file], view)).toBe('Delete')
|
||||
expect(action.displayName([file], view)).toBe('Delete file')
|
||||
expect(action.iconSvgInline([], view)).toBe('<svg>SvgMock</svg>')
|
||||
expect(action.default).toBeUndefined()
|
||||
expect(action.order).toBe(100)
|
||||
})
|
||||
|
||||
test('Default trashbin view values', () => {
|
||||
test('Default folder displayName', () => {
|
||||
expect(action.displayName([folder], view)).toBe('Delete folder')
|
||||
})
|
||||
|
||||
test('Default trashbin view displayName', () => {
|
||||
expect(action.displayName([file], trashbinView)).toBe('Delete permanently')
|
||||
})
|
||||
|
||||
// TODO: Fix this test
|
||||
// test('Shared node values', () => {
|
||||
// jest.spyOn(auth, 'getCurrentUser').mockReturnValue(null)
|
||||
// expect(action.displayName([file2], view)).toBe('Unshare')
|
||||
// })
|
||||
test('Shared root node displayName', () => {
|
||||
expect(action.displayName([file2], view)).toBe('Leave this share')
|
||||
expect(action.displayName([folder2], view)).toBe('Leave this share')
|
||||
expect(action.displayName([file2, folder2], view)).toBe('Leave these shares')
|
||||
})
|
||||
|
||||
// test('Shared and owned nodes values', () => {
|
||||
// expect(action.displayName([file, file2], view)).toBe('Delete and unshare')
|
||||
// })
|
||||
test('External storage root node displayName', () => {
|
||||
expect(action.displayName([folder3], view)).toBe('Disconnect storage')
|
||||
expect(action.displayName([folder3, folder3], view)).toBe('Disconnect storages')
|
||||
})
|
||||
|
||||
test('Shared and owned nodes displayName', () => {
|
||||
expect(action.displayName([file, file2], view)).toBe('Delete and unshare')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Delete action enabled tests', () => {
|
||||
|
|
|
|||
|
|
@ -20,21 +20,102 @@
|
|||
*
|
||||
*/
|
||||
import { emit } from '@nextcloud/event-bus'
|
||||
import { Permission, Node, View, FileAction } from '@nextcloud/files'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
import { Permission, Node, View, FileAction, FileType } from '@nextcloud/files'
|
||||
import { translate as t, translatePlural as n } from '@nextcloud/l10n'
|
||||
import axios from '@nextcloud/axios'
|
||||
|
||||
import CloseSvg from '@mdi/svg/svg/close.svg?raw'
|
||||
import NetworkOffSvg from '@mdi/svg/svg/network-off.svg?raw'
|
||||
import TrashCanSvg from '@mdi/svg/svg/trash-can.svg?raw'
|
||||
|
||||
import logger from '../logger.js'
|
||||
|
||||
const canUnshareOnly = (nodes: Node[]) => {
|
||||
return nodes.every(node => node.attributes['is-mount-root'] === true
|
||||
&& node.attributes['mount-type'] === 'shared')
|
||||
}
|
||||
|
||||
const canDisconnectOnly = (nodes: Node[]) => {
|
||||
return nodes.every(node => node.attributes['is-mount-root'] === true
|
||||
&& node.attributes['mount-type'] === 'external')
|
||||
}
|
||||
|
||||
const isMixedUnshareAndDelete = (nodes: Node[]) => {
|
||||
if (nodes.length === 1) {
|
||||
return false
|
||||
}
|
||||
|
||||
const hasSharedItems = nodes.some(node => canUnshareOnly([node]))
|
||||
const hasDeleteItems = nodes.some(node => !canUnshareOnly([node]))
|
||||
return hasSharedItems && hasDeleteItems
|
||||
}
|
||||
|
||||
const isAllFiles = (nodes: Node[]) => {
|
||||
return !nodes.some(node => node.type !== FileType.File)
|
||||
}
|
||||
|
||||
const isAllFolders = (nodes: Node[]) => {
|
||||
return !nodes.some(node => node.type !== FileType.Folder)
|
||||
}
|
||||
|
||||
export const action = new FileAction({
|
||||
id: 'delete',
|
||||
displayName(nodes: Node[], view: View) {
|
||||
return view.id === 'trashbin'
|
||||
? t('files', 'Delete permanently')
|
||||
: t('files', 'Delete')
|
||||
/**
|
||||
* If we're in the trashbin, we can only delete permanently
|
||||
*/
|
||||
if (view.id === 'trashbin') {
|
||||
return t('files', 'Delete permanently')
|
||||
}
|
||||
|
||||
/**
|
||||
* If we're in the sharing view, we can only unshare
|
||||
*/
|
||||
if (isMixedUnshareAndDelete(nodes)) {
|
||||
return t('files', 'Delete and unshare')
|
||||
}
|
||||
|
||||
/**
|
||||
* If those nodes are all the root node of a
|
||||
* share, we can only unshare them.
|
||||
*/
|
||||
if (canUnshareOnly(nodes)) {
|
||||
return n('files', 'Leave this share', 'Leave these shares', nodes.length)
|
||||
}
|
||||
|
||||
/**
|
||||
* If those nodes are all the root node of an
|
||||
* external storage, we can only disconnect it.
|
||||
*/
|
||||
if (canDisconnectOnly(nodes)) {
|
||||
return n('files', 'Disconnect storage', 'Disconnect storages', nodes.length)
|
||||
}
|
||||
|
||||
/**
|
||||
* If we're only selecting files, use proper wording
|
||||
*/
|
||||
if (isAllFiles(nodes)) {
|
||||
return n('files', 'Delete file', 'Delete files', nodes.length)
|
||||
}
|
||||
|
||||
/**
|
||||
* If we're only selecting folders, use proper wording
|
||||
*/
|
||||
if (isAllFolders(nodes)) {
|
||||
return n('files', 'Delete folder', 'Delete folders', nodes.length)
|
||||
}
|
||||
|
||||
return t('files', 'Delete')
|
||||
},
|
||||
iconSvgInline: () => {
|
||||
iconSvgInline: (nodes: Node[]) => {
|
||||
if (canUnshareOnly(nodes)) {
|
||||
return CloseSvg
|
||||
}
|
||||
|
||||
if (canDisconnectOnly(nodes)) {
|
||||
return NetworkOffSvg
|
||||
}
|
||||
|
||||
return TrashCanSvg
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -66,5 +66,6 @@ registerRecentView()
|
|||
registerPreviewServiceWorker()
|
||||
|
||||
registerDavProperty('nc:hidden', { nc: 'http://nextcloud.org/ns' })
|
||||
registerDavProperty('nc:is-mount-root', { nc: 'http://nextcloud.org/ns' })
|
||||
|
||||
initLivePhotos()
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
import { registerDavProperty } from '@nextcloud/files'
|
||||
import registerSharingViews from './views/shares'
|
||||
|
||||
import './actions/acceptShareAction'
|
||||
|
|
@ -29,3 +30,7 @@ import './actions/restoreShareAction'
|
|||
import './actions/sharingStatusAction'
|
||||
|
||||
registerSharingViews()
|
||||
|
||||
registerDavProperty('nc:share-attributes', { nc: 'http://nextcloud.org/ns' })
|
||||
registerDavProperty('oc:share-types', { oc: 'http://owncloud.org/ns' })
|
||||
registerDavProperty('ocs:share-permissions', { ocs: 'http://open-collaboration-services.org/ns' })
|
||||
|
|
|
|||
Loading…
Reference in a new issue