diff --git a/core/Command/SystemTag/Add.php b/core/Command/SystemTag/Add.php new file mode 100644 index 0000000000..e16c0da126 --- /dev/null +++ b/core/Command/SystemTag/Add.php @@ -0,0 +1,101 @@ + + * + * @author Johannes Leuker + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace OC\Core\Command\SystemTag; + +use OC\Core\Command\Base; +use OCP\SystemTag\ISystemTag; +use OCP\SystemTag\ISystemTagManager; +use OCP\SystemTag\TagAlreadyExistsException; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class Add extends Base { + + /** @var ISystemTagManager */ + protected $systemTagManager; + + public function __construct(ISystemTagManager $systemTagManager) { + $this->systemTagManager = $systemTagManager; + parent::__construct(); + } + + protected function configure() { + $this + ->setName('tag:add') + ->setDescription('Add new tag') + ->addArgument( + 'name', + InputArgument::REQUIRED, + 'name of the tag', + ) + ->addArgument( + 'access', + InputArgument::REQUIRED, + 'access level of the tag (public, restricted or invisible)', + ); + parent::configure(); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + $name = $input->getArgument('name'); + if ($name === '') { + $output->writeln('`name` can\'t be empty'); + return 3; + } + + switch ($input->getArgument('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('`access` property is invalid'); + return 1; + } + + try { + $tag = $this->systemTagManager->createTag($name, $userVisible, $userAssignable); + + $this->writeArrayInOutputFormat($input, $output, + [ + 'id' => $tag->getId(), + 'name' => $tag->getName(), + 'access' => ISystemTag::ACCESS_LEVEL_LOOKUP[$tag->getAccessLevel()], + ]); + return 0; + } catch (TagAlreadyExistsException $e) { + $output->writeln(''.$e->getMessage().''); + return 2; + } + } +} diff --git a/core/Command/SystemTag/Delete.php b/core/Command/SystemTag/Delete.php new file mode 100644 index 0000000000..cc06b7d513 --- /dev/null +++ b/core/Command/SystemTag/Delete.php @@ -0,0 +1,64 @@ + + * + * @author Johannes Leuker + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace OC\Core\Command\SystemTag; + +use OC\Core\Command\Base; +use OCP\SystemTag\ISystemTagManager; +use OCP\SystemTag\TagNotFoundException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class Delete extends Base { + + /** @var ISystemTagManager */ + protected $systemTagManager; + + public function __construct(ISystemTagManager $systemTagManager) { + $this->systemTagManager = $systemTagManager; + parent::__construct(); + } + + protected function configure() { + $this + ->setName('tag:delete') + ->setDescription('delete a tag') + ->addArgument( + 'id', + InputOption::VALUE_REQUIRED, + 'The ID of the tag that should be deleted', + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + try { + $this->systemTagManager->deleteTags($input->getArgument('id')); + $output->writeln('The specified tag was deleted'); + return 0; + } catch (TagNotFoundException $e) { + $output->writeln('Tag not found'); + return 1; + } + } +} diff --git a/core/Command/SystemTag/Edit.php b/core/Command/SystemTag/Edit.php new file mode 100644 index 0000000000..8f58d08e2c --- /dev/null +++ b/core/Command/SystemTag/Edit.php @@ -0,0 +1,119 @@ + + * + * @author Johannes Leuker + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace OC\Core\Command\SystemTag; + +use OC\Core\Command\Base; +use OCP\SystemTag\ISystemTagManager; +use OCP\SystemTag\TagAlreadyExistsException; +use OCP\SystemTag\TagNotFoundException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class Edit extends Base { + + /** @var ISystemTagManager */ + protected $systemTagManager; + + /** + * @param ISystemTagManager $systemTagManager + */ + public function __construct(ISystemTagManager $systemTagManager) { + $this->systemTagManager = $systemTagManager; + parent::__construct(); + } + + protected function configure() { + $this + ->setName('tag:edit') + ->setDescription('edit tag attributes') + ->addArgument( + 'id', + InputOption::VALUE_REQUIRED, + 'The ID of the tag that should be deleted', + ) + ->addOption( + 'name', + null, + InputOption::VALUE_OPTIONAL, + 'sets the \'name\' parameter', + ) + ->addOption( + 'access', + null, + InputOption::VALUE_OPTIONAL, + 'sets the access control level (public, restricted, invisible)', + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + $tagArray = $this->systemTagManager->getTagsByIds($input->getArgument('id')); + // returns an array, but we always expect 0 or 1 results + + if (!$tagArray) { + $output->writeln('Tag not found'); + return 3; + } + + $tag = array_values($tagArray)[0]; + $name = $tag->getName(); + if (!empty($input->getOption('name'))) { + $name = $input->getOption('name'); + } + + $userVisible = $tag->isUserVisible(); + $userAssignable = $tag->isUserAssignable(); + if ($input->getOption('access')) { + switch ($input->getOption('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('`access` property is invalid'); + return 1; + } + } + + try { + $this->systemTagManager->updateTag($input->getArgument('id'), $name, $userVisible, $userAssignable); + $output->writeln('Tag updated ("' . $name . '", '. $userVisible . ', ' . $userAssignable . ')'); + return 0; + } catch (TagNotFoundException $e) { + $output->writeln('Tag not found'); + return 1; + } catch (TagAlreadyExistsException $e) { + $output->writeln(''.$e->getMessage().''); + return 2; + } + } +} diff --git a/core/Command/SystemTag/ListCommand.php b/core/Command/SystemTag/ListCommand.php new file mode 100644 index 0000000000..72ef1467f4 --- /dev/null +++ b/core/Command/SystemTag/ListCommand.php @@ -0,0 +1,85 @@ + + * + * @author Johannes Leuker + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace OC\Core\Command\SystemTag; + +use OC\Core\Command\Base; +use OCP\SystemTag\ISystemTagManager; +use OCP\SystemTag\ISystemTag; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class ListCommand extends Base { + + /** @var ISystemTagManager */ + protected $systemTagManager; + + public function __construct(ISystemTagManager $systemTagManager) { + $this->systemTagManager = $systemTagManager; + parent::__construct(); + } + + protected function configure() { + $this + ->setName('tag:list') + ->setDescription('list tags') + ->addOption( + 'visibilityFilter', + null, + InputOption::VALUE_OPTIONAL, + 'filter by visibility (1,0)' + ) + ->addOption( + 'nameSearchPattern', + null, + InputOption::VALUE_OPTIONAL, + 'optional search pattern for the tag name (infix)' + ); + parent::configure(); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + $tags = $this->systemTagManager->getAllTags( + $input->getOption('visibilityFilter'), + $input->getOption('nameSearchPattern') + ); + + $this->writeArrayInOutputFormat($input, $output, $this->formatTags($tags)); + return 0; + } + + /** + * @param ISystemtag[] $tags + * @return array + */ + private function formatTags(array $tags) { + foreach ($tags as $tag) { + $result[$tag->getId()] = [ + 'name' => $tag->getName(), + 'access' => ISystemTag::ACCESS_LEVEL_LOOKUP[$tag->getAccessLevel()], + ]; + } + return $result; + } +} diff --git a/core/register_command.php b/core/register_command.php index c9e8605219..8242e0d497 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -193,6 +193,11 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) { $application->add(new OC\Core\Command\Group\RemoveUser(\OC::$server->getUserManager(), \OC::$server->getGroupManager())); $application->add(new OC\Core\Command\Group\Info(\OC::$server->get(\OCP\IGroupManager::class))); + $application->add(new OC\Core\Command\SystemTag\ListCommand(\OC::$server->get(\OCP\SystemTag\ISystemTagManager::class))); + $application->add(new OC\Core\Command\SystemTag\Delete(\OC::$server->get(\OCP\SystemTag\ISystemTagManager::class))); + $application->add(new OC\Core\Command\SystemTag\Add(\OC::$server->get(\OCP\SystemTag\ISystemTagManager::class))); + $application->add(new OC\Core\Command\SystemTag\Edit(\OC::$server->get(\OCP\SystemTag\ISystemTagManager::class))); + $application->add(new OC\Core\Command\Security\ListCertificates(\OC::$server->getCertificateManager(), \OC::$server->getL10N('core'))); $application->add(new OC\Core\Command\Security\ImportCertificate(\OC::$server->getCertificateManager())); $application->add(new OC\Core\Command\Security\RemoveCertificate(\OC::$server->getCertificateManager())); diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index d90785e08d..010c2a53a3 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -867,6 +867,10 @@ return array( 'OC\\Core\\Command\\Security\\RemoveCertificate' => $baseDir . '/core/Command/Security/RemoveCertificate.php', 'OC\\Core\\Command\\Security\\ResetBruteforceAttempts' => $baseDir . '/core/Command/Security/ResetBruteforceAttempts.php', 'OC\\Core\\Command\\Status' => $baseDir . '/core/Command/Status.php', + 'OC\\Core\\Command\\SystemTag\\Add' => $baseDir . '/core/Command/SystemTag/Add.php', + 'OC\\Core\\Command\\SystemTag\\Delete' => $baseDir . '/core/Command/SystemTag/Delete.php', + 'OC\\Core\\Command\\SystemTag\\Edit' => $baseDir . '/core/Command/SystemTag/Edit.php', + 'OC\\Core\\Command\\SystemTag\\ListCommand' => $baseDir . '/core/Command/SystemTag/ListCommand.php', 'OC\\Core\\Command\\TwoFactorAuth\\Base' => $baseDir . '/core/Command/TwoFactorAuth/Base.php', 'OC\\Core\\Command\\TwoFactorAuth\\Cleanup' => $baseDir . '/core/Command/TwoFactorAuth/Cleanup.php', 'OC\\Core\\Command\\TwoFactorAuth\\Disable' => $baseDir . '/core/Command/TwoFactorAuth/Disable.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 702b47eb4c..1af3458d99 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -7,7 +7,7 @@ namespace Composer\Autoload; class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c { public static $prefixLengthsPsr4 = array ( - 'O' => + 'O' => array ( 'OC\\Core\\' => 8, 'OC\\' => 3, @@ -16,15 +16,15 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c ); public static $prefixDirsPsr4 = array ( - 'OC\\Core\\' => + 'OC\\Core\\' => array ( 0 => __DIR__ . '/../../..' . '/core', ), - 'OC\\' => + 'OC\\' => array ( 0 => __DIR__ . '/../../..' . '/lib/private', ), - 'OCP\\' => + 'OCP\\' => array ( 0 => __DIR__ . '/../../..' . '/lib/public', ), @@ -896,6 +896,10 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Core\\Command\\Security\\RemoveCertificate' => __DIR__ . '/../../..' . '/core/Command/Security/RemoveCertificate.php', 'OC\\Core\\Command\\Security\\ResetBruteforceAttempts' => __DIR__ . '/../../..' . '/core/Command/Security/ResetBruteforceAttempts.php', 'OC\\Core\\Command\\Status' => __DIR__ . '/../../..' . '/core/Command/Status.php', + 'OC\\Core\\Command\\SystemTag\\Add' => __DIR__ . '/../../..' . '/core/Command/SystemTag/Add.php', + 'OC\\Core\\Command\\SystemTag\\Delete' => __DIR__ . '/../../..' . '/core/Command/SystemTag/Delete.php', + 'OC\\Core\\Command\\SystemTag\\Edit' => __DIR__ . '/../../..' . '/core/Command/SystemTag/Edit.php', + 'OC\\Core\\Command\\SystemTag\\ListCommand' => __DIR__ . '/../../..' . '/core/Command/SystemTag/ListCommand.php', 'OC\\Core\\Command\\TwoFactorAuth\\Base' => __DIR__ . '/../../..' . '/core/Command/TwoFactorAuth/Base.php', 'OC\\Core\\Command\\TwoFactorAuth\\Cleanup' => __DIR__ . '/../../..' . '/core/Command/TwoFactorAuth/Cleanup.php', 'OC\\Core\\Command\\TwoFactorAuth\\Disable' => __DIR__ . '/../../..' . '/core/Command/TwoFactorAuth/Disable.php', diff --git a/lib/private/SystemTag/SystemTag.php b/lib/private/SystemTag/SystemTag.php index 8a48e048d3..a33fadd990 100644 --- a/lib/private/SystemTag/SystemTag.php +++ b/lib/private/SystemTag/SystemTag.php @@ -93,4 +93,19 @@ class SystemTag implements ISystemTag { public function isUserAssignable(): bool { return $this->userAssignable; } + + /** + * {@inheritdoc} + */ + public function getAccessLevel(): int { + if ($this->userVisible) { + if ($this->userAssignable) { + return self::ACCESS_LEVEL_PUBLIC; + } else { + return self::ACCESS_LEVEL_RESTRICTED; + } + } else { + return self::ACCESS_LEVEL_INVISIBLE; + } + } } diff --git a/lib/public/SystemTag/ISystemTag.php b/lib/public/SystemTag/ISystemTag.php index 39adb39ec0..b4dfc6856e 100644 --- a/lib/public/SystemTag/ISystemTag.php +++ b/lib/public/SystemTag/ISystemTag.php @@ -32,6 +32,27 @@ namespace OCP\SystemTag; * @since 9.0.0 */ interface ISystemTag { + /** + * @since 22.0.0 + */ + public const ACCESS_LEVEL_PUBLIC = 0; + /** + * @since 22.0.0 + */ + public const ACCESS_LEVEL_RESTRICTED = 1; + /** + * @since 22.0.0 + */ + public const ACCESS_LEVEL_INVISIBLE = 2; + + /** + * @since 22.0.0 + */ + public const ACCESS_LEVEL_LOOKUP = [ + ISystemTag::ACCESS_LEVEL_PUBLIC => 'public', + ISystemTag::ACCESS_LEVEL_RESTRICTED => 'restricted', + ISystemTag::ACCESS_LEVEL_INVISIBLE => 'invisible', + ]; /** * Returns the tag id @@ -68,4 +89,13 @@ interface ISystemTag { * @since 9.0.0 */ public function isUserAssignable(): bool; + + /** + * Returns a term summarizing the access control flags + * + * @return int the level of access control + * + * @since 22.0.0 + */ + public function getAccessLevel(): int; } diff --git a/tests/Core/Command/SystemTag/AddTest.php b/tests/Core/Command/SystemTag/AddTest.php new file mode 100644 index 0000000000..c875c8f3ec --- /dev/null +++ b/tests/Core/Command/SystemTag/AddTest.php @@ -0,0 +1,138 @@ + + * + * @author Johannes Leuker + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace Test\Core\Command\SystemTag; + +use OC\Core\Command\SystemTag\Add; +use OCP\SystemTag\ISystemTag; +use OCP\SystemTag\ISystemTagManager; +use OCP\SystemTag\TagAlreadyExistsException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Test\TestCase; + +class AddTest extends TestCase { + + /** @var ISystemTagManager|\PHPUnit\Framework\MockObject\MockObject */ + private $systemTagManager; + + /** @var ListCommand|\PHPUnit\Framework\MockObject\MockObject */ + private $command; + + /** @var InputInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $input; + + /** @var OutputInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $output; + + protected function setUp(): void { + parent::setUp(); + + $this->systemTagManager = $this->createMock(ISystemTagManager::class); + $this->command = $this->getMockBuilder(Add::class) + ->setConstructorArgs([$this->systemTagManager]) + ->setMethods(['writeArrayInOutputFormat']) + ->getMock(); + + $this->input = $this->createMock(InputInterface::class); + $this->output = $this->createMock(OutputInterface::class); + } + + public function testExecute() { + $tagId = '42'; + $tagName = 'wichtig'; + $tagAccess = 'public'; + + $tag = $this->createMock(ISystemTag::class); + $tag->method('getId')->willReturn($tagId); + $tag->method('getName')->willReturn($tagName); + $tag->method('getAccessLevel')->willReturn(ISystemTag::ACCESS_LEVEL_PUBLIC); + + $this->systemTagManager->method('createTag') + ->with( + $tagName, + true, + true + )->willReturn($tag); + + $this->input->method('getArgument') + ->willReturnCallback(function ($arg) use ($tagName, $tagAccess) { + if ($arg === 'name') { + return $tagName; + } elseif ($arg === 'access') { + return $tagAccess; + } + throw new \Exception(); + }); + + $this->command->expects($this->once()) + ->method('writeArrayInOutputFormat') + ->with( + $this->equalTo($this->input), + $this->equalTo($this->output), + [ + 'id' => $tagId, + 'name' => $tagName, + 'access' => $tagAccess, + ] + ); + + $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]); + } + + public function testAlreadyExists() { + $tagId = '42'; + $tagName = 'wichtig'; + $tagAccess = 'public'; + + $tag = $this->createMock(ISystemTag::class); + $tag->method('getId')->willReturn($tagId); + $tag->method('getName')->willReturn($tagName); + $tag->method('getAccessLevel')->willReturn(ISystemTag::ACCESS_LEVEL_PUBLIC); + + $this->systemTagManager->method('createTag') + ->willReturnCallback(function ($tagName, $userVisible, $userAssignable) { + throw new TagAlreadyExistsException( + 'Tag ("' . $tagName . '", '. $userVisible . ', ' . $userAssignable . ') already exists' + ); + }); + + $this->input->method('getArgument') + ->willReturnCallback(function ($arg) use ($tagName, $tagAccess) { + if ($arg === 'name') { + return $tagName; + } elseif ($arg === 'access') { + return $tagAccess; + } + throw new \Exception(); + }); + + $this->output->expects($this->once()) + ->method('writeln') + ->with( + 'Tag ("wichtig", 1, 1) already exists' + ); + + $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]); + } +} diff --git a/tests/Core/Command/SystemTag/DeleteTest.php b/tests/Core/Command/SystemTag/DeleteTest.php new file mode 100644 index 0000000000..138606049e --- /dev/null +++ b/tests/Core/Command/SystemTag/DeleteTest.php @@ -0,0 +1,100 @@ + + * + * @author Johannes Leuker + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace Test\Core\Command\SystemTag; + +use OC\Core\Command\SystemTag\Delete; +use OCP\SystemTag\ISystemTagManager; +use OCP\SystemTag\TagNotFoundException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Test\TestCase; + +class DeleteTest extends TestCase { + + /** @var ISystemTagManager|\PHPUnit\Framework\MockObject\MockObject */ + private $systemTagManager; + + /** @var ListCommand|\PHPUnit\Framework\MockObject\MockObject */ + private $command; + + /** @var InputInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $input; + + /** @var OutputInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $output; + + protected function setUp(): void { + parent::setUp(); + + $this->systemTagManager = $this->createMock(ISystemTagManager::class); + $this->command = $this->getMockBuilder(Delete::class) + ->setConstructorArgs([$this->systemTagManager]) + ->setMethods(['writeArrayInOutputFormat']) + ->getMock(); + + $this->input = $this->createMock(InputInterface::class); + $this->output = $this->createMock(OutputInterface::class); + } + + public function testExecute() { + $tagId = 69; + + $this->input->method('getArgument') + ->willReturnCallback(function ($arg) use ($tagId) { + if ($arg === 'id') { + return $tagId; + } + throw new \Exception(); + }); + + $this->output->expects($this->once()) + ->method('writeln') + ->with('The specified tag was deleted'); + + $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]); + } + + public function testNotFound() { + $tagId = 69; + + $this->input->method('getArgument') + ->willReturnCallback(function ($arg) use ($tagId) { + if ($arg === 'id') { + return $tagId; + } + throw new \Exception(); + }); + + $this->systemTagManager->method('deleteTags') + ->willReturnCallback(function ($tagId) { + throw new TagNotFoundException(); + }); + + $this->output->expects($this->once()) + ->method('writeln') + ->with('Tag not found'); + + $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]); + } +} diff --git a/tests/Core/Command/SystemTag/EditTest.php b/tests/Core/Command/SystemTag/EditTest.php new file mode 100644 index 0000000000..7794ac5aef --- /dev/null +++ b/tests/Core/Command/SystemTag/EditTest.php @@ -0,0 +1,201 @@ + + * + * @author Johannes Leuker + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace Test\Core\Command\SystemTag; + +use OC\Core\Command\SystemTag\Edit; +use OCP\SystemTag\ISystemTag; +use OCP\SystemTag\ISystemTagManager; +use OCP\SystemTag\TagAlreadyExistsException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Test\TestCase; + +class EditTest extends TestCase { + + /** @var ISystemTagManager|\PHPUnit\Framework\MockObject\MockObject */ + private $systemTagManager; + + /** @var ListCommand|\PHPUnit\Framework\MockObject\MockObject */ + private $command; + + /** @var InputInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $input; + + /** @var OutputInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $output; + + protected function setUp(): void { + parent::setUp(); + + $this->systemTagManager = $this->createMock(ISystemTagManager::class); + $this->command = $this->getMockBuilder(Edit::class) + ->setConstructorArgs([$this->systemTagManager]) + ->setMethods(['writeArrayInOutputFormat']) + ->getMock(); + + $this->input = $this->createMock(InputInterface::class); + $this->output = $this->createMock(OutputInterface::class); + } + + public function testExecute() { + $tagId = '5'; + $tagName = 'unwichtige Dateien'; + $newTagName = 'moderat wichtige Dateien'; + $newTagAccess = 'restricted'; + $newTagUserVisible = true; + $newTagUserAssignable = false; + + $tag = $this->createMock(ISystemTag::class); + $tag->method('getId')->willReturn($tagId); + $tag->method('getName')->willReturn($tagName); + $tag->method('getAccessLevel')->willReturn(ISystemTag::ACCESS_LEVEL_INVISIBLE); + + $this->systemTagManager->method('getTagsByIds') + ->with($tagId) + ->willReturn([$tag]); + + $this->input->method('getArgument') + ->willReturnCallback(function ($arg) use ($tagId) { + if ($arg === 'id') { + return $tagId; + } + throw new \Exception(); + }); + + $this->input->method('getOption') + ->willReturnCallback(function ($arg) use ($newTagName, $newTagAccess) { + if ($arg === 'name') { + return $newTagName; + } elseif ($arg === 'access') { + return $newTagAccess; + } + throw new \Exception(); + }); + + $this->systemTagManager->expects($this->once()) + ->method('updateTag') + ->with( + $tagId, + $newTagName, + $newTagUserVisible, + $newTagUserAssignable + ); + + $this->output->expects($this->once()) + ->method('writeln') + ->with( + 'Tag updated ("'.$newTagName.'", '.$newTagUserVisible.', '.$newTagUserAssignable.')' + ); + + $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]); + } + + public function testAlreadyExists() { + $tagId = '5'; + $tagName = 'unwichtige Dateien'; + $tagUserVisible = false; + $tagUserAssignable = false; + $newTagName = 'moderat wichtige Dateien'; + $newTagAccess = 'restricted'; + $newTagUserVisible = true; + $newTagUserAssignable = false; + + $tag = $this->createMock(ISystemTag::class); + $tag->method('getId')->willReturn($tagId); + $tag->method('getName')->willReturn($tagName); + $tag->method('isUserVisible')->willReturn($tagUserVisible); + $tag->method('isUserAssignable')->willReturn($tagUserAssignable); + $tag->method('getAccessLevel')->willReturn(ISystemTag::ACCESS_LEVEL_INVISIBLE); + + $this->systemTagManager->method('getTagsByIds') + ->with($tagId) + ->willReturn([$tag]); + + $this->input->method('getArgument') + ->willReturnCallback(function ($arg) use ($tagId) { + if ($arg === 'id') { + return $tagId; + } + throw new \Exception(); + }); + + $this->input->method('getOption') + ->willReturnCallback(function ($arg) use ($newTagName, $newTagAccess) { + if ($arg === 'name') { + return $newTagName; + } elseif ($arg === 'access') { + return $newTagAccess; + } + throw new \Exception(); + }); + + $this->systemTagManager->method('updateTag') + ->willReturnCallback(function ($tagId, $tagName, $userVisible, $userAssignable) { + throw new TagAlreadyExistsException( + 'Tag ("' . $tagName . '", '. $userVisible . ', ' . $userAssignable . ') already exists' + ); + }); + + $this->systemTagManager->expects($this->once()) + ->method('updateTag') + ->with( + $tagId, + $newTagName, + $newTagUserVisible, + $newTagUserAssignable + ); + + $this->output->expects($this->once()) + ->method('writeln') + ->with( + 'Tag ("' . $newTagName . '", '. $newTagUserVisible . ', ' . $newTagUserAssignable . ') already exists' + ); + + $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]); + } + + public function testNotFound() { + $tagId = '404'; + + $this->input->method('getArgument') + ->willReturnCallback(function ($arg) use ($tagId) { + if ($arg === 'id') { + return $tagId; + } + throw new \Exception(); + }); + + $this->systemTagManager->method('getTagsByIds') + ->with($tagId) + ->willReturn([]); + + $this->output->expects($this->once()) + ->method('writeln') + ->with( + 'Tag not found' + ); + + $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]); + } +} diff --git a/tests/Core/Command/SystemTag/ListCommandTest.php b/tests/Core/Command/SystemTag/ListCommandTest.php new file mode 100644 index 0000000000..2e2fba1017 --- /dev/null +++ b/tests/Core/Command/SystemTag/ListCommandTest.php @@ -0,0 +1,113 @@ + + * + * @author Johannes Leuker + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace Test\Core\Command\SystemTag; + +use OC\Core\Command\SystemTag\ListCommand; +use OCP\SystemTag\ISystemTag; +use OCP\SystemTag\ISystemTagManager; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Test\TestCase; + +class ListCommandTest extends TestCase { + + /** @var ISystemTagManager|\PHPUnit\Framework\MockObject\MockObject */ + private $systemTagManager; + + /** @var ListCommand|\PHPUnit\Framework\MockObject\MockObject */ + private $command; + + /** @var InputInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $input; + + /** @var OutputInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $output; + + protected function setUp(): void { + parent::setUp(); + + $this->systemTagManager = $this->createMock(ISystemTagManager::class); + $this->command = $this->getMockBuilder(ListCommand::class) + ->setConstructorArgs([$this->systemTagManager]) + ->setMethods(['writeArrayInOutputFormat']) + ->getMock(); + + $this->input = $this->createMock(InputInterface::class); + $this->output = $this->createMock(OutputInterface::class); + } + + public function testExecute() { + $tag1 = $this->createMock(ISystemTag::class); + $tag1->method('getId')->willReturn('1'); + $tag1->method('getName')->willReturn('public_tag'); + $tag1->method('getAccessLevel')->willReturn(ISystemTag::ACCESS_LEVEL_PUBLIC); + $tag2 = $this->createMock(ISystemTag::class); + $tag2->method('getId')->willReturn('2'); + $tag2->method('getName')->willReturn('restricted_tag'); + $tag2->method('getAccessLevel')->willReturn(ISystemTag::ACCESS_LEVEL_RESTRICTED); + $tag3 = $this->createMock(ISystemTag::class); + $tag3->method('getId')->willReturn('3'); + $tag3->method('getName')->willReturn('invisible_tag'); + $tag3->method('getAccessLevel')->willReturn(ISystemTag::ACCESS_LEVEL_INVISIBLE); + + $this->systemTagManager->method('getAllTags') + ->with( + null, + null + )->willReturn([$tag1, $tag2, $tag3]); + + $this->input->method('getOption') + ->willReturnCallback(function ($arg) { + if ($arg === 'visibilityFilter') { + return null; + } elseif ($arg === 'nameSearchPattern') { + return null; + } + throw new \Exception(); + }); + + $this->command->expects($this->once()) + ->method('writeArrayInOutputFormat') + ->with( + $this->equalTo($this->input), + $this->equalTo($this->output), + [ + '1' => [ + 'name' => 'public_tag', + 'access' => 'public', + ], + '2' => [ + 'name' => 'restricted_tag', + 'access' => 'restricted', + ], + '3' => [ + 'name' => 'invisible_tag', + 'access' => 'invisible', + ] + ] + ); + + $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]); + } +}