Skip to content

Commit

Permalink
feat(files): commands to manage system-tags on files
Browse files Browse the repository at this point in the history
Resolves #32735 by adding new occ commands to manage system-tags for files

Signed-off-by: schaarsc <schaarsc@users.noreply.github.com>
  • Loading branch information
schaarsc committed Sep 23, 2024
1 parent 46301a1 commit 34cde37
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 0 deletions.
3 changes: 3 additions & 0 deletions apps/files/appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
<command>OCA\Files\Command\Object\Delete</command>
<command>OCA\Files\Command\Object\Get</command>
<command>OCA\Files\Command\Object\Put</command>
<command>OCA\Files\Command\TagAdd</command>
<command>OCA\Files\Command\TagDelete</command>
<command>OCA\Files\Command\TagDeleteAll</command>
</commands>

<settings>
Expand Down
3 changes: 3 additions & 0 deletions apps/files/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
'OCA\\Files\\Command\\Scan' => $baseDir . '/../lib/Command/Scan.php',
'OCA\\Files\\Command\\ScanAppData' => $baseDir . '/../lib/Command/ScanAppData.php',
'OCA\\Files\\Command\\TransferOwnership' => $baseDir . '/../lib/Command/TransferOwnership.php',
'OCA\\Files\\Command\\TagAdd' => $baseDir . '/../lib/Command/TagAdd.php',
'OCA\\Files\\Command\\TagDelete' => $baseDir . '/../lib/Command/TagDelete.php',
'OCA\\Files\\Command\\TagDeleteAll' => $baseDir . '/../lib/Command/TagDeleteAll.php',
'OCA\\Files\\Controller\\ApiController' => $baseDir . '/../lib/Controller/ApiController.php',
'OCA\\Files\\Controller\\DirectEditingController' => $baseDir . '/../lib/Controller/DirectEditingController.php',
'OCA\\Files\\Controller\\DirectEditingViewController' => $baseDir . '/../lib/Controller/DirectEditingViewController.php',
Expand Down
3 changes: 3 additions & 0 deletions apps/files/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ class ComposerStaticInitFiles
'OCA\\Files\\Command\\Scan' => __DIR__ . '/..' . '/../lib/Command/Scan.php',
'OCA\\Files\\Command\\ScanAppData' => __DIR__ . '/..' . '/../lib/Command/ScanAppData.php',
'OCA\\Files\\Command\\TransferOwnership' => __DIR__ . '/..' . '/../lib/Command/TransferOwnership.php',
'OCA\\Files\\Command\\TagAdd' => __DIR__ . '/..' . '/../lib/Command/TagAdd.php',
'OCA\\Files\\Command\\TagDelete' => __DIR__ . '/..' . '/../lib/Command/TagDelete.php',
'OCA\\Files\\Command\\TagDeleteAll' => __DIR__ . '/..' . '/../lib/Command/TagDeleteAll.php',
'OCA\\Files\\Controller\\ApiController' => __DIR__ . '/..' . '/../lib/Controller/ApiController.php',
'OCA\\Files\\Controller\\DirectEditingController' => __DIR__ . '/..' . '/../lib/Controller/DirectEditingController.php',
'OCA\\Files\\Controller\\DirectEditingViewController' => __DIR__ . '/..' . '/../lib/Controller/DirectEditingViewController.php',
Expand Down
87 changes: 87 additions & 0 deletions apps/files/lib/Command/TagAdd.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php
declare(strict_types = 1);
/**
* SPDX-FileCopyrightText: Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Files\Command;

use OCP\SystemTag\ISystemTagManager;
use OCP\SystemTag\ISystemTagObjectMapper;
use OCP\SystemTag\TagAlreadyExistsException;
use OC\Core\Command\Info\FileUtils;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class TagAdd extends Command
{

public function __construct(private FileUtils $fileUtils, private ISystemTagManager $systemTagManager, private ISystemTagObjectMapper $systemTagObjectMapper)
{
parent::__construct();
}

protected function configure(): void
{
$this->setName('files:tag-add')
->setDescription('Add a system-tag to a file or folder')
->addArgument('target', InputArgument::REQUIRED, "file id or path")
->addArgument('tags', InputArgument::REQUIRED, "Name of the tag(s) to add, comma separated")
->addArgument('access', InputArgument::REQUIRED, 'access level of the tag (public, restricted or invisible)');
}

public function execute(InputInterface $input, OutputInterface $output): int
{
$targetInput = $input->getArgument('target');
$tagsInput = $input->getArgument('tags');

if ($tagsInput === '') {
$output->writeln('<error>`tags` can\'t be empty</error>');
return 3;
}

$tagNameArray = explode(',', $tagsInput);

$access = $input->getArgument('access');
switch ($access) {
case 'public':
$userVisible = true;
$userAssignable = true;
break;
case 'restricted':
$userVisible = true;
$userAssignable = false;
break;
case 'invisible':
$userVisible = false;
$userAssignable = false;
break;
default:
$output->writeln('<error>`access` property is invalid</error>');
return 1;
}

$targetNode = $this->fileUtils->getNode($targetInput);

if (! $targetNode) {
$output->writeln("<error>file $targetInput not found</error>");
return 1;
}

foreach ($tagNameArray as $tagName) {
try {
$tag = $this->systemTagManager->createTag($tagName, $userVisible, $userAssignable);
$output->writeln("<info>$access</info> tag named <info>$tagName</info> created.");
} catch (TagAlreadyExistsException $e) {
$tag = $this->systemTagManager->getTag($tagName, $userVisible, $userAssignable);
}

$this->systemTagObjectMapper->assignTags((string) $targetNode->getId(), 'files', $tag->getId());
$output->writeln("<info>$access</info> tag named <info>$tagName</info> added.");
}

return 0;
}
}
85 changes: 85 additions & 0 deletions apps/files/lib/Command/TagDelete.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php
declare(strict_types = 1);
/**
* SPDX-FileCopyrightText: Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Files\Command;

use OCP\SystemTag\ISystemTagManager;
use OCP\SystemTag\ISystemTagObjectMapper;
use OCP\SystemTag\TagNotFoundException;
use OC\Core\Command\Info\FileUtils;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class TagDelete extends Command
{

public function __construct(private FileUtils $fileUtils, private ISystemTagManager $systemTagManager, private ISystemTagObjectMapper $systemTagObjectMapper)
{
parent::__construct();
}

protected function configure(): void
{
$this->setName('files:tag-delete')
->setDescription('Delete a system-tag from a file or folder')
->addArgument('target', InputArgument::REQUIRED, "file id or path")
->addArgument('tags', InputArgument::REQUIRED, "Name of the tag(s) to delete, comma separated")
->addArgument('access', InputArgument::REQUIRED, 'access level of the tag (public, restricted or invisible)');
}

public function execute(InputInterface $input, OutputInterface $output): int
{
$targetInput = $input->getArgument('target');
$tagsInput = $input->getArgument('tags');

if ($tagsInput === '') {
$output->writeln('<error>`tags` can\'t be empty</error>');
return 3;
}

$tagNameArray = explode(',', $tagsInput);

$access = $input->getArgument('access');
switch ($access) {
case 'public':
$userVisible = true;
$userAssignable = true;
break;
case 'restricted':
$userVisible = true;
$userAssignable = false;
break;
case 'invisible':
$userVisible = false;
$userAssignable = false;
break;
default:
$output->writeln('<error>`access` property is invalid</error>');
return 1;
}

$targetNode = $this->fileUtils->getNode($targetInput);

if (! $targetNode) {
$output->writeln("<error>file $targetInput not found</error>");
return 1;
}

foreach ($tagNameArray as $tagName) {
try {
$tag = $this->systemTagManager->getTag($tagName, $userVisible, $userAssignable);
$this->systemTagObjectMapper->unassignTags((string) $targetNode->getId(), 'files', $tag->getId());
$output->writeln("<info>$access</info> tag named <info>$tagName</info> removed.");
} catch (TagNotFoundException $e) {
$output->writeln("<info>$access</info> tag named <info>$tagName</info> does not exist!");
}
}

return 0;
}
}
47 changes: 47 additions & 0 deletions apps/files/lib/Command/TagDeleteAll.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php
declare(strict_types = 1);
/**
* SPDX-FileCopyrightText: Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Files\Command;

use OCP\SystemTag\ISystemTagObjectMapper;
use OC\Core\Command\Info\FileUtils;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class TagDeleteAll extends Command
{

public function __construct(private FileUtils $fileUtils, private ISystemTagObjectMapper $systemTagObjectMapper)
{
parent::__construct();
}

protected function configure(): void
{
$this->setName('files:tag-delete-all')
->setDescription('Delete all system-tags from a file or folder')
->addArgument('target', InputArgument::REQUIRED, "file id or path");
}

public function execute(InputInterface $input, OutputInterface $output): int
{
$targetInput = $input->getArgument('target');
$targetNode = $this->fileUtils->getNode($targetInput);

if (! $targetNode) {
$output->writeln("<error>file $targetInput not found</error>");
return 1;
}

$tags = $this->systemTagObjectMapper->getTagIdsForObjects($targetNode->getId(), 'files');

Check failure on line 41 in apps/files/lib/Command/TagDeleteAll.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

InvalidArgument

apps/files/lib/Command/TagDeleteAll.php:41:61: InvalidArgument: Argument 1 of OCP\SystemTag\ISystemTagObjectMapper::getTagIdsForObjects expects array<array-key, mixed>|string, but int provided (see https://psalm.dev/004)

Check failure

Code scanning / Psalm

InvalidArgument Error

Argument 1 of OCP\SystemTag\ISystemTagObjectMapper::getTagIdsForObjects expects array<array-key, mixed>|string, but int provided
$this->systemTagObjectMapper->unassignTags((string) $targetNode->getId(), 'files', $tags[$targetNode->getId()]);
$output->writeln("<info>all tags removed.</info>");

return 0;
}
}

0 comments on commit 34cde37

Please sign in to comment.