Skip to content

Commit

Permalink
Merge pull request #40475 from nextcloud/feat/f2v/systemtags
Browse files Browse the repository at this point in the history
  • Loading branch information
skjnldsv committed Sep 19, 2023
2 parents cd7c3f0 + ef1abd9 commit 085568b
Show file tree
Hide file tree
Showing 32 changed files with 218 additions and 874 deletions.
4 changes: 2 additions & 2 deletions apps/dav/lib/SystemTag/SystemTagsInUseCollection.php
Expand Up @@ -99,8 +99,8 @@ public function getChildren(): array {
$tag = new SystemTag((string)$tagData['id'], $tagData['name'], (bool)$tagData['visibility'], (bool)$tagData['editable']);
// read only, so we can submit the isAdmin parameter as false generally
$node = new SystemTagNode($tag, $user, false, $this->systemTagManager);
$node->setNumberOfFiles($tagData['number_files']);
$node->setReferenceFileId($tagData['ref_file_id']);
$node->setNumberOfFiles((int) $tagData['number_files']);
$node->setReferenceFileId((int) $tagData['ref_file_id']);
$children[] = $node;
}
return $children;
Expand Down
14 changes: 13 additions & 1 deletion apps/files/src/actions/downloadAction.ts
Expand Up @@ -47,7 +47,19 @@ export const action = new FileAction({
iconSvgInline: () => ArrowDownSvg,

enabled(nodes: Node[]) {
return nodes.length > 0 && nodes
if (nodes.length === 0) {
return false
}

// We can download direct dav files. But if we have
// some folders, we need to use the /apps/files/ajax/download.php
// endpoint, which only supports user root folder.
if (nodes.some(node => node.type === FileType.Folder)
&& nodes.some(node => !node.root?.startsWith('/files'))) {
return false
}

return nodes
.map(node => node.permissions)
.every(permission => (permission & Permission.READ) !== 0)
},
Expand Down
7 changes: 7 additions & 0 deletions apps/files/src/components/FileEntry.vue
Expand Up @@ -190,6 +190,7 @@ import AccountGroupIcon from 'vue-material-design-icons/AccountGroup.vue'
import FileIcon from 'vue-material-design-icons/File.vue'
import FolderIcon from 'vue-material-design-icons/Folder.vue'
import KeyIcon from 'vue-material-design-icons/Key.vue'
import TagIcon from 'vue-material-design-icons/Tag.vue'
import LinkIcon from 'vue-material-design-icons/Link.vue'
import NetworkIcon from 'vue-material-design-icons/Network.vue'
import AccountPlusIcon from 'vue-material-design-icons/AccountPlus.vue'
Expand Down Expand Up @@ -237,6 +238,7 @@ export default Vue.extend({
NcLoadingIcon,
NcTextField,
NetworkIcon,
TagIcon,
},
props: {
Expand Down Expand Up @@ -381,6 +383,11 @@ export default Vue.extend({
return KeyIcon
}
// System tags
if (this.source?.attributes?.['is-tag']) {
return TagIcon
}
// Link and mail shared folders
const shareTypes = Object.values(this.source?.attributes?.['share-types'] || {}).flat() as number[]
if (shareTypes.some(type => type === ShareType.SHARE_TYPE_LINK || type === ShareType.SHARE_TYPE_EMAIL)) {
Expand Down
29 changes: 1 addition & 28 deletions apps/files/src/services/Favorites.ts
Expand Up @@ -28,6 +28,7 @@ import { getCurrentUser } from '@nextcloud/auth'

import { getClient, rootPath } from './WebdavClient'
import { getDavNameSpaces, getDavProperties, getDefaultPropfind } from './DavProperties'
import { resultToNode } from './Files'

const client = getClient()

Expand All @@ -47,34 +48,6 @@ interface ResponseProps extends DAVResultResponseProps {
size: number,
}

const resultToNode = function(node: FileStat): File | Folder {
const props = node.props as ResponseProps
const permissions = davParsePermissions(props?.permissions)
const owner = getCurrentUser()?.uid as string

const nodeData = {
id: props?.fileid as number || 0,
source: generateRemoteUrl('dav' + rootPath + node.filename),
mtime: new Date(node.lastmod),
mime: node.mime as string,
size: props?.size as number || 0,
permissions,
owner,
root: rootPath,
attributes: {
...node,
...props,
hasPreview: props?.['has-preview'],
},
}

delete nodeData.attributes.props

return node.type === 'file'
? new File(nodeData)
: new Folder(nodeData)
}

export const getContents = async (path = '/'): Promise<ContentsWithRoot> => {
const propfindPayload = getDefaultPropfind()

Expand Down
2 changes: 1 addition & 1 deletion apps/files/src/services/Files.ts
Expand Up @@ -40,7 +40,7 @@ interface ResponseProps extends DAVResultResponseProps {
size: number,
}

const resultToNode = function(node: FileStat): File | Folder {
export const resultToNode = function(node: FileStat): File | Folder {
const props = node.props as ResponseProps
const permissions = davParsePermissions(props?.permissions)
const owner = getCurrentUser()?.uid as string
Expand Down
29 changes: 1 addition & 28 deletions apps/files/src/services/Recent.ts
Expand Up @@ -28,6 +28,7 @@ import { getCurrentUser } from '@nextcloud/auth'

import { getClient, rootPath } from './WebdavClient'
import { getDavNameSpaces, getDavProperties } from './DavProperties'
import { resultToNode } from './Files'

const client = getClient(generateRemoteUrl('dav'))

Expand Down Expand Up @@ -94,34 +95,6 @@ interface ResponseProps extends DAVResultResponseProps {
size: number,
}

const resultToNode = function(node: FileStat): File | Folder {
const props = node.props as ResponseProps
const permissions = davParsePermissions(props?.permissions)
const owner = getCurrentUser()?.uid as string

const nodeData = {
id: props?.fileid as number || 0,
source: generateRemoteUrl('dav' + node.filename),
mtime: new Date(node.lastmod),
mime: node.mime as string,
size: props?.size as number || 0,
permissions,
owner,
root: rootPath,
attributes: {
...node,
...props,
hasPreview: props?.['has-preview'],
},
}

delete nodeData.attributes.props

return node.type === 'file'
? new File(nodeData)
: new Folder(nodeData)
}

export const getContents = async (path = '/'): Promise<ContentsWithRoot> => {
const contentsResponse = await client.getDirectoryContents(path, {
details: true,
Expand Down
9 changes: 9 additions & 0 deletions apps/files/src/views/FilesList.vue
Expand Up @@ -328,12 +328,21 @@ export default Vue.extend({
},
},
mounted() {
this.fetchContent()
},
methods: {
async fetchContent() {
this.loading = true
const dir = this.dir
const currentView = this.currentView
if (!currentView) {
logger.debug('The current view doesn\'t exists or is not ready.', { currentView })
return
}
// If we have a cancellable promise ongoing, cancel it
if (typeof this.promise?.cancel === 'function') {
this.promise.cancel()
Expand Down
3 changes: 2 additions & 1 deletion apps/files/src/views/Sidebar.vue
Expand Up @@ -91,6 +91,7 @@
import { emit } from '@nextcloud/event-bus'
import { encodePath } from '@nextcloud/paths'
import { File, Folder } from '@nextcloud/files'
import { getCapabilities } from '@nextcloud/capabilities'
import { getCurrentUser } from '@nextcloud/auth'
import { Type as ShareTypes } from '@nextcloud/sharing'
import $ from 'jquery'
Expand Down Expand Up @@ -299,7 +300,7 @@ export default {
},
isSystemTagsEnabled() {
return OCA && 'SystemTags' in OCA
return getCapabilities()?.systemtags?.enabled === true
},
},
created() {
Expand Down
1 change: 1 addition & 0 deletions apps/systemtags/composer/composer/autoload_classmap.php
Expand Up @@ -11,6 +11,7 @@
'OCA\\SystemTags\\Activity\\Provider' => $baseDir . '/../lib/Activity/Provider.php',
'OCA\\SystemTags\\Activity\\Setting' => $baseDir . '/../lib/Activity/Setting.php',
'OCA\\SystemTags\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php',
'OCA\\SystemTags\\Capabilities' => $baseDir . '/../lib/Capabilities.php',
'OCA\\SystemTags\\Controller\\LastUsedController' => $baseDir . '/../lib/Controller/LastUsedController.php',
'OCA\\SystemTags\\Search\\TagSearchProvider' => $baseDir . '/../lib/Search/TagSearchProvider.php',
'OCA\\SystemTags\\Settings\\Admin' => $baseDir . '/../lib/Settings/Admin.php',
Expand Down
1 change: 1 addition & 0 deletions apps/systemtags/composer/composer/autoload_static.php
Expand Up @@ -26,6 +26,7 @@ class ComposerStaticInitSystemTags
'OCA\\SystemTags\\Activity\\Provider' => __DIR__ . '/..' . '/../lib/Activity/Provider.php',
'OCA\\SystemTags\\Activity\\Setting' => __DIR__ . '/..' . '/../lib/Activity/Setting.php',
'OCA\\SystemTags\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php',
'OCA\\SystemTags\\Capabilities' => __DIR__ . '/..' . '/../lib/Capabilities.php',
'OCA\\SystemTags\\Controller\\LastUsedController' => __DIR__ . '/..' . '/../lib/Controller/LastUsedController.php',
'OCA\\SystemTags\\Search\\TagSearchProvider' => __DIR__ . '/..' . '/../lib/Search/TagSearchProvider.php',
'OCA\\SystemTags\\Settings\\Admin' => __DIR__ . '/..' . '/../lib/Settings/Admin.php',
Expand Down
15 changes: 3 additions & 12 deletions apps/systemtags/lib/AppInfo/Application.php
Expand Up @@ -28,6 +28,7 @@
use OCA\Files\Event\LoadAdditionalScriptsEvent;
use OCA\SystemTags\Search\TagSearchProvider;
use OCA\SystemTags\Activity\Listener;
use OCA\SystemTags\Capabilities;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
Expand All @@ -45,6 +46,7 @@ public function __construct() {

public function register(IRegistrationContext $context): void {
$context->registerSearchProvider(TagSearchProvider::class);
$context->registerCapability(Capabilities::class);
}

public function boot(IBootContext $context): void {
Expand All @@ -56,7 +58,7 @@ public function boot(IBootContext $context): void {
LoadAdditionalScriptsEvent::class,
function () {
\OCP\Util::addScript('core', 'systemtags');
\OCP\Util::addScript(self::APP_ID, 'systemtags');
\OCP\Util::addInitScript(self::APP_ID, 'init');
}
);

Expand All @@ -77,16 +79,5 @@ function () {
$dispatcher->addListener(MapperEvent::EVENT_ASSIGN, $mapperListener);
$dispatcher->addListener(MapperEvent::EVENT_UNASSIGN, $mapperListener);
});

\OCA\Files\App::getNavigationManager()->add(function () {
$l = \OC::$server->getL10N(self::APP_ID);
return [
'id' => 'systemtagsfilter',
'appname' => self::APP_ID,
'script' => 'list.php',
'order' => 25,
'name' => $l->t('Tags'),
];
});
}
}
40 changes: 40 additions & 0 deletions apps/systemtags/lib/Capabilities.php
@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);

/**
* @copyright Copyright (c) 2023 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.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/>
*
*/
namespace OCA\SystemTags;

use OCP\Capabilities\ICapability;

class Capabilities implements ICapability {
/**
* @return array{systemtags: array{enabled: true}}
*/
public function getCapabilities() {
$capabilities = [
'systemtags' => [
'enabled' => true,
]
];
return $capabilities;
}
}

0 comments on commit 085568b

Please sign in to comment.