diff --git a/appinfo/info.xml b/appinfo/info.xml index c6e655aa5..bb977b169 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -26,7 +26,7 @@ In your Nextcloud instance, simply navigate to **»Apps«**, find the **»Teams«** and **»Collectives«** apps and enable them. ]]> - 2.14.4 + 2.15.0 agpl CollectiveCloud Team Collectives diff --git a/composer.lock b/composer.lock index 3fefc171e..5d31ea07a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "fdf3c7f31d0b558bade1d751baaa4e7e", + "content-hash": "c52d7e2779851295d90eb9565eab5330", "packages": [ { "name": "predis/predis", @@ -67,6 +67,487 @@ ], "time": "2023-09-13T16:42:03+00:00" }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/string", + "version": "v6.0.19", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "d9e72497367c23e08bf94176d2be45b00a9d232a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/d9e72497367c23e08bf94176d2be45b00a9d232a", + "reference": "d9e72497367c23e08bf94176d2be45b00a9d232a", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.0" + }, + "require-dev": { + "symfony/error-handler": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/translation-contracts": "^2.0|^3.0", + "symfony/var-exporter": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v6.0.19" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-01T08:36:10+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v2.5.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "b0073a77ac0b7ea55131020e87b1e3af540f4664" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/b0073a77ac0b7ea55131020e87b1e3af540f4664", + "reference": "b0073a77ac0b7ea55131020e87b1e3af540f4664", + "shasum": "" + }, + "require": { + "php": ">=7.2.5" + }, + "suggest": { + "symfony/translation-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v2.5.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T13:51:25+00:00" + }, { "name": "teamtnt/tntsearch", "version": "v4.3.0", @@ -913,7 +1394,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { @@ -928,5 +1409,5 @@ "platform-overrides": { "php": "8.0.2" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/lib/Db/Page.php b/lib/Db/Page.php index d3c169b0f..58c739955 100644 --- a/lib/Db/Page.php +++ b/lib/Db/Page.php @@ -48,6 +48,7 @@ public function jsonSerialize(): array { return [ 'id' => $this->id, 'fileId' => $this->fileId, + 'slug' => $this->slug, 'lastUserId' => $this->lastUserId, 'emoji' => $this->emoji, 'subpageOrder' => json_decode($this->getSubpageOrder() ?? '[]', true, 512, JSON_THROW_ON_ERROR), diff --git a/lib/Listeners/CircleEditingEventListener.php b/lib/Listeners/CircleEditingEventListener.php index 8c04e6e46..a0bd54d7e 100644 --- a/lib/Listeners/CircleEditingEventListener.php +++ b/lib/Listeners/CircleEditingEventListener.php @@ -12,7 +12,7 @@ use OCA\Circles\Events\EditingCircleEvent; use OCA\Collectives\Db\CollectiveMapper; use OCA\Collectives\Service\NotFoundException; -use OCA\Collectives\Service\SlugGeneratorService; +use OCA\Collectives\Service\SlugService; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; use OCP\Files\NotPermittedException as FilesNotPermittedException; @@ -21,7 +21,7 @@ class CircleEditingEventListener implements IEventListener { public function __construct( private CollectiveMapper $collectiveMapper, - private SlugGeneratorService $slugGeneratorService, + private SlugService $slugService, ) { } /** @@ -48,7 +48,7 @@ public function handle(Event $event): void { } // Update slug if name has changed - $slug = $this->slugGeneratorService->generateCollectiveSlug($collective->getId(), $name); + $slug = $this->slugService->generateCollectiveSlug($collective->getId(), $name); $collective->setSlug($slug); $this->collectiveMapper->update($collective); } diff --git a/lib/Migration/Version021200Date20240820000000.php b/lib/Migration/Version021500Date20240820000000.php similarity index 87% rename from lib/Migration/Version021200Date20240820000000.php rename to lib/Migration/Version021500Date20240820000000.php index 2af5b3ef0..1dfeec72e 100644 --- a/lib/Migration/Version021200Date20240820000000.php +++ b/lib/Migration/Version021500Date20240820000000.php @@ -11,7 +11,7 @@ use Closure; use OCA\Collectives\Service\CircleHelper; -use OCA\Collectives\Service\SlugGeneratorService; +use OCA\Collectives\Service\SlugService; use OCP\DB\ISchemaWrapper; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\Types; @@ -19,13 +19,13 @@ use OCP\Migration\IOutput; use OCP\Migration\SimpleMigrationStep; -class Version021200Date20240820000000 extends SimpleMigrationStep { +class Version021500Date20240820000000 extends SimpleMigrationStep { private bool $runSlugGeneration = false; public function __construct( private IDBConnection $connection, private CircleHelper $circleHelper, - private SlugGeneratorService $slugGeneratorService, + private SlugService $slugService, ) { } @@ -64,7 +64,7 @@ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array while ($row = $result->fetch()) { $circle = $this->circleHelper->getCircle($row['circle_unique_id'], null, true); - $slug = $this->slugGeneratorService->generateCollectiveSlug($row['id'], $circle->getSanitizedName()); + $slug = $this->slugService->generateCollectiveSlug($row['id'], $circle->getSanitizedName()); $update ->setParameter('id', (int)$row['id'], IQueryBuilder::PARAM_INT) diff --git a/lib/Migration/Version021200Date20240820000001.php b/lib/Migration/Version021500Date20240820000001.php similarity index 85% rename from lib/Migration/Version021200Date20240820000001.php rename to lib/Migration/Version021500Date20240820000001.php index e1caac2c5..3e17c2a81 100644 --- a/lib/Migration/Version021200Date20240820000001.php +++ b/lib/Migration/Version021500Date20240820000001.php @@ -13,7 +13,7 @@ use OCA\Collectives\Model\PageInfo; use OCA\Collectives\Service\CircleHelper; use OCA\Collectives\Service\PageService; -use OCA\Collectives\Service\SlugGeneratorService; +use OCA\Collectives\Service\SlugService; use OCP\DB\ISchemaWrapper; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\Types; @@ -21,14 +21,14 @@ use OCP\Migration\IOutput; use OCP\Migration\SimpleMigrationStep; -class Version021200Date20240820000001 extends SimpleMigrationStep { +class Version021500Date20240820000001 extends SimpleMigrationStep { private bool $runSlugGeneration = false; public function __construct( private IDBConnection $connection, private CircleHelper $circleHelper, - private PageService $service, - private SlugGeneratorService $slugGeneratorService, + private PageService $pageService, + private SlugService $slugService, ) { } @@ -72,15 +72,14 @@ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array while ($rowCollective = $resultCollectives->fetch()) { $circle = $this->circleHelper->getCircle($rowCollective['circle_unique_id'], null, true); - /** @var PageInfo[] $pageInfos */ - $pageInfos = $this->service->findAll($rowCollective['id'], $circle->getOwner()->getUserId()); + $pageInfos = $this->pageService->findAll($rowCollective['id'], $circle->getOwner()->getUserId()); foreach ($pageInfos as $pageInfo) { if ($pageInfo->getFileName() === PageInfo::INDEX_PAGE_TITLE . PageInfo::SUFFIX) { continue; } - $slug = $this->slugGeneratorService->generatePageSlug($pageInfo->getTitle()); + $slug = $this->slugService->generatePageSlug($pageInfo->getTitle()); $update ->setParameter('file_id', $pageInfo->getId(), IQueryBuilder::PARAM_INT) ->setParameter('slug', $slug, IQueryBuilder::PARAM_STR) diff --git a/lib/Service/CollectiveService.php b/lib/Service/CollectiveService.php index d82b0df48..18a616431 100644 --- a/lib/Service/CollectiveService.php +++ b/lib/Service/CollectiveService.php @@ -46,7 +46,7 @@ public function __construct( private IL10N $l10n, private IEventDispatcher $eventDispatcher, private NodeHelper $nodeHelper, - private SlugGeneratorService $slugGeneratorService, + private SlugService $slugService, ) { parent::__construct($collectiveMapper, $circleHelper); } @@ -216,7 +216,7 @@ public function createCollective(string $userId, } $collective = $this->collectiveMapper->insert($collective); - $slug = $this->slugGeneratorService->generateCollectiveSlug($collective->getId(), $name); + $slug = $this->slugService->generateCollectiveSlug($collective->getId(), $name); $collective->setSlug($slug); $this->collectiveMapper->update($collective); diff --git a/lib/Service/PageService.php b/lib/Service/PageService.php index de929ebfa..2cb96ea85 100644 --- a/lib/Service/PageService.php +++ b/lib/Service/PageService.php @@ -49,7 +49,7 @@ public function __construct( private IConfig $config, ContainerInterface $container, private SessionService $sessionService, - private SlugGeneratorService $slugGeneratorService, + private SlugService $slugService, ) { try { $this->pushQueue = $container->get(IQueue::class); @@ -334,7 +334,7 @@ private function newPage(int $collectiveId, Folder $folder, string $filename, st $userId, $this->userManager->getDisplayName($userId)); $slug = $title ? $this->generateSlugForPage($title, $newFile) : null; - $this->updatePage($collectiveId, $newFile->getId(), $userId, slug: $slug); + $this->updatePage($collectiveId, $newFile->getId(), $userId, null, null, $slug); $pageInfo->setSlug($slug); } catch (FilesNotFoundException|InvalidPathException $e) { throw new NotFoundException($e->getMessage(), 0, $e); @@ -456,6 +456,8 @@ public function findChildren(int $collectiveId, int $parentId, string $userId): * @throws MissingDependencyException * @throws NotFoundException * @throws NotPermittedException + * + * @return PageInfo[] */ public function findAll(int $collectiveId, string $userId): array { $folder = $this->getCollectiveFolder($collectiveId, $userId); @@ -755,9 +757,9 @@ public function copy(int $collectiveId, int $id, ?int $parentId, ?string $title, if (null !== $newFile = $this->moveOrCopyPage($collectiveFolder, $file, $parentId, $title, true)) { $file = $newFile; } - $slug = $title ? $this->generateSlugForPage($title, $file) : $this->generateSlugForPage($page->getTitle(), $file); + $slug = $this->generateSlugForPage($title ?: $page->getTitle(), $file); try { - $this->updatePage($collectiveId, $file->getId(), $userId, $page->getEmoji(), slug: $slug); + $this->updatePage($collectiveId, $file->getId(), $userId, $page->getEmoji(), null, $slug); } catch (InvalidPathException|FilesNotFoundException $e) { throw new NotFoundException($e->getMessage(), 0, $e); } @@ -786,7 +788,7 @@ public function move(int $collectiveId, int $id, ?int $parentId, ?string $title, $slug = $title ? $this->generateSlugForPage($title, $file) : null; try { - $this->updatePage($collectiveId, $file->getId(), $userId, slug: $slug); + $this->updatePage($collectiveId, $file->getId(), $userId, null, null, $slug); } catch (InvalidPathException|FilesNotFoundException $e) { throw new NotFoundException($e->getMessage(), 0, $e); } @@ -1102,6 +1104,6 @@ private function generateSlugForPage(string $title, ?File $file): ?string { return null; } - return $this->slugGeneratorService->generatePageSlug($title); + return $this->slugService->generatePageSlug($title); } } diff --git a/lib/Service/SlugGeneratorService.php b/lib/Service/SlugService.php similarity index 95% rename from lib/Service/SlugGeneratorService.php rename to lib/Service/SlugService.php index 5d3a378f1..40ce07afb 100644 --- a/lib/Service/SlugGeneratorService.php +++ b/lib/Service/SlugService.php @@ -11,7 +11,7 @@ use Symfony\Component\String\Slugger\SluggerInterface; -class SlugGeneratorService { +class SlugService { public function __construct( private SluggerInterface $slugger, ) { diff --git a/tests/stub.phpstub b/tests/stub.phpstub index 665b7a6a8..f28759b78 100644 --- a/tests/stub.phpstub +++ b/tests/stub.phpstub @@ -647,24 +647,18 @@ namespace OCA\Circles\Model { } } -namespace OCA\Circles\Model\Probes { - class CircleProbe { - public function mustBeMember(bool $must = true): self {} - } -} - namespace OCA\Circles\Model\Federated { use OCA\Circles\Tools\Model\SimpleDataStore; - class FederatedEvent { - public function getParams(): SimpleDataStore {} - } + class FederatedEvent { + public function getParams(): SimpleDataStore {} + } } -namespace OCA\Circles\Tools\Model { - class SimpleDataStore { - public function g(string $key): string {} - } +namespace OCA\Circles\Model\Probes { + class CircleProbe { + public function mustBeMember(bool $must = true): self {} + } } namespace OCA\Circles\Events { @@ -704,6 +698,12 @@ namespace OCA\Circles\Tools\Exceptions { class InvalidItemException extends Exception {} } +namespace OCA\Circles\Tools\Model { + class SimpleDataStore { + public function g(string $key): string {} + } +} + namespace OCA\Circles { use OCA\Circles\Model\Circle; use OCA\Circles\Model\FederatedUser;