From c843d147740572f084c0d1ae8ae5ed05561236f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Wed, 18 Jun 2025 09:39:31 +0200 Subject: [PATCH 01/45] NGSTACK-906 add 'priority' property to Tag objects --- bundle/API/Repository/Values/Tags/Tag.php | 8 ++++++++ bundle/SPI/Persistence/Tags/Tag.php | 7 +++++++ bundle/SPI/Persistence/Tags/TagInfo.php | 7 +++++++ 3 files changed, 22 insertions(+) diff --git a/bundle/API/Repository/Values/Tags/Tag.php b/bundle/API/Repository/Values/Tags/Tag.php index 99a6714e..7726c10d 100644 --- a/bundle/API/Repository/Values/Tags/Tag.php +++ b/bundle/API/Repository/Values/Tags/Tag.php @@ -28,6 +28,7 @@ * @property-read bool $alwaysAvailable Indicates if the Tag object is shown in the main language if it is not present in an other requested language * @property-read string $mainLanguageCode The main language code of the Tag object * @property-read string[] $languageCodes List of languages in this Tag object + * @property-read int $priority Tag priority */ final class Tag extends ValueObject { @@ -104,6 +105,13 @@ final class Tag extends ValueObject */ protected ?string $prioritizedLanguageCode; + /** + * Tag priority. + * + * Position of the Tag among its siblings when sorted by priority. + */ + protected int $priority; + /** * Construct object optionally with a set of properties. * diff --git a/bundle/SPI/Persistence/Tags/Tag.php b/bundle/SPI/Persistence/Tags/Tag.php index 8fe27331..2c37b301 100644 --- a/bundle/SPI/Persistence/Tags/Tag.php +++ b/bundle/SPI/Persistence/Tags/Tag.php @@ -72,4 +72,11 @@ final class Tag extends ValueObject * @var int[] */ public array $languageIds = []; + + /** + * Tag priority. + * + * Position of the Tag among its siblings when sorted by priority. + */ + public int $priority; } diff --git a/bundle/SPI/Persistence/Tags/TagInfo.php b/bundle/SPI/Persistence/Tags/TagInfo.php index 1d3d23a7..b5bc8095 100644 --- a/bundle/SPI/Persistence/Tags/TagInfo.php +++ b/bundle/SPI/Persistence/Tags/TagInfo.php @@ -64,4 +64,11 @@ final class TagInfo extends ValueObject * @var int[] */ public array $languageIds = []; + + /** + * Tag priority. + * + * Position of the Tag among its siblings when sorted by priority. + */ + public int $priority; } From 1c5b716c047c0d3e17e8e7326e288895ad2f107f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Wed, 18 Jun 2025 09:41:05 +0200 Subject: [PATCH 02/45] NGSTACK-906 add support for 'priority' property in the Mapper layer --- bundle/Core/Persistence/Legacy/Tags/Mapper.php | 2 ++ bundle/Core/Repository/TagsMapper.php | 1 + 2 files changed, 3 insertions(+) diff --git a/bundle/Core/Persistence/Legacy/Tags/Mapper.php b/bundle/Core/Persistence/Legacy/Tags/Mapper.php index 024eecdf..e4572bcb 100644 --- a/bundle/Core/Persistence/Legacy/Tags/Mapper.php +++ b/bundle/Core/Persistence/Legacy/Tags/Mapper.php @@ -35,6 +35,7 @@ public function createTagInfoFromRow(array $row): TagInfo $tagInfo->alwaysAvailable = (bool) ((int) $row['language_mask'] & 1); $tagInfo->mainLanguageCode = $this->languageHandler->load($row['main_language_id'])->languageCode; $tagInfo->languageIds = $this->languageMaskGenerator->extractLanguageIdsFromMask((int) $row['language_mask']); + $tagInfo->priority = (int) $row['priority']; return $tagInfo; } @@ -60,6 +61,7 @@ public function extractTagListFromRows(array $rows): array $tag->alwaysAvailable = (bool) ((int) $row['language_mask'] & 1); $tag->mainLanguageCode = $this->languageHandler->load($row['main_language_id'])->languageCode; $tag->languageIds = $this->languageMaskGenerator->extractLanguageIdsFromMask((int) $row['language_mask']); + $tag->priority = (int) $row['priority']; $tagList[$tagId] = $tag; } diff --git a/bundle/Core/Repository/TagsMapper.php b/bundle/Core/Repository/TagsMapper.php index 2d94ff7e..52acc6d3 100644 --- a/bundle/Core/Repository/TagsMapper.php +++ b/bundle/Core/Repository/TagsMapper.php @@ -67,6 +67,7 @@ public function buildTagDomainList(array $spiTags, array $prioritizedLanguages = 'mainLanguageCode' => $spiTag->mainLanguageCode, 'languageCodes' => $languageCodes, 'prioritizedLanguageCode' => $prioritizedLanguageCode, + 'priority' => $spiTag->priority, ], ); } From 2d27fc097a4b5196b6c898bac0a089a192a53636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Wed, 18 Jun 2025 09:42:01 +0200 Subject: [PATCH 03/45] NGSTACK-906 modify createTagFindQuery method to select 'priority' property --- bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php | 1 + 1 file changed, 1 insertion(+) diff --git a/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php b/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php index 02a857dd..eeb46b6a 100644 --- a/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php +++ b/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php @@ -930,6 +930,7 @@ private function createTagFindQuery(?array $translations = null, bool $useAlways 'eztags.remote_id', 'eztags.main_language_id', 'eztags.language_mask', + 'eztags.priority', // Tag keywords 'eztags_keyword.keyword', 'eztags_keyword.locale', From a8cf6057f7acc5f577d0da24fc2f92d7d919da33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Wed, 18 Jun 2025 09:43:10 +0200 Subject: [PATCH 04/45] NGSTACK-906 add 'priority' column to 'eztags' table in MySQL and PostgreSQL database schemas --- bundle/Resources/sql/mysql/schema.sql | 1 + bundle/Resources/sql/postgresql/schema.sql | 1 + 2 files changed, 2 insertions(+) diff --git a/bundle/Resources/sql/mysql/schema.sql b/bundle/Resources/sql/mysql/schema.sql index 6130ee95..10263532 100644 --- a/bundle/Resources/sql/mysql/schema.sql +++ b/bundle/Resources/sql/mysql/schema.sql @@ -9,6 +9,7 @@ CREATE TABLE `eztags` ( `remote_id` varchar(100) NOT NULL default '', `main_language_id` int(11) NOT NULL default '0', `language_mask` int(11) NOT NULL default '0', + `priority` int(11) NOT NULL default '0', PRIMARY KEY ( `id` ), KEY `idx_eztags_keyword` ( `keyword`(191) ), KEY `idx_eztags_keyword_id` ( `keyword`(191), `id` ), diff --git a/bundle/Resources/sql/postgresql/schema.sql b/bundle/Resources/sql/postgresql/schema.sql index b86c4705..21fa5720 100644 --- a/bundle/Resources/sql/postgresql/schema.sql +++ b/bundle/Resources/sql/postgresql/schema.sql @@ -10,6 +10,7 @@ CREATE TABLE eztags ( remote_id varchar(100) NOT NULL default '', main_language_id integer not null default 0, language_mask integer not null default 0, + priority integer not null default 0, PRIMARY KEY (id), CONSTRAINT idx_eztags_remote_id UNIQUE (remote_id) ); From 3a841117201ddc8307d8d59b6cb998cf577b4fdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Wed, 18 Jun 2025 09:43:54 +0200 Subject: [PATCH 05/45] NGSTACK-906 add translations for tag priority --- bundle/Resources/translations/netgen_tags_admin.en.yml | 1 + bundle/Resources/translations/netgen_tags_admin.fr.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/bundle/Resources/translations/netgen_tags_admin.en.yml b/bundle/Resources/translations/netgen_tags_admin.en.yml index 34a5ea08..c770cee1 100644 --- a/bundle/Resources/translations/netgen_tags_admin.en.yml +++ b/bundle/Resources/translations/netgen_tags_admin.en.yml @@ -11,6 +11,7 @@ tag.remote_id: 'Remote ID' tag.tag_name: 'Keyword' tag.translations: 'Translations' tag.modified: 'Modified' +tag.priority: 'Priority' tag.main_tag: 'Main tag' tag.parent_tag: 'Parent tag' diff --git a/bundle/Resources/translations/netgen_tags_admin.fr.yml b/bundle/Resources/translations/netgen_tags_admin.fr.yml index 2bdf7739..91c8cc78 100644 --- a/bundle/Resources/translations/netgen_tags_admin.fr.yml +++ b/bundle/Resources/translations/netgen_tags_admin.fr.yml @@ -11,6 +11,7 @@ tag.remote_id: 'Identification à distance' tag.tag_name: 'Mot-clé' tag.translations: 'Traductions' tag.modified: 'Modifié' +tag.priority: 'Priorité' tag.main_tag: 'Tag principale' tag.parent_tag: 'Tag parent' From 8e535ea0b0e250152e7744a155866d797a334758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Wed, 18 Jun 2025 09:44:25 +0200 Subject: [PATCH 06/45] NGSTACK-906 show 'priority' column for tag's children --- bundle/Resources/views/admin/tag/children.html.twig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bundle/Resources/views/admin/tag/children.html.twig b/bundle/Resources/views/admin/tag/children.html.twig index 7b21158e..f2941981 100644 --- a/bundle/Resources/views/admin/tag/children.html.twig +++ b/bundle/Resources/views/admin/tag/children.html.twig @@ -26,6 +26,7 @@ {{ 'tag.tag_name'|trans }} {{ 'tag.translations'|trans }} {{ 'tag.modified'|trans }} + {{ 'tag.priority'|trans }} @@ -48,6 +49,7 @@ {% endfor %} {{ child.modificationDate|date }} + {{ child.priority }} {% endfor %} From 7d59685ff2af4bc8e533f7cb9613e0080bbf4de5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Wed, 18 Jun 2025 10:35:34 +0200 Subject: [PATCH 07/45] NGSTACK-906 add 'subitems' tab to the show tag template --- bundle/Resources/views/admin/tag/show.html.twig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bundle/Resources/views/admin/tag/show.html.twig b/bundle/Resources/views/admin/tag/show.html.twig index 0cdebad0..6cdf57df 100644 --- a/bundle/Resources/views/admin/tag/show.html.twig +++ b/bundle/Resources/views/admin/tag/show.html.twig @@ -49,6 +49,7 @@ {% if not tag.isSynonym %}
  • {{ 'tag.synonyms.title'|trans }} {{ synonyms|length }}
  • {{ 'tag.subtree_limitations.title'|trans }} {{ subTreeLimitations|length }}
  • +
  • {{ 'tag.subitems.title'|trans }}
  • {% endif %} @@ -68,6 +69,10 @@
    {% include '@NetgenTags/admin/tag/tabs/subtree_limitations.html.twig' %}
    + +
    + {% include '@NetgenTags/admin/tag/tabs/subitems.html.twig' %} +
    {% endif %} From fe7ba816e33ea9b40878d63f69afc036efeaa41d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Wed, 18 Jun 2025 10:36:14 +0200 Subject: [PATCH 08/45] NGSTACK-906 add 'subitems' tab template with dropdowns for sortBy and sortOrder --- .../views/admin/tag/tabs/subitems.html.twig | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 bundle/Resources/views/admin/tag/tabs/subitems.html.twig diff --git a/bundle/Resources/views/admin/tag/tabs/subitems.html.twig b/bundle/Resources/views/admin/tag/tabs/subitems.html.twig new file mode 100644 index 00000000..42706123 --- /dev/null +++ b/bundle/Resources/views/admin/tag/tabs/subitems.html.twig @@ -0,0 +1,33 @@ +{% trans_default_domain 'netgen_tags_admin' %} + +
    +
    + + + + + + + +
    +
    From 9158a5d80ac4c6e0870fec4fd681e3163b2284c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Wed, 18 Jun 2025 10:37:31 +0200 Subject: [PATCH 09/45] NGSTACK-906 add translations for tag subitems sorting --- bundle/Resources/translations/netgen_tags_admin.en.yml | 6 ++++++ bundle/Resources/translations/netgen_tags_admin.fr.yml | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/bundle/Resources/translations/netgen_tags_admin.en.yml b/bundle/Resources/translations/netgen_tags_admin.en.yml index c770cee1..7205e464 100644 --- a/bundle/Resources/translations/netgen_tags_admin.en.yml +++ b/bundle/Resources/translations/netgen_tags_admin.en.yml @@ -44,6 +44,12 @@ tag.children.no_children: 'No tags found' tag.subtree_limitations.title: 'Subtree limitations' tag.subtree_limitations.no_limitations: 'No subtree limitations' +tag.subitems.title: 'Subitems' +tag.subitems.sort_by: 'Sort by' +tag.subitems.sort_order: 'Sort order' +tag.subitems.sort_order.ascending: 'Ascending' +tag.subitems.sort_order.descending: 'Descending' + tag.related_content.title: 'List of content related to tag' tag.related_content.filter.content_type: 'Content type filter' tag.related_content.filter.sort: 'Sort' diff --git a/bundle/Resources/translations/netgen_tags_admin.fr.yml b/bundle/Resources/translations/netgen_tags_admin.fr.yml index 91c8cc78..03c1784d 100644 --- a/bundle/Resources/translations/netgen_tags_admin.fr.yml +++ b/bundle/Resources/translations/netgen_tags_admin.fr.yml @@ -44,6 +44,12 @@ tag.children.no_children: 'Pas de tags trouvés' tag.subtree_limitations.title: 'Limites des sous-arbres' tag.subtree_limitations.no_limitations: 'Aucune limite de sous-arbre' +tag.subitems.title: 'Sous-éléments' +tag.subitems.sort_by: 'Trier par' +tag.subitems.sort_order: 'Ordre de tri' +tag.subitems.sort_order.ascending: 'Ascendant' +tag.subitems.sort_order.descending: 'Descendant' + tag.related_content.title: 'Liste des contenus liés au tag' tag.add.title: 'Nouveau tag' From 9ce94275faf7000b52c86e4d413f5837b2637b0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Fri, 20 Jun 2025 09:14:35 +0200 Subject: [PATCH 10/45] NGSTACK-906 update tag children sorting functionality in TagController and update adapter implementation --- bundle/Controller/Admin/TagController.php | 10 +++++++-- .../Pagerfanta/ChildrenTagsAdapter.php | 21 ++++++++++++++++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/bundle/Controller/Admin/TagController.php b/bundle/Controller/Admin/TagController.php index 7d9030df..e0757f45 100644 --- a/bundle/Controller/Admin/TagController.php +++ b/bundle/Controller/Admin/TagController.php @@ -8,6 +8,7 @@ use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; use Netgen\TagsBundle\API\Repository\TagsService; use Netgen\TagsBundle\API\Repository\Values\Tags\Tag; +use Netgen\TagsBundle\Core\Pagination\Pagerfanta\ChildrenTagsAdapter; use Netgen\TagsBundle\Core\Pagination\Pagerfanta\SearchTagsAdapter; use Netgen\TagsBundle\Form\Type\CopyTagsType; use Netgen\TagsBundle\Form\Type\LanguageSelectType; @@ -16,7 +17,6 @@ use Netgen\TagsBundle\Form\Type\TagCreateType; use Netgen\TagsBundle\Form\Type\TagMergeType; use Netgen\TagsBundle\Form\Type\TagUpdateType; -use Pagerfanta\Adapter\AdapterInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -29,7 +29,7 @@ final class TagController extends Controller public function __construct( private TagsService $tagsService, private ContentTypeService $contentTypeService, - private AdapterInterface $tagChildrenAdapter, + private ChildrenTagsAdapter $tagChildrenAdapter, private SearchTagsAdapter $searchTagsAdapter, ) {} @@ -45,6 +45,12 @@ public function showTagAction(Request $request, ?Tag $tag = null): Response if (!$tag instanceof Tag || !$tag->isSynonym()) { $configResolver = $this->getConfigResolver(); + $sortBy = $request->query->get('sort_by'); + $sortOrder = $request->query->get('sort_order'); + if ($sortBy !== null && $sortOrder !== null) { + $this->tagChildrenAdapter->setSorting($sortBy, $sortOrder); + } + $currentPage = (int) $request->query->get('page'); $pager = $this->createPager( $this->tagChildrenAdapter, diff --git a/bundle/Core/Pagination/Pagerfanta/ChildrenTagsAdapter.php b/bundle/Core/Pagination/Pagerfanta/ChildrenTagsAdapter.php index 1882190d..53d127f9 100644 --- a/bundle/Core/Pagination/Pagerfanta/ChildrenTagsAdapter.php +++ b/bundle/Core/Pagination/Pagerfanta/ChildrenTagsAdapter.php @@ -11,16 +11,23 @@ final class ChildrenTagsAdapter implements AdapterInterface, TagAdapterInterface { private ?Tag $tag = null; - private int $nbResults; + private ?string $sortBy = null; + private ?string $sortOrder = null; - public function __construct(private TagsService $tagsService) {} + public function __construct(private readonly TagsService $tagsService) {} public function setTag(Tag $tag): void { $this->tag = $tag; } + public function setSorting(?string $sortBy, ?string $sortOrder): void + { + $this->sortBy = $sortBy; + $this->sortOrder = $sortOrder; + } + public function getNbResults(): int { $this->nbResults = $this->nbResults ?? $this->tagsService->getTagChildrenCount($this->tag); @@ -30,7 +37,15 @@ public function getNbResults(): int public function getSlice($offset, $length): iterable { - $childrenTags = $this->tagsService->loadTagChildren($this->tag, $offset, $length); + $childrenTags = $this->tagsService->loadTagChildren( + $this->tag, + $offset, + $length, + null, + true, + $this->sortBy, + $this->sortOrder, + ); $this->nbResults = $this->nbResults ?? $this->tagsService->getTagChildrenCount($this->tag); From 60cf2cf8692f76ea1156770ada7d4c3f351facc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Fri, 20 Jun 2025 09:18:17 +0200 Subject: [PATCH 11/45] NGSTACK-906 add support for sorting in loadTagChildren() method in Service layer --- bundle/API/Repository/TagsService.php | 4 +++- bundle/Core/Event/TagsService.php | 4 ++-- bundle/Core/Repository/TagsService.php | 4 +++- bundle/Core/SiteAccessAware/TagsService.php | 4 +++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/bundle/API/Repository/TagsService.php b/bundle/API/Repository/TagsService.php index 9d4f4008..734c738a 100644 --- a/bundle/API/Repository/TagsService.php +++ b/bundle/API/Repository/TagsService.php @@ -71,12 +71,14 @@ public function loadTagByUrl(string $url, array $languages): Tag; * @param int $limit The number of tags returned. If $limit = -1 all children starting at $offset are returned * @param string[]|null $languages A language filter for keywords. If not given all languages are returned * @param bool $useAlwaysAvailable Add main language to $languages if true (default) and if tag is always available + * @param string|null $sortBy The field to sort the children by (e.g. 'id', 'keyword', 'modificationDate', 'priority') + * @param string|null $sortOrder The sort direction, either 'asc' or 'desc' * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the current user is not allowed to read tags * * @return \Netgen\TagsBundle\API\Repository\Values\Tags\TagList */ - public function loadTagChildren(?Tag $tag = null, int $offset = 0, int $limit = -1, ?array $languages = null, bool $useAlwaysAvailable = true): TagList; + public function loadTagChildren(?Tag $tag = null, int $offset = 0, int $limit = -1, ?array $languages = null, bool $useAlwaysAvailable = true, ?string $sortBy = null, ?string $sortOrder = null): TagList; /** * Returns the number of children of a tag object. diff --git a/bundle/Core/Event/TagsService.php b/bundle/Core/Event/TagsService.php index 816a3470..609776d3 100644 --- a/bundle/Core/Event/TagsService.php +++ b/bundle/Core/Event/TagsService.php @@ -38,9 +38,9 @@ public function loadTagByUrl(string $url, array $languages): Tag return $this->service->loadTagByUrl($url, $languages); } - public function loadTagChildren(?Tag $tag = null, int $offset = 0, int $limit = -1, ?array $languages = null, bool $useAlwaysAvailable = true): TagList + public function loadTagChildren(?Tag $tag = null, int $offset = 0, int $limit = -1, ?array $languages = null, bool $useAlwaysAvailable = true, ?string $sortBy = null, ?string $sortOrder = null): TagList { - return $this->service->loadTagChildren($tag, $offset, $limit, $languages, $useAlwaysAvailable); + return $this->service->loadTagChildren($tag, $offset, $limit, $languages, $useAlwaysAvailable, $sortBy, $sortOrder); } public function getTagChildrenCount(?Tag $tag = null, ?array $languages = null, bool $useAlwaysAvailable = true): int diff --git a/bundle/Core/Repository/TagsService.php b/bundle/Core/Repository/TagsService.php index 0574906d..47faabbf 100644 --- a/bundle/Core/Repository/TagsService.php +++ b/bundle/Core/Repository/TagsService.php @@ -152,7 +152,7 @@ public function loadTagByUrl(string $url, array $languages): Tag return $this->mapper->buildTagDomainObject($spiTag, $languages); } - public function loadTagChildren(?Tag $tag = null, int $offset = 0, int $limit = -1, ?array $languages = null, bool $useAlwaysAvailable = true): TagList + public function loadTagChildren(?Tag $tag = null, int $offset = 0, int $limit = -1, ?array $languages = null, bool $useAlwaysAvailable = true, ?string $sortBy = null, ?string $sortOrder = null): TagList { if ($this->hasAccess('tags', 'read') === false) { throw new UnauthorizedException('tags', 'read'); @@ -164,6 +164,8 @@ public function loadTagChildren(?Tag $tag = null, int $offset = 0, int $limit = $limit, $languages, $useAlwaysAvailable, + $sortBy, + $sortOrder, ); $tags = []; diff --git a/bundle/Core/SiteAccessAware/TagsService.php b/bundle/Core/SiteAccessAware/TagsService.php index 37d36a94..76ffd810 100644 --- a/bundle/Core/SiteAccessAware/TagsService.php +++ b/bundle/Core/SiteAccessAware/TagsService.php @@ -52,7 +52,7 @@ public function loadTagByUrl(string $url, array $languages): Tag ); } - public function loadTagChildren(?Tag $tag = null, int $offset = 0, int $limit = -1, ?array $languages = null, bool $useAlwaysAvailable = true): TagList + public function loadTagChildren(?Tag $tag = null, int $offset = 0, int $limit = -1, ?array $languages = null, bool $useAlwaysAvailable = true, ?string $sortBy = null, ?string $sortOrder = null): TagList { return $this->innerService->loadTagChildren( $tag, @@ -60,6 +60,8 @@ public function loadTagChildren(?Tag $tag = null, int $offset = 0, int $limit = $limit, $this->languageResolver->getPrioritizedLanguages($languages), $this->languageResolver->getUseAlwaysAvailable($useAlwaysAvailable), + $sortBy, + $sortOrder, ); } From 1041265f942f8a466e023f150766c3f07f20882f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Fri, 20 Jun 2025 09:20:10 +0200 Subject: [PATCH 12/45] NGSTACK-906 add support for sorting in loadChildren() method for Tag Handler --- bundle/Core/Persistence/Cache/TagsHandler.php | 4 ++-- bundle/Core/Persistence/Legacy/Tags/Handler.php | 4 ++-- bundle/SPI/Persistence/Tags/Handler.php | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bundle/Core/Persistence/Cache/TagsHandler.php b/bundle/Core/Persistence/Cache/TagsHandler.php index eadecf1c..aea605b1 100644 --- a/bundle/Core/Persistence/Cache/TagsHandler.php +++ b/bundle/Core/Persistence/Cache/TagsHandler.php @@ -155,11 +155,11 @@ public function loadTagByKeywordAndParentId(string $keyword, int $parentTagId, ? return $this->tagsHandler->loadTagByKeywordAndParentId($keyword, $parentTagId, $translations, $useAlwaysAvailable); } - public function loadChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true): array + public function loadChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true, ?string $sortBy = null, ?string $sortOrder = null): array { $this->logger->logCall(__METHOD__, ['tag' => $tagId, 'translations' => $translations, 'useAlwaysAvailable' => $useAlwaysAvailable]); - return $this->tagsHandler->loadChildren($tagId, $offset, $limit, $translations, $useAlwaysAvailable); + return $this->tagsHandler->loadChildren($tagId, $offset, $limit, $translations, $useAlwaysAvailable, $sortBy, $sortOrder); } public function getChildrenCount(int $tagId, ?array $translations = null, bool $useAlwaysAvailable = true): int diff --git a/bundle/Core/Persistence/Legacy/Tags/Handler.php b/bundle/Core/Persistence/Legacy/Tags/Handler.php index 3414804b..61f59a71 100644 --- a/bundle/Core/Persistence/Legacy/Tags/Handler.php +++ b/bundle/Core/Persistence/Legacy/Tags/Handler.php @@ -89,9 +89,9 @@ public function loadTagByKeywordAndParentId(string $keyword, int $parentTagId, ? return reset($tag); } - public function loadChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true): array + public function loadChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true, ?string $sortBy = null, ?string $sortOrder = null): array { - $tags = $this->gateway->getChildren($tagId, $offset, $limit, $translations, $useAlwaysAvailable); + $tags = $this->gateway->getChildren($tagId, $offset, $limit, $translations, $useAlwaysAvailable, $sortBy, $sortOrder); return $this->mapper->extractTagListFromRows($tags); } diff --git a/bundle/SPI/Persistence/Tags/Handler.php b/bundle/SPI/Persistence/Tags/Handler.php index 164dbec2..aaebb2ca 100644 --- a/bundle/SPI/Persistence/Tags/Handler.php +++ b/bundle/SPI/Persistence/Tags/Handler.php @@ -63,7 +63,7 @@ public function loadTagByKeywordAndParentId(string $keyword, int $parentTagId, ? * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If the specified tag is not found */ - public function loadChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true): array; + public function loadChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true, ?string $sortBy = null, ?string $sortOrder = null): array; /** * Returns the number of children of a tag identified by $tagId. From 0955f352ac1013237557ff689ffacfaac51d18e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Fri, 20 Jun 2025 09:21:48 +0200 Subject: [PATCH 13/45] NGSTACK-906 add support for sorting in getChildren() method for Gateway layer --- .../Core/Persistence/Legacy/Tags/Gateway.php | 2 +- .../Legacy/Tags/Gateway/DoctrineDatabase.php | 28 +++++++++++++------ .../Tags/Gateway/ExceptionConversion.php | 4 +-- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/bundle/Core/Persistence/Legacy/Tags/Gateway.php b/bundle/Core/Persistence/Legacy/Tags/Gateway.php index 9ad839d1..62a22667 100644 --- a/bundle/Core/Persistence/Legacy/Tags/Gateway.php +++ b/bundle/Core/Persistence/Legacy/Tags/Gateway.php @@ -40,7 +40,7 @@ abstract public function getFullTagDataByKeywordAndParentId(string $keyword, int * * If $limit = -1 all children starting at $offset are returned. */ - abstract public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true): array; + abstract public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true, ?string $sortBy = null, ?string $sortOrder = null): array; /** * Returns how many tags exist below tag identified by $tagId. diff --git a/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php b/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php index eeb46b6a..79788a2b 100644 --- a/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php +++ b/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php @@ -126,7 +126,7 @@ public function getFullTagDataByKeywordAndParentId(string $keyword, int $parentI return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); } - public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true): array + public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true, ?string $sortBy = null, ?string $sortOrder = null): array { $tagIdsQuery = $this->createTagIdsQuery($translations, $useAlwaysAvailable); $tagIdsQuery->andWhere( @@ -137,10 +137,17 @@ public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array ), $tagIdsQuery->expr()->eq('eztags.main_tag_id', 0), ), - )->setParameter('parent_id', $tagId, Types::INTEGER) - ->orderBy('eztags.keyword', 'ASC') - ->setFirstResult($offset) - ->setMaxResults($limit > 0 ? $limit : PHP_INT_MAX); + )->setParameter('parent_id', $tagId, Types::INTEGER); + + if ($sortBy !== null && $sortOrder !== null) { + $tagIdsQuery->orderBy('eztags.' . $sortBy, $sortOrder); + } else { + $tagIdsQuery->orderBy('eztags.keyword', 'ASC'); + } + + $tagIdsQuery + ->setFirstResult($offset) + ->setMaxResults($limit > 0 ? $limit : PHP_INT_MAX); $statement = $tagIdsQuery->execute(); @@ -160,8 +167,13 @@ public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array [':id'], ), ) - ->setParameter('id', $tagIds, Connection::PARAM_INT_ARRAY) - ->orderBy('eztags_keyword.keyword', 'ASC'); + ->setParameter('id', $tagIds, Connection::PARAM_INT_ARRAY); + + if ($sortBy !== null && $sortOrder !== null) { + $query->orderBy('eztags.' . $sortBy, $sortOrder); + } else { + $query->orderBy('eztags_keyword.keyword', 'ASC'); + } return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); } @@ -847,7 +859,7 @@ public function deleteTag(int $tagId): void private function createTagIdsQuery(?array $translations = null, bool $useAlwaysAvailable = true): QueryBuilder { $query = $this->connection->createQueryBuilder(); - $query->select('DISTINCT eztags.id, eztags.keyword') + $query->select('DISTINCT eztags.id, eztags.keyword, eztags.modified, eztags.priority') ->from('eztags', 'eztags') // @todo: Joining with eztags_keyword is probably a VERY bad way to gather that information // since it creates an additional cartesian product with translations. diff --git a/bundle/Core/Persistence/Legacy/Tags/Gateway/ExceptionConversion.php b/bundle/Core/Persistence/Legacy/Tags/Gateway/ExceptionConversion.php index f982ea2f..95cb946f 100644 --- a/bundle/Core/Persistence/Legacy/Tags/Gateway/ExceptionConversion.php +++ b/bundle/Core/Persistence/Legacy/Tags/Gateway/ExceptionConversion.php @@ -71,10 +71,10 @@ public function getFullTagDataByKeywordAndParentId(string $keyword, int $parentI } } - public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true): array + public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true, ?string $sortBy = null, ?string $sortOrder = null): array { try { - return $this->innerGateway->getChildren($tagId, $offset, $limit, $translations, $useAlwaysAvailable); + return $this->innerGateway->getChildren($tagId, $offset, $limit, $translations, $useAlwaysAvailable, $sortBy, $sortOrder); } catch (DBALException $e) { throw new RuntimeException('Database error', 0, $e); } catch (PDOException $e) { From 43def26bb2532243cfcf28574c02c43e1d623c2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Fri, 20 Jun 2025 09:22:55 +0200 Subject: [PATCH 14/45] NGSTACK-906 update subitems sort option value from 'tag_id' to 'id' in admin UI --- bundle/Resources/views/admin/tag/tabs/subitems.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundle/Resources/views/admin/tag/tabs/subitems.html.twig b/bundle/Resources/views/admin/tag/tabs/subitems.html.twig index 42706123..83036ccf 100644 --- a/bundle/Resources/views/admin/tag/tabs/subitems.html.twig +++ b/bundle/Resources/views/admin/tag/tabs/subitems.html.twig @@ -4,7 +4,7 @@
    - - - - - + +
    +
    + + +
    - - +
    + + +
    +
    - -
    - + + From f1e4e1a408ab61726673ff7cf1ccd95a8940e13f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Fri, 20 Jun 2025 11:14:12 +0200 Subject: [PATCH 16/45] NGSTACK-906 update tests to handle 'priority' property --- tests/API/Repository/FieldType/TagsIntegrationTest.php | 3 +++ tests/Core/Persistence/Legacy/Tags/MapperTest.php | 4 ++++ tests/_fixtures/schema/schema.mysql.sql | 1 + tests/_fixtures/schema/schema.postgresql.sql | 3 ++- tests/_fixtures/schema/schema.sqlite.sql | 3 ++- 5 files changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/API/Repository/FieldType/TagsIntegrationTest.php b/tests/API/Repository/FieldType/TagsIntegrationTest.php index 2cd0c5d4..657a8e84 100644 --- a/tests/API/Repository/FieldType/TagsIntegrationTest.php +++ b/tests/API/Repository/FieldType/TagsIntegrationTest.php @@ -253,6 +253,7 @@ private function getTag1(): Tag 'mainLanguageCode' => 'eng-GB', 'languageCodes' => ['eng-GB'], 'prioritizedLanguageCode' => 'eng-GB', + 'priority' => 0, ], ); } @@ -276,6 +277,7 @@ private function getTag2(): Tag 'mainLanguageCode' => 'eng-GB', 'languageCodes' => ['eng-GB'], 'prioritizedLanguageCode' => 'eng-GB', + 'priority' => 0, ], ); } @@ -299,6 +301,7 @@ private function getTag3(): Tag 'mainLanguageCode' => 'eng-GB', 'languageCodes' => ['eng-GB'], 'prioritizedLanguageCode' => 'eng-GB', + 'priority' => 0, ], ); } diff --git a/tests/Core/Persistence/Legacy/Tags/MapperTest.php b/tests/Core/Persistence/Legacy/Tags/MapperTest.php index 9e1ec38c..53a97cb5 100644 --- a/tests/Core/Persistence/Legacy/Tags/MapperTest.php +++ b/tests/Core/Persistence/Legacy/Tags/MapperTest.php @@ -28,6 +28,7 @@ final class MapperTest extends TestCase 'remote_id' => '123456abcdef', 'main_language_id' => 8, 'language_mask' => 9, + 'priority' => 0, ]; /** @@ -47,6 +48,7 @@ final class MapperTest extends TestCase 'language_mask' => 9, 'keyword' => 'Croatia', 'locale' => 'eng-GB', + 'priority' => 0, ]; /** @@ -65,6 +67,7 @@ final class MapperTest extends TestCase 'alwaysAvailable' => true, 'mainLanguageCode' => 'eng-GB', 'languageIds' => [8], + 'priority' => 0, ]; /** @@ -84,6 +87,7 @@ final class MapperTest extends TestCase 'alwaysAvailable' => true, 'mainLanguageCode' => 'eng-GB', 'languageIds' => [8], + 'priority' => 0, ]; private Mapper $tagsMapper; diff --git a/tests/_fixtures/schema/schema.mysql.sql b/tests/_fixtures/schema/schema.mysql.sql index b59724fd..9afa0925 100644 --- a/tests/_fixtures/schema/schema.mysql.sql +++ b/tests/_fixtures/schema/schema.mysql.sql @@ -11,6 +11,7 @@ CREATE TABLE `eztags` ( `remote_id` varchar(100) NOT NULL DEFAULT '', `main_language_id` int(11) NOT NULL DEFAULT '0', `language_mask` int(11) NOT NULL DEFAULT '0', + `priority` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), UNIQUE KEY `eztags_remote_id` (`remote_id`), KEY `eztags_keyword` (`keyword`), diff --git a/tests/_fixtures/schema/schema.postgresql.sql b/tests/_fixtures/schema/schema.postgresql.sql index 8107505c..fd2e84ae 100644 --- a/tests/_fixtures/schema/schema.postgresql.sql +++ b/tests/_fixtures/schema/schema.postgresql.sql @@ -26,7 +26,8 @@ CREATE TABLE eztags ( modified integer DEFAULT 0 NOT NULL, remote_id character varying(100) DEFAULT ''::character varying NOT NULL, main_language_id integer DEFAULT 0 NOT NULL, - language_mask integer DEFAULT 0 NOT NULL + language_mask integer DEFAULT 0 NOT NULL, + priority integer DEFAULT 0 NOT NULL ); DROP TABLE IF EXISTS eztags_attribute_link; diff --git a/tests/_fixtures/schema/schema.sqlite.sql b/tests/_fixtures/schema/schema.sqlite.sql index e93ecd60..4a648b36 100644 --- a/tests/_fixtures/schema/schema.sqlite.sql +++ b/tests/_fixtures/schema/schema.sqlite.sql @@ -10,7 +10,8 @@ CREATE TABLE 'eztags' ( 'modified' integer NOT NULL DEFAULT 0, 'remote_id' text(100) NOT NULL DEFAULT '', 'main_language_id' integer NOT NULL DEFAULT 0, - 'language_mask' integer NOT NULL DEFAULT 0 + 'language_mask' integer NOT NULL DEFAULT 0, + 'priority' integer NOT NULL DEFAULT 0 ); DROP TABLE IF EXISTS 'eztags_attribute_link'; From 61399785afbd962bd8c8b01496974987976391e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Tue, 24 Jun 2025 11:51:20 +0200 Subject: [PATCH 17/45] NGSTACK-906 add enums for tag sortField and sortOrder properties --- bundle/API/Repository/Values/Enums/TagSortField.php | 13 +++++++++++++ bundle/API/Repository/Values/Enums/TagSortOrder.php | 11 +++++++++++ 2 files changed, 24 insertions(+) create mode 100644 bundle/API/Repository/Values/Enums/TagSortField.php create mode 100644 bundle/API/Repository/Values/Enums/TagSortOrder.php diff --git a/bundle/API/Repository/Values/Enums/TagSortField.php b/bundle/API/Repository/Values/Enums/TagSortField.php new file mode 100644 index 00000000..b15ae5ea --- /dev/null +++ b/bundle/API/Repository/Values/Enums/TagSortField.php @@ -0,0 +1,13 @@ + Date: Tue, 24 Jun 2025 11:54:36 +0200 Subject: [PATCH 18/45] NGSTACK-906 add sortField and sortOrder properties to Tag objects and TagInfo object --- bundle/API/Repository/Values/Tags/Tag.php | 14 ++++++++++++++ bundle/SPI/Persistence/Tags/Tag.php | 12 ++++++++++++ bundle/SPI/Persistence/Tags/TagInfo.php | 12 ++++++++++++ 3 files changed, 38 insertions(+) diff --git a/bundle/API/Repository/Values/Tags/Tag.php b/bundle/API/Repository/Values/Tags/Tag.php index 7726c10d..112f066d 100644 --- a/bundle/API/Repository/Values/Tags/Tag.php +++ b/bundle/API/Repository/Values/Tags/Tag.php @@ -6,6 +6,8 @@ use DateTimeInterface; use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortField; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder; use function array_map; use function count; @@ -29,6 +31,8 @@ * @property-read string $mainLanguageCode The main language code of the Tag object * @property-read string[] $languageCodes List of languages in this Tag object * @property-read int $priority Tag priority + * @property-read TagSortField $sortField Specifies by which property the child tags should be sorted on + * @property-read TagSortOrder $sortOrder Specifies whether the sort order should be ascending or descending */ final class Tag extends ValueObject { @@ -112,6 +116,16 @@ final class Tag extends ValueObject */ protected int $priority; + /** + * Specifies by which property the child tags should be sorted on. + */ + protected TagSortField $sortField; + + /** + * Specifies whether the sort order should be ascending or descending. + */ + protected TagSortOrder $sortOrder; + /** * Construct object optionally with a set of properties. * diff --git a/bundle/SPI/Persistence/Tags/Tag.php b/bundle/SPI/Persistence/Tags/Tag.php index 2c37b301..a6aaa41f 100644 --- a/bundle/SPI/Persistence/Tags/Tag.php +++ b/bundle/SPI/Persistence/Tags/Tag.php @@ -5,6 +5,8 @@ namespace Netgen\TagsBundle\SPI\Persistence\Tags; use Ibexa\Contracts\Core\Persistence\ValueObject; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortField; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder; /** * Class representing a tag. @@ -79,4 +81,14 @@ final class Tag extends ValueObject * Position of the Tag among its siblings when sorted by priority. */ public int $priority; + + /** + * Specifies by which property the child tags should be sorted on. + */ + public TagSortField $sortField; + + /** + * Specifies whether the sort order should be ascending or descending. + */ + public TagSortOrder $sortOrder; } diff --git a/bundle/SPI/Persistence/Tags/TagInfo.php b/bundle/SPI/Persistence/Tags/TagInfo.php index b5bc8095..157267b2 100644 --- a/bundle/SPI/Persistence/Tags/TagInfo.php +++ b/bundle/SPI/Persistence/Tags/TagInfo.php @@ -5,6 +5,8 @@ namespace Netgen\TagsBundle\SPI\Persistence\Tags; use Ibexa\Contracts\Core\Persistence\ValueObject; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortField; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder; /** * Class representing a tag info (basically a tag without keywords). @@ -71,4 +73,14 @@ final class TagInfo extends ValueObject * Position of the Tag among its siblings when sorted by priority. */ public int $priority; + + /** + * Specifies by which property the child tags should be sorted on. + */ + public TagSortField $sortField; + + /** + * Specifies whether the sort order should be ascending or descending. + */ + public TagSortOrder $sortOrder; } From ef0d60a850748954b4ee55ca00f10a21e4852b75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Tue, 24 Jun 2025 11:56:11 +0200 Subject: [PATCH 19/45] NGSTACK-906 update Mappers with sortField and sortOrder fields --- bundle/Core/Persistence/Legacy/Tags/Mapper.php | 6 ++++++ bundle/Core/Repository/TagsMapper.php | 2 ++ 2 files changed, 8 insertions(+) diff --git a/bundle/Core/Persistence/Legacy/Tags/Mapper.php b/bundle/Core/Persistence/Legacy/Tags/Mapper.php index e4572bcb..ce94ae10 100644 --- a/bundle/Core/Persistence/Legacy/Tags/Mapper.php +++ b/bundle/Core/Persistence/Legacy/Tags/Mapper.php @@ -6,6 +6,8 @@ use Ibexa\Contracts\Core\Persistence\Content\Language\Handler as LanguageHandler; use Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortField; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder; use Netgen\TagsBundle\SPI\Persistence\Tags\Tag; use Netgen\TagsBundle\SPI\Persistence\Tags\TagInfo; @@ -36,6 +38,8 @@ public function createTagInfoFromRow(array $row): TagInfo $tagInfo->mainLanguageCode = $this->languageHandler->load($row['main_language_id'])->languageCode; $tagInfo->languageIds = $this->languageMaskGenerator->extractLanguageIdsFromMask((int) $row['language_mask']); $tagInfo->priority = (int) $row['priority']; + $tagInfo->sortField = TagSortField::from($row['sortField']); + $tagInfo->sortOrder = TagSortOrder::from($row['sortOrder']); return $tagInfo; } @@ -62,6 +66,8 @@ public function extractTagListFromRows(array $rows): array $tag->mainLanguageCode = $this->languageHandler->load($row['main_language_id'])->languageCode; $tag->languageIds = $this->languageMaskGenerator->extractLanguageIdsFromMask((int) $row['language_mask']); $tag->priority = (int) $row['priority']; + $tag->sortField = TagSortField::from($row['sortField']); + $tag->sortOrder = TagSortOrder::from($row['sortOrder']); $tagList[$tagId] = $tag; } diff --git a/bundle/Core/Repository/TagsMapper.php b/bundle/Core/Repository/TagsMapper.php index 52acc6d3..0fd04a9a 100644 --- a/bundle/Core/Repository/TagsMapper.php +++ b/bundle/Core/Repository/TagsMapper.php @@ -68,6 +68,8 @@ public function buildTagDomainList(array $spiTags, array $prioritizedLanguages = 'languageCodes' => $languageCodes, 'prioritizedLanguageCode' => $prioritizedLanguageCode, 'priority' => $spiTag->priority, + 'sortField' => $spiTag->sortField, + 'sortOrder' => $spiTag->sortOrder, ], ); } From be00504356695180d7b3c64b0e8be42795f1a9f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Tue, 24 Jun 2025 11:57:37 +0200 Subject: [PATCH 20/45] NGSTACK-906 remove sortBy and sortOrder parameters from the getChildren() method in Gateway layer --- .../Core/Persistence/Legacy/Tags/Gateway.php | 2 +- .../Legacy/Tags/Gateway/DoctrineDatabase.php | 20 ++++--------------- .../Tags/Gateway/ExceptionConversion.php | 4 ++-- 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/bundle/Core/Persistence/Legacy/Tags/Gateway.php b/bundle/Core/Persistence/Legacy/Tags/Gateway.php index 62a22667..9ad839d1 100644 --- a/bundle/Core/Persistence/Legacy/Tags/Gateway.php +++ b/bundle/Core/Persistence/Legacy/Tags/Gateway.php @@ -40,7 +40,7 @@ abstract public function getFullTagDataByKeywordAndParentId(string $keyword, int * * If $limit = -1 all children starting at $offset are returned. */ - abstract public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true, ?string $sortBy = null, ?string $sortOrder = null): array; + abstract public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true): array; /** * Returns how many tags exist below tag identified by $tagId. diff --git a/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php b/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php index 79788a2b..870f5ba6 100644 --- a/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php +++ b/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php @@ -126,7 +126,7 @@ public function getFullTagDataByKeywordAndParentId(string $keyword, int $parentI return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); } - public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true, ?string $sortBy = null, ?string $sortOrder = null): array + public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true): array { $tagIdsQuery = $this->createTagIdsQuery($translations, $useAlwaysAvailable); $tagIdsQuery->andWhere( @@ -137,15 +137,7 @@ public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array ), $tagIdsQuery->expr()->eq('eztags.main_tag_id', 0), ), - )->setParameter('parent_id', $tagId, Types::INTEGER); - - if ($sortBy !== null && $sortOrder !== null) { - $tagIdsQuery->orderBy('eztags.' . $sortBy, $sortOrder); - } else { - $tagIdsQuery->orderBy('eztags.keyword', 'ASC'); - } - - $tagIdsQuery + )->setParameter('parent_id', $tagId, Types::INTEGER) ->setFirstResult($offset) ->setMaxResults($limit > 0 ? $limit : PHP_INT_MAX); @@ -169,12 +161,6 @@ public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array ) ->setParameter('id', $tagIds, Connection::PARAM_INT_ARRAY); - if ($sortBy !== null && $sortOrder !== null) { - $query->orderBy('eztags.' . $sortBy, $sortOrder); - } else { - $query->orderBy('eztags_keyword.keyword', 'ASC'); - } - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); } @@ -943,6 +929,8 @@ private function createTagFindQuery(?array $translations = null, bool $useAlways 'eztags.main_language_id', 'eztags.language_mask', 'eztags.priority', + 'eztags.sortField', + 'eztags.sortOrder', // Tag keywords 'eztags_keyword.keyword', 'eztags_keyword.locale', diff --git a/bundle/Core/Persistence/Legacy/Tags/Gateway/ExceptionConversion.php b/bundle/Core/Persistence/Legacy/Tags/Gateway/ExceptionConversion.php index 95cb946f..f982ea2f 100644 --- a/bundle/Core/Persistence/Legacy/Tags/Gateway/ExceptionConversion.php +++ b/bundle/Core/Persistence/Legacy/Tags/Gateway/ExceptionConversion.php @@ -71,10 +71,10 @@ public function getFullTagDataByKeywordAndParentId(string $keyword, int $parentI } } - public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true, ?string $sortBy = null, ?string $sortOrder = null): array + public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true): array { try { - return $this->innerGateway->getChildren($tagId, $offset, $limit, $translations, $useAlwaysAvailable, $sortBy, $sortOrder); + return $this->innerGateway->getChildren($tagId, $offset, $limit, $translations, $useAlwaysAvailable); } catch (DBALException $e) { throw new RuntimeException('Database error', 0, $e); } catch (PDOException $e) { From d88c0667e2b975880bcb9322bd3db0028ccc98f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Tue, 24 Jun 2025 11:59:44 +0200 Subject: [PATCH 21/45] NGSTACK-906 remove sortBy and sortOrder parameters from the loadChildren() method in Handler layer --- bundle/Core/Persistence/Cache/TagsHandler.php | 4 ++-- bundle/Core/Persistence/Legacy/Tags/Handler.php | 4 ++-- bundle/SPI/Persistence/Tags/Handler.php | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bundle/Core/Persistence/Cache/TagsHandler.php b/bundle/Core/Persistence/Cache/TagsHandler.php index aea605b1..eadecf1c 100644 --- a/bundle/Core/Persistence/Cache/TagsHandler.php +++ b/bundle/Core/Persistence/Cache/TagsHandler.php @@ -155,11 +155,11 @@ public function loadTagByKeywordAndParentId(string $keyword, int $parentTagId, ? return $this->tagsHandler->loadTagByKeywordAndParentId($keyword, $parentTagId, $translations, $useAlwaysAvailable); } - public function loadChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true, ?string $sortBy = null, ?string $sortOrder = null): array + public function loadChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true): array { $this->logger->logCall(__METHOD__, ['tag' => $tagId, 'translations' => $translations, 'useAlwaysAvailable' => $useAlwaysAvailable]); - return $this->tagsHandler->loadChildren($tagId, $offset, $limit, $translations, $useAlwaysAvailable, $sortBy, $sortOrder); + return $this->tagsHandler->loadChildren($tagId, $offset, $limit, $translations, $useAlwaysAvailable); } public function getChildrenCount(int $tagId, ?array $translations = null, bool $useAlwaysAvailable = true): int diff --git a/bundle/Core/Persistence/Legacy/Tags/Handler.php b/bundle/Core/Persistence/Legacy/Tags/Handler.php index 61f59a71..3414804b 100644 --- a/bundle/Core/Persistence/Legacy/Tags/Handler.php +++ b/bundle/Core/Persistence/Legacy/Tags/Handler.php @@ -89,9 +89,9 @@ public function loadTagByKeywordAndParentId(string $keyword, int $parentTagId, ? return reset($tag); } - public function loadChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true, ?string $sortBy = null, ?string $sortOrder = null): array + public function loadChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true): array { - $tags = $this->gateway->getChildren($tagId, $offset, $limit, $translations, $useAlwaysAvailable, $sortBy, $sortOrder); + $tags = $this->gateway->getChildren($tagId, $offset, $limit, $translations, $useAlwaysAvailable); return $this->mapper->extractTagListFromRows($tags); } diff --git a/bundle/SPI/Persistence/Tags/Handler.php b/bundle/SPI/Persistence/Tags/Handler.php index aaebb2ca..164dbec2 100644 --- a/bundle/SPI/Persistence/Tags/Handler.php +++ b/bundle/SPI/Persistence/Tags/Handler.php @@ -63,7 +63,7 @@ public function loadTagByKeywordAndParentId(string $keyword, int $parentTagId, ? * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If the specified tag is not found */ - public function loadChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true, ?string $sortBy = null, ?string $sortOrder = null): array; + public function loadChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true): array; /** * Returns the number of children of a tag identified by $tagId. From c0bf0f449de042f702e403e43e4350634daf058b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Tue, 24 Jun 2025 12:00:35 +0200 Subject: [PATCH 22/45] NGSTACK-906 remove sortBy and sortOrder parameters from the loadTagChildren() method in Service layer --- bundle/API/Repository/TagsService.php | 4 +--- bundle/Core/Event/TagsService.php | 4 ++-- bundle/Core/Repository/TagsService.php | 4 +--- bundle/Core/SiteAccessAware/TagsService.php | 4 +--- 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/bundle/API/Repository/TagsService.php b/bundle/API/Repository/TagsService.php index 734c738a..9d4f4008 100644 --- a/bundle/API/Repository/TagsService.php +++ b/bundle/API/Repository/TagsService.php @@ -71,14 +71,12 @@ public function loadTagByUrl(string $url, array $languages): Tag; * @param int $limit The number of tags returned. If $limit = -1 all children starting at $offset are returned * @param string[]|null $languages A language filter for keywords. If not given all languages are returned * @param bool $useAlwaysAvailable Add main language to $languages if true (default) and if tag is always available - * @param string|null $sortBy The field to sort the children by (e.g. 'id', 'keyword', 'modificationDate', 'priority') - * @param string|null $sortOrder The sort direction, either 'asc' or 'desc' * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the current user is not allowed to read tags * * @return \Netgen\TagsBundle\API\Repository\Values\Tags\TagList */ - public function loadTagChildren(?Tag $tag = null, int $offset = 0, int $limit = -1, ?array $languages = null, bool $useAlwaysAvailable = true, ?string $sortBy = null, ?string $sortOrder = null): TagList; + public function loadTagChildren(?Tag $tag = null, int $offset = 0, int $limit = -1, ?array $languages = null, bool $useAlwaysAvailable = true): TagList; /** * Returns the number of children of a tag object. diff --git a/bundle/Core/Event/TagsService.php b/bundle/Core/Event/TagsService.php index 609776d3..816a3470 100644 --- a/bundle/Core/Event/TagsService.php +++ b/bundle/Core/Event/TagsService.php @@ -38,9 +38,9 @@ public function loadTagByUrl(string $url, array $languages): Tag return $this->service->loadTagByUrl($url, $languages); } - public function loadTagChildren(?Tag $tag = null, int $offset = 0, int $limit = -1, ?array $languages = null, bool $useAlwaysAvailable = true, ?string $sortBy = null, ?string $sortOrder = null): TagList + public function loadTagChildren(?Tag $tag = null, int $offset = 0, int $limit = -1, ?array $languages = null, bool $useAlwaysAvailable = true): TagList { - return $this->service->loadTagChildren($tag, $offset, $limit, $languages, $useAlwaysAvailable, $sortBy, $sortOrder); + return $this->service->loadTagChildren($tag, $offset, $limit, $languages, $useAlwaysAvailable); } public function getTagChildrenCount(?Tag $tag = null, ?array $languages = null, bool $useAlwaysAvailable = true): int diff --git a/bundle/Core/Repository/TagsService.php b/bundle/Core/Repository/TagsService.php index 47faabbf..0574906d 100644 --- a/bundle/Core/Repository/TagsService.php +++ b/bundle/Core/Repository/TagsService.php @@ -152,7 +152,7 @@ public function loadTagByUrl(string $url, array $languages): Tag return $this->mapper->buildTagDomainObject($spiTag, $languages); } - public function loadTagChildren(?Tag $tag = null, int $offset = 0, int $limit = -1, ?array $languages = null, bool $useAlwaysAvailable = true, ?string $sortBy = null, ?string $sortOrder = null): TagList + public function loadTagChildren(?Tag $tag = null, int $offset = 0, int $limit = -1, ?array $languages = null, bool $useAlwaysAvailable = true): TagList { if ($this->hasAccess('tags', 'read') === false) { throw new UnauthorizedException('tags', 'read'); @@ -164,8 +164,6 @@ public function loadTagChildren(?Tag $tag = null, int $offset = 0, int $limit = $limit, $languages, $useAlwaysAvailable, - $sortBy, - $sortOrder, ); $tags = []; diff --git a/bundle/Core/SiteAccessAware/TagsService.php b/bundle/Core/SiteAccessAware/TagsService.php index 76ffd810..37d36a94 100644 --- a/bundle/Core/SiteAccessAware/TagsService.php +++ b/bundle/Core/SiteAccessAware/TagsService.php @@ -52,7 +52,7 @@ public function loadTagByUrl(string $url, array $languages): Tag ); } - public function loadTagChildren(?Tag $tag = null, int $offset = 0, int $limit = -1, ?array $languages = null, bool $useAlwaysAvailable = true, ?string $sortBy = null, ?string $sortOrder = null): TagList + public function loadTagChildren(?Tag $tag = null, int $offset = 0, int $limit = -1, ?array $languages = null, bool $useAlwaysAvailable = true): TagList { return $this->innerService->loadTagChildren( $tag, @@ -60,8 +60,6 @@ public function loadTagChildren(?Tag $tag = null, int $offset = 0, int $limit = $limit, $this->languageResolver->getPrioritizedLanguages($languages), $this->languageResolver->getUseAlwaysAvailable($useAlwaysAvailable), - $sortBy, - $sortOrder, ); } From c719cc4e0fa10621cf7d64cdeaea7b8c29bf28d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Tue, 24 Jun 2025 14:03:34 +0200 Subject: [PATCH 23/45] NGSTACK-906 rename subitems template to sort_children_tags template and move it out of tabs folder --- .../views/admin/tag/children.html.twig | 2 +- .../admin/tag/sort_children_tags.html.twig | 27 +++++++++++++++++++ .../views/admin/tag/tabs/subitems.html.twig | 25 ----------------- 3 files changed, 28 insertions(+), 26 deletions(-) create mode 100644 bundle/Resources/views/admin/tag/sort_children_tags.html.twig delete mode 100644 bundle/Resources/views/admin/tag/tabs/subitems.html.twig diff --git a/bundle/Resources/views/admin/tag/children.html.twig b/bundle/Resources/views/admin/tag/children.html.twig index 38df5fdc..fff6d3e5 100644 --- a/bundle/Resources/views/admin/tag/children.html.twig +++ b/bundle/Resources/views/admin/tag/children.html.twig @@ -75,7 +75,7 @@ {% if tag is defined and not tag.isSynonym %} - {% include '@NetgenTags/admin/tag/tabs/subitems.html.twig' %} + {% include '@NetgenTags/admin/tag/sort_children_tags.html.twig' %} {% endif %} {% else %}

    {{ 'tag.children.no_children'|trans }}

    diff --git a/bundle/Resources/views/admin/tag/sort_children_tags.html.twig b/bundle/Resources/views/admin/tag/sort_children_tags.html.twig new file mode 100644 index 00000000..6e29b78f --- /dev/null +++ b/bundle/Resources/views/admin/tag/sort_children_tags.html.twig @@ -0,0 +1,27 @@ +{% trans_default_domain 'netgen_tags_admin' %} + +
    +
    +
    + + +
    + +
    + + +
    +
    + + + + +
    diff --git a/bundle/Resources/views/admin/tag/tabs/subitems.html.twig b/bundle/Resources/views/admin/tag/tabs/subitems.html.twig deleted file mode 100644 index 900792ec..00000000 --- a/bundle/Resources/views/admin/tag/tabs/subitems.html.twig +++ /dev/null @@ -1,25 +0,0 @@ -{% trans_default_domain 'netgen_tags_admin' %} - -
    -
    -
    - - -
    - -
    - - -
    -
    - - -
    From 93c1134f6147180ea572d98a374993bd7fbc0fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Tue, 24 Jun 2025 14:07:27 +0200 Subject: [PATCH 24/45] NGSTACK-906 add sortField and sortOrder properties to UpdateStruct and TagStruct classes --- bundle/API/Repository/Values/Tags/TagStruct.php | 12 ++++++++++++ bundle/SPI/Persistence/Tags/UpdateStruct.php | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/bundle/API/Repository/Values/Tags/TagStruct.php b/bundle/API/Repository/Values/Tags/TagStruct.php index dd5277f1..5e1754d4 100644 --- a/bundle/API/Repository/Values/Tags/TagStruct.php +++ b/bundle/API/Repository/Values/Tags/TagStruct.php @@ -5,6 +5,8 @@ namespace Netgen\TagsBundle\API\Repository\Values\Tags; use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortField; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder; use function array_key_exists; @@ -22,6 +24,16 @@ abstract class TagStruct extends ValueObject */ public ?string $remoteId = null; + /** + * Specifies by which property the child tags should be sorted on. + */ + public TagSortField $sortField; + + /** + * Specifies whether the sort order should be ascending or descending. + */ + public TagSortOrder $sortOrder; + /** * Tag keywords in the target languages * Eg. array( "cro-HR" => "Hrvatska", "eng-GB" => "Croatia" ). diff --git a/bundle/SPI/Persistence/Tags/UpdateStruct.php b/bundle/SPI/Persistence/Tags/UpdateStruct.php index 26cd78e5..05848770 100644 --- a/bundle/SPI/Persistence/Tags/UpdateStruct.php +++ b/bundle/SPI/Persistence/Tags/UpdateStruct.php @@ -5,6 +5,8 @@ namespace Netgen\TagsBundle\SPI\Persistence\Tags; use Ibexa\Contracts\Core\Persistence\ValueObject; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortField; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder; /** * This class represents a value for updating a tag. @@ -33,4 +35,14 @@ final class UpdateStruct extends ValueObject * Indicates if the tag is shown in the main language if it's not present in an other requested language. */ public ?bool $alwaysAvailable; + + /** + * Specifies by which property the child tags should be sorted on. + */ + public TagSortField $sortField; + + /** + * Specifies whether the sort order should be ascending or descending. + */ + public TagSortOrder $sortOrder; } From 41e67877b94b09e99544b1958d869bd8dc0a1b01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Tue, 24 Jun 2025 14:09:28 +0200 Subject: [PATCH 25/45] NGSTACK-906 remove sorting logic in ChildrenTagsAdapter --- .../Pagination/Pagerfanta/ChildrenTagsAdapter.php | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/bundle/Core/Pagination/Pagerfanta/ChildrenTagsAdapter.php b/bundle/Core/Pagination/Pagerfanta/ChildrenTagsAdapter.php index 53d127f9..a4c159b2 100644 --- a/bundle/Core/Pagination/Pagerfanta/ChildrenTagsAdapter.php +++ b/bundle/Core/Pagination/Pagerfanta/ChildrenTagsAdapter.php @@ -12,8 +12,6 @@ final class ChildrenTagsAdapter implements AdapterInterface, TagAdapterInterface { private ?Tag $tag = null; private int $nbResults; - private ?string $sortBy = null; - private ?string $sortOrder = null; public function __construct(private readonly TagsService $tagsService) {} @@ -22,12 +20,6 @@ public function setTag(Tag $tag): void $this->tag = $tag; } - public function setSorting(?string $sortBy, ?string $sortOrder): void - { - $this->sortBy = $sortBy; - $this->sortOrder = $sortOrder; - } - public function getNbResults(): int { $this->nbResults = $this->nbResults ?? $this->tagsService->getTagChildrenCount($this->tag); @@ -41,10 +33,6 @@ public function getSlice($offset, $length): iterable $this->tag, $offset, $length, - null, - true, - $this->sortBy, - $this->sortOrder, ); $this->nbResults = $this->nbResults ?? $this->tagsService->getTagChildrenCount($this->tag); From 705fc354b9ce15287c0602eebe5c8bc343fd375a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Tue, 24 Jun 2025 14:13:05 +0200 Subject: [PATCH 26/45] NGSTACK-906 add new route and controller method for updating sortField and sortOrder data for some tag --- bundle/Controller/Admin/TagController.php | 34 ++++++++++++++----- bundle/Core/Repository/TagsService.php | 2 ++ .../Resources/config/routing/admin/tag.yaml | 5 +++ 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/bundle/Controller/Admin/TagController.php b/bundle/Controller/Admin/TagController.php index e0757f45..b5f2bb6a 100644 --- a/bundle/Controller/Admin/TagController.php +++ b/bundle/Controller/Admin/TagController.php @@ -7,8 +7,10 @@ use Ibexa\Contracts\Core\Repository\ContentTypeService; use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; use Netgen\TagsBundle\API\Repository\TagsService; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortField; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder; use Netgen\TagsBundle\API\Repository\Values\Tags\Tag; -use Netgen\TagsBundle\Core\Pagination\Pagerfanta\ChildrenTagsAdapter; +use Netgen\TagsBundle\API\Repository\Values\Tags\TagUpdateStruct; use Netgen\TagsBundle\Core\Pagination\Pagerfanta\SearchTagsAdapter; use Netgen\TagsBundle\Form\Type\CopyTagsType; use Netgen\TagsBundle\Form\Type\LanguageSelectType; @@ -17,6 +19,7 @@ use Netgen\TagsBundle\Form\Type\TagCreateType; use Netgen\TagsBundle\Form\Type\TagMergeType; use Netgen\TagsBundle\Form\Type\TagUpdateType; +use Pagerfanta\Adapter\AdapterInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -29,7 +32,7 @@ final class TagController extends Controller public function __construct( private TagsService $tagsService, private ContentTypeService $contentTypeService, - private ChildrenTagsAdapter $tagChildrenAdapter, + private AdapterInterface $tagChildrenAdapter, private SearchTagsAdapter $searchTagsAdapter, ) {} @@ -44,13 +47,6 @@ public function showTagAction(Request $request, ?Tag $tag = null): Response if (!$tag instanceof Tag || !$tag->isSynonym()) { $configResolver = $this->getConfigResolver(); - - $sortBy = $request->query->get('sort_by'); - $sortOrder = $request->query->get('sort_order'); - if ($sortBy !== null && $sortOrder !== null) { - $this->tagChildrenAdapter->setSorting($sortBy, $sortOrder); - } - $currentPage = (int) $request->query->get('page'); $pager = $this->createPager( $this->tagChildrenAdapter, @@ -270,6 +266,26 @@ public function updateTagAction(Request $request, Tag $tag, string $languageCode ); } + public function updateTagSortAction(Request $request, Tag $tag): Response + { + if (!$this->isCsrfTokenValid('netgen_tags_admin', (string) ($request->request->get('_csrf_token') ?? ''))) { + $this->addFlashMessage('errors', 'invalid_csrf_token'); + + return $this->redirectToTag($tag); + } + + $sortBy = $request->request->get('sort_by'); + $sortOrder = $request->request->get('sort_order'); + + $tagUpdateStruct = new TagUpdateStruct(); + $tagUpdateStruct->sortField = TagSortField::from((string) $sortBy); + $tagUpdateStruct->sortOrder = TagSortOrder::from((string) $sortOrder); + + $this->tagsService->updateTag($tag, $tagUpdateStruct); + + return $this->redirectToTag($tag); + } + /** * This method is called for delete tag or synonym action. * It shows a confirmation view. diff --git a/bundle/Core/Repository/TagsService.php b/bundle/Core/Repository/TagsService.php index 0574906d..d1cf5f77 100644 --- a/bundle/Core/Repository/TagsService.php +++ b/bundle/Core/Repository/TagsService.php @@ -453,6 +453,8 @@ public function updateTag(Tag $tag, TagUpdateStruct $tagUpdateStruct): Tag $updateStruct->remoteId = trim($tagUpdateStruct->remoteId ?? $spiTag->remoteId); $updateStruct->mainLanguageCode = $mainLanguageCode; $updateStruct->alwaysAvailable = $tagUpdateStruct->alwaysAvailable ?? $spiTag->alwaysAvailable; + $updateStruct->sortField = $tagUpdateStruct->sortField ?? $spiTag->sortField; + $updateStruct->sortOrder = $tagUpdateStruct->sortOrder ?? $spiTag->sortOrder; $this->repository->beginTransaction(); diff --git a/bundle/Resources/config/routing/admin/tag.yaml b/bundle/Resources/config/routing/admin/tag.yaml index 8f06b8cf..6a986862 100644 --- a/bundle/Resources/config/routing/admin/tag.yaml +++ b/bundle/Resources/config/routing/admin/tag.yaml @@ -43,6 +43,11 @@ netgen_tags_admin_tag_update_select: controller: netgen_tags.admin.controller.tag:updateTagSelectAction methods: [GET, POST] +netgen_tags_admin_tag_update_sort: + path: /{tagId}/update/sort + controller: netgen_tags.admin.controller.tag:updateTagSortAction + methods: [POST] + netgen_tags_admin_tag_update: path: /{tagId}/update/{languageCode} controller: netgen_tags.admin.controller.tag:updateTagAction From 2397fccda5943447d763b1521d94a420b9a2530c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Tue, 24 Jun 2025 14:41:57 +0200 Subject: [PATCH 27/45] NGSTACK-906 rename sortField property to sortBy property everywhere --- .../Values/Enums/{TagSortField.php => TagSortBy.php} | 2 +- bundle/API/Repository/Values/Tags/Tag.php | 6 +++--- bundle/API/Repository/Values/Tags/TagStruct.php | 6 +++--- bundle/Controller/Admin/TagController.php | 4 ++-- bundle/Core/Persistence/Legacy/Tags/Mapper.php | 10 +++++----- bundle/Core/Repository/TagsMapper.php | 2 +- bundle/Core/Repository/TagsService.php | 2 +- .../views/admin/tag/sort_children_tags.html.twig | 8 ++++---- bundle/SPI/Persistence/Tags/Tag.php | 4 ++-- bundle/SPI/Persistence/Tags/TagInfo.php | 4 ++-- bundle/SPI/Persistence/Tags/UpdateStruct.php | 6 +++--- 11 files changed, 27 insertions(+), 27 deletions(-) rename bundle/API/Repository/Values/Enums/{TagSortField.php => TagSortBy.php} (88%) diff --git a/bundle/API/Repository/Values/Enums/TagSortField.php b/bundle/API/Repository/Values/Enums/TagSortBy.php similarity index 88% rename from bundle/API/Repository/Values/Enums/TagSortField.php rename to bundle/API/Repository/Values/Enums/TagSortBy.php index b15ae5ea..abddf2c2 100644 --- a/bundle/API/Repository/Values/Enums/TagSortField.php +++ b/bundle/API/Repository/Values/Enums/TagSortBy.php @@ -4,7 +4,7 @@ namespace Netgen\TagsBundle\API\Repository\Values\Enums; -enum TagSortField: string +enum TagSortBy: string { case ID = 'id'; case KEYWORD = 'keyword'; diff --git a/bundle/API/Repository/Values/Tags/Tag.php b/bundle/API/Repository/Values/Tags/Tag.php index 112f066d..2b6f3686 100644 --- a/bundle/API/Repository/Values/Tags/Tag.php +++ b/bundle/API/Repository/Values/Tags/Tag.php @@ -6,7 +6,7 @@ use DateTimeInterface; use Ibexa\Contracts\Core\Repository\Values\ValueObject; -use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortField; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortBy; use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder; use function array_map; @@ -31,7 +31,7 @@ * @property-read string $mainLanguageCode The main language code of the Tag object * @property-read string[] $languageCodes List of languages in this Tag object * @property-read int $priority Tag priority - * @property-read TagSortField $sortField Specifies by which property the child tags should be sorted on + * @property-read TagSortBy $sortBy Specifies by which property the child tags should be sorted on * @property-read TagSortOrder $sortOrder Specifies whether the sort order should be ascending or descending */ final class Tag extends ValueObject @@ -119,7 +119,7 @@ final class Tag extends ValueObject /** * Specifies by which property the child tags should be sorted on. */ - protected TagSortField $sortField; + protected TagSortBy $sortBy; /** * Specifies whether the sort order should be ascending or descending. diff --git a/bundle/API/Repository/Values/Tags/TagStruct.php b/bundle/API/Repository/Values/Tags/TagStruct.php index 5e1754d4..91631aee 100644 --- a/bundle/API/Repository/Values/Tags/TagStruct.php +++ b/bundle/API/Repository/Values/Tags/TagStruct.php @@ -5,7 +5,7 @@ namespace Netgen\TagsBundle\API\Repository\Values\Tags; use Ibexa\Contracts\Core\Repository\Values\ValueObject; -use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortField; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortBy; use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder; use function array_key_exists; @@ -27,12 +27,12 @@ abstract class TagStruct extends ValueObject /** * Specifies by which property the child tags should be sorted on. */ - public TagSortField $sortField; + public ?TagSortBy $sortBy; /** * Specifies whether the sort order should be ascending or descending. */ - public TagSortOrder $sortOrder; + public ?TagSortOrder $sortOrder; /** * Tag keywords in the target languages diff --git a/bundle/Controller/Admin/TagController.php b/bundle/Controller/Admin/TagController.php index b5f2bb6a..9f2384fa 100644 --- a/bundle/Controller/Admin/TagController.php +++ b/bundle/Controller/Admin/TagController.php @@ -7,7 +7,7 @@ use Ibexa\Contracts\Core\Repository\ContentTypeService; use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; use Netgen\TagsBundle\API\Repository\TagsService; -use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortField; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortBy; use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder; use Netgen\TagsBundle\API\Repository\Values\Tags\Tag; use Netgen\TagsBundle\API\Repository\Values\Tags\TagUpdateStruct; @@ -278,7 +278,7 @@ public function updateTagSortAction(Request $request, Tag $tag): Response $sortOrder = $request->request->get('sort_order'); $tagUpdateStruct = new TagUpdateStruct(); - $tagUpdateStruct->sortField = TagSortField::from((string) $sortBy); + $tagUpdateStruct->sortBy = TagSortBy::from((string) $sortBy); $tagUpdateStruct->sortOrder = TagSortOrder::from((string) $sortOrder); $this->tagsService->updateTag($tag, $tagUpdateStruct); diff --git a/bundle/Core/Persistence/Legacy/Tags/Mapper.php b/bundle/Core/Persistence/Legacy/Tags/Mapper.php index ce94ae10..2d911120 100644 --- a/bundle/Core/Persistence/Legacy/Tags/Mapper.php +++ b/bundle/Core/Persistence/Legacy/Tags/Mapper.php @@ -6,7 +6,7 @@ use Ibexa\Contracts\Core\Persistence\Content\Language\Handler as LanguageHandler; use Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator; -use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortField; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortBy; use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder; use Netgen\TagsBundle\SPI\Persistence\Tags\Tag; use Netgen\TagsBundle\SPI\Persistence\Tags\TagInfo; @@ -38,8 +38,8 @@ public function createTagInfoFromRow(array $row): TagInfo $tagInfo->mainLanguageCode = $this->languageHandler->load($row['main_language_id'])->languageCode; $tagInfo->languageIds = $this->languageMaskGenerator->extractLanguageIdsFromMask((int) $row['language_mask']); $tagInfo->priority = (int) $row['priority']; - $tagInfo->sortField = TagSortField::from($row['sortField']); - $tagInfo->sortOrder = TagSortOrder::from($row['sortOrder']); + $tagInfo->sortBy = TagSortBy::from($row['sort_by']); + $tagInfo->sortOrder = TagSortOrder::from($row['sort_order']); return $tagInfo; } @@ -66,8 +66,8 @@ public function extractTagListFromRows(array $rows): array $tag->mainLanguageCode = $this->languageHandler->load($row['main_language_id'])->languageCode; $tag->languageIds = $this->languageMaskGenerator->extractLanguageIdsFromMask((int) $row['language_mask']); $tag->priority = (int) $row['priority']; - $tag->sortField = TagSortField::from($row['sortField']); - $tag->sortOrder = TagSortOrder::from($row['sortOrder']); + $tag->sortBy = TagSortBy::from($row['sort_by']); + $tag->sortOrder = TagSortOrder::from($row['sort_order']); $tagList[$tagId] = $tag; } diff --git a/bundle/Core/Repository/TagsMapper.php b/bundle/Core/Repository/TagsMapper.php index 0fd04a9a..ffde6bc2 100644 --- a/bundle/Core/Repository/TagsMapper.php +++ b/bundle/Core/Repository/TagsMapper.php @@ -68,7 +68,7 @@ public function buildTagDomainList(array $spiTags, array $prioritizedLanguages = 'languageCodes' => $languageCodes, 'prioritizedLanguageCode' => $prioritizedLanguageCode, 'priority' => $spiTag->priority, - 'sortField' => $spiTag->sortField, + 'sortBy' => $spiTag->sortBy, 'sortOrder' => $spiTag->sortOrder, ], ); diff --git a/bundle/Core/Repository/TagsService.php b/bundle/Core/Repository/TagsService.php index d1cf5f77..94c89e61 100644 --- a/bundle/Core/Repository/TagsService.php +++ b/bundle/Core/Repository/TagsService.php @@ -453,7 +453,7 @@ public function updateTag(Tag $tag, TagUpdateStruct $tagUpdateStruct): Tag $updateStruct->remoteId = trim($tagUpdateStruct->remoteId ?? $spiTag->remoteId); $updateStruct->mainLanguageCode = $mainLanguageCode; $updateStruct->alwaysAvailable = $tagUpdateStruct->alwaysAvailable ?? $spiTag->alwaysAvailable; - $updateStruct->sortField = $tagUpdateStruct->sortField ?? $spiTag->sortField; + $updateStruct->sortBy = $tagUpdateStruct->sortBy ?? $spiTag->sortBy; $updateStruct->sortOrder = $tagUpdateStruct->sortOrder ?? $spiTag->sortOrder; $this->repository->beginTransaction(); diff --git a/bundle/Resources/views/admin/tag/sort_children_tags.html.twig b/bundle/Resources/views/admin/tag/sort_children_tags.html.twig index 6e29b78f..f4275ede 100644 --- a/bundle/Resources/views/admin/tag/sort_children_tags.html.twig +++ b/bundle/Resources/views/admin/tag/sort_children_tags.html.twig @@ -5,10 +5,10 @@
    diff --git a/bundle/SPI/Persistence/Tags/Tag.php b/bundle/SPI/Persistence/Tags/Tag.php index a6aaa41f..31ee182e 100644 --- a/bundle/SPI/Persistence/Tags/Tag.php +++ b/bundle/SPI/Persistence/Tags/Tag.php @@ -5,7 +5,7 @@ namespace Netgen\TagsBundle\SPI\Persistence\Tags; use Ibexa\Contracts\Core\Persistence\ValueObject; -use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortField; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortBy; use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder; /** @@ -85,7 +85,7 @@ final class Tag extends ValueObject /** * Specifies by which property the child tags should be sorted on. */ - public TagSortField $sortField; + public TagSortBy $sortBy; /** * Specifies whether the sort order should be ascending or descending. diff --git a/bundle/SPI/Persistence/Tags/TagInfo.php b/bundle/SPI/Persistence/Tags/TagInfo.php index 157267b2..09ce7cac 100644 --- a/bundle/SPI/Persistence/Tags/TagInfo.php +++ b/bundle/SPI/Persistence/Tags/TagInfo.php @@ -5,7 +5,7 @@ namespace Netgen\TagsBundle\SPI\Persistence\Tags; use Ibexa\Contracts\Core\Persistence\ValueObject; -use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortField; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortBy; use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder; /** @@ -77,7 +77,7 @@ final class TagInfo extends ValueObject /** * Specifies by which property the child tags should be sorted on. */ - public TagSortField $sortField; + public TagSortBy $sortBy; /** * Specifies whether the sort order should be ascending or descending. diff --git a/bundle/SPI/Persistence/Tags/UpdateStruct.php b/bundle/SPI/Persistence/Tags/UpdateStruct.php index 05848770..fc9fb2e8 100644 --- a/bundle/SPI/Persistence/Tags/UpdateStruct.php +++ b/bundle/SPI/Persistence/Tags/UpdateStruct.php @@ -5,7 +5,7 @@ namespace Netgen\TagsBundle\SPI\Persistence\Tags; use Ibexa\Contracts\Core\Persistence\ValueObject; -use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortField; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortBy; use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder; /** @@ -39,10 +39,10 @@ final class UpdateStruct extends ValueObject /** * Specifies by which property the child tags should be sorted on. */ - public TagSortField $sortField; + public ?TagSortBy $sortBy; /** * Specifies whether the sort order should be ascending or descending. */ - public TagSortOrder $sortOrder; + public ?TagSortOrder $sortOrder; } From c6c4555727f9347332693c23bfdf57426ff05ba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Tue, 24 Jun 2025 14:43:57 +0200 Subject: [PATCH 28/45] NGSTACK-906 add logic for sorting children by sortBy and sortOrder in DoctrineDatabase class --- .../Legacy/Tags/Gateway/DoctrineDatabase.php | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php b/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php index 870f5ba6..44b5c812 100644 --- a/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php +++ b/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php @@ -128,6 +128,10 @@ public function getFullTagDataByKeywordAndParentId(string $keyword, int $parentI public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true): array { + $tagData = $tagId !== 0 ? $this->getBasicTagData($tagId) : []; + $sortBy = $tagData['sort_by'] ?? 'id'; + $sortOrder = $tagData['sort_order'] ?? 'asc'; + $tagIdsQuery = $this->createTagIdsQuery($translations, $useAlwaysAvailable); $tagIdsQuery->andWhere( $tagIdsQuery->expr()->andX( @@ -137,7 +141,9 @@ public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array ), $tagIdsQuery->expr()->eq('eztags.main_tag_id', 0), ), - )->setParameter('parent_id', $tagId, Types::INTEGER) + ) + ->orderBy('eztags.' . $sortBy, $sortOrder) + ->setParameter('parent_id', $tagId, Types::INTEGER) ->setFirstResult($offset) ->setMaxResults($limit > 0 ? $limit : PHP_INT_MAX); @@ -159,7 +165,8 @@ public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array [':id'], ), ) - ->setParameter('id', $tagIds, Connection::PARAM_INT_ARRAY); + ->orderBy('eztags.' . $sortBy, $sortOrder) + ->setParameter('id', $tagIds, Connection::PARAM_INT_ARRAY); return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); } @@ -444,6 +451,12 @@ public function update(UpdateStruct $updateStruct, int $tagId): void )->set( 'language_mask', ':language_mask', + )->set( + 'sort_by', + ':sort_by', + )->set( + 'sort_order', + ':sort_order', )->where( $query->expr()->eq( 'id', @@ -467,7 +480,9 @@ public function update(UpdateStruct $updateStruct, int $tagId): void is_bool($updateStruct->alwaysAvailable) ? $updateStruct->alwaysAvailable : true, ), Types::INTEGER, - ); + ) + ->setParameter('sort_by', $updateStruct->sortBy->value ?? 'id', Types::STRING) + ->setParameter('sort_order', $updateStruct->sortOrder->value ?? 'asc', Types::STRING); $query->execute(); @@ -929,8 +944,8 @@ private function createTagFindQuery(?array $translations = null, bool $useAlways 'eztags.main_language_id', 'eztags.language_mask', 'eztags.priority', - 'eztags.sortField', - 'eztags.sortOrder', + 'eztags.sort_by', + 'eztags.sort_order', // Tag keywords 'eztags_keyword.keyword', 'eztags_keyword.locale', From 4a5391df7ea1e0794ef0c8ba951b2618835eef16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Tue, 24 Jun 2025 15:24:26 +0200 Subject: [PATCH 29/45] NGSTACK-906 fix tests regarding sortBy and sortOrder properties --- .../FieldType/TagsIntegrationTest.php | 8 +++++++ .../Tags/Gateway/DoctrineDatabaseTest.php | 24 +++++++++---------- .../Persistence/Legacy/Tags/MapperTest.php | 10 ++++++++ tests/_fixtures/schema/schema.mysql.sql | 2 ++ tests/_fixtures/schema/schema.postgresql.sql | 4 +++- tests/_fixtures/schema/schema.sqlite.sql | 4 +++- 6 files changed, 38 insertions(+), 14 deletions(-) diff --git a/tests/API/Repository/FieldType/TagsIntegrationTest.php b/tests/API/Repository/FieldType/TagsIntegrationTest.php index 657a8e84..ad53e66d 100644 --- a/tests/API/Repository/FieldType/TagsIntegrationTest.php +++ b/tests/API/Repository/FieldType/TagsIntegrationTest.php @@ -8,6 +8,8 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Field; use Ibexa\Core\Base\Exceptions\InvalidArgumentType; use Ibexa\Tests\Integration\Core\Repository\FieldType\BaseIntegrationTest; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortBy; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder; use Netgen\TagsBundle\API\Repository\Values\Tags\Tag; use Netgen\TagsBundle\Core\FieldType\Tags\Type; use Netgen\TagsBundle\Core\FieldType\Tags\Value as TagsValue; @@ -254,6 +256,8 @@ private function getTag1(): Tag 'languageCodes' => ['eng-GB'], 'prioritizedLanguageCode' => 'eng-GB', 'priority' => 0, + 'sortBy' => TagSortBy::Id, + 'sortOrder' => TagSortOrder::Ascending, ], ); } @@ -278,6 +282,8 @@ private function getTag2(): Tag 'languageCodes' => ['eng-GB'], 'prioritizedLanguageCode' => 'eng-GB', 'priority' => 0, + 'sortBy' => TagSortBy::Id, + 'sortOrder' => TagSortOrder::Ascending, ], ); } @@ -302,6 +308,8 @@ private function getTag3(): Tag 'languageCodes' => ['eng-GB'], 'prioritizedLanguageCode' => 'eng-GB', 'priority' => 0, + 'sortBy' => TagSortBy::Id, + 'sortOrder' => TagSortOrder::Ascending, ], ); } diff --git a/tests/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabaseTest.php b/tests/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabaseTest.php index 8d84aff7..2261f0c0 100644 --- a/tests/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabaseTest.php +++ b/tests/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabaseTest.php @@ -342,12 +342,12 @@ public function testGetChildren(): void } self::assertCount(6, $data); - self::assertSame(20, $data[0]['id']); - self::assertSame(15, $data[1]['id']); - self::assertSame(72, $data[2]['id']); - self::assertSame(71, $data[3]['id']); - self::assertSame(18, $data[4]['id']); - self::assertSame(19, $data[5]['id']); + self::assertSame(15, $data[0]['id']); + self::assertSame(18, $data[1]['id']); + self::assertSame(19, $data[2]['id']); + self::assertSame(20, $data[3]['id']); + self::assertSame(71, $data[4]['id']); + self::assertSame(72, $data[5]['id']); } /** @@ -373,12 +373,12 @@ public function testGetChildrenWithoutAlwaysAvailable(): void } self::assertCount(6, $data); - self::assertSame(20, $data[0]['id']); - self::assertSame(15, $data[1]['id']); - self::assertSame(72, $data[2]['id']); - self::assertSame(71, $data[3]['id']); - self::assertSame(18, $data[4]['id']); - self::assertSame(19, $data[5]['id']); + self::assertSame(15, $data[0]['id']); + self::assertSame(18, $data[1]['id']); + self::assertSame(19, $data[2]['id']); + self::assertSame(20, $data[3]['id']); + self::assertSame(71, $data[4]['id']); + self::assertSame(72, $data[5]['id']); } /** diff --git a/tests/Core/Persistence/Legacy/Tags/MapperTest.php b/tests/Core/Persistence/Legacy/Tags/MapperTest.php index 53a97cb5..b36b58de 100644 --- a/tests/Core/Persistence/Legacy/Tags/MapperTest.php +++ b/tests/Core/Persistence/Legacy/Tags/MapperTest.php @@ -6,6 +6,8 @@ use Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator; use Ibexa\Tests\Core\Persistence\Legacy\TestCase; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortBy; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder; use Netgen\TagsBundle\Core\Persistence\Legacy\Tags\Mapper; use Netgen\TagsBundle\SPI\Persistence\Tags\Tag; use Netgen\TagsBundle\Tests\Core\Persistence\Legacy\Content\LanguageHandlerMock; @@ -29,6 +31,8 @@ final class MapperTest extends TestCase 'main_language_id' => 8, 'language_mask' => 9, 'priority' => 0, + 'sort_by' => 'id', + 'sort_order' => 'asc', ]; /** @@ -49,6 +53,8 @@ final class MapperTest extends TestCase 'keyword' => 'Croatia', 'locale' => 'eng-GB', 'priority' => 0, + 'sort_by' => 'id', + 'sort_order' => 'asc', ]; /** @@ -68,6 +74,8 @@ final class MapperTest extends TestCase 'mainLanguageCode' => 'eng-GB', 'languageIds' => [8], 'priority' => 0, + 'sortBy' => TagSortBy::Id, + 'sortOrder' => TagSortOrder::Ascending, ]; /** @@ -88,6 +96,8 @@ final class MapperTest extends TestCase 'mainLanguageCode' => 'eng-GB', 'languageIds' => [8], 'priority' => 0, + 'sortBy' => TagSortBy::Id, + 'sortOrder' => TagSortOrder::Ascending, ]; private Mapper $tagsMapper; diff --git a/tests/_fixtures/schema/schema.mysql.sql b/tests/_fixtures/schema/schema.mysql.sql index 9afa0925..ec09cafa 100644 --- a/tests/_fixtures/schema/schema.mysql.sql +++ b/tests/_fixtures/schema/schema.mysql.sql @@ -12,6 +12,8 @@ CREATE TABLE `eztags` ( `main_language_id` int(11) NOT NULL DEFAULT '0', `language_mask` int(11) NOT NULL DEFAULT '0', `priority` int(11) NOT NULL DEFAULT '0', + `sort_by` enum('id', 'keyword', 'modified', 'priority') NOT NULL DEFAULT 'id', + `sort_order` enum('asc', 'desc') NOT NULL DEFAULT 'asc', PRIMARY KEY (`id`), UNIQUE KEY `eztags_remote_id` (`remote_id`), KEY `eztags_keyword` (`keyword`), diff --git a/tests/_fixtures/schema/schema.postgresql.sql b/tests/_fixtures/schema/schema.postgresql.sql index fd2e84ae..f2564294 100644 --- a/tests/_fixtures/schema/schema.postgresql.sql +++ b/tests/_fixtures/schema/schema.postgresql.sql @@ -27,7 +27,9 @@ CREATE TABLE eztags ( remote_id character varying(100) DEFAULT ''::character varying NOT NULL, main_language_id integer DEFAULT 0 NOT NULL, language_mask integer DEFAULT 0 NOT NULL, - priority integer DEFAULT 0 NOT NULL + priority integer DEFAULT 0 NOT NULL, + sort_by enum('id', 'keyword', 'modified', 'priority') DEFAULT 'id' NOT NULL, + sort_order enum('asc', 'desc') DEFAULT 'asc' NOT NULL, ); DROP TABLE IF EXISTS eztags_attribute_link; diff --git a/tests/_fixtures/schema/schema.sqlite.sql b/tests/_fixtures/schema/schema.sqlite.sql index 4a648b36..ddad1e21 100644 --- a/tests/_fixtures/schema/schema.sqlite.sql +++ b/tests/_fixtures/schema/schema.sqlite.sql @@ -11,7 +11,9 @@ CREATE TABLE 'eztags' ( 'remote_id' text(100) NOT NULL DEFAULT '', 'main_language_id' integer NOT NULL DEFAULT 0, 'language_mask' integer NOT NULL DEFAULT 0, - 'priority' integer NOT NULL DEFAULT 0 + 'priority' integer NOT NULL DEFAULT 0, + 'sort_by' text check (sort_by IN ('id', 'keyword', 'modified', 'priority')) NOT NULL DEFAULT 'id', + 'sort_order' text check (sort_order IN ('asc', 'desc')) NOT NULL DEFAULT 'asc' ); DROP TABLE IF EXISTS 'eztags_attribute_link'; From 4224d33ccf899f460ac94d11cac29658ef811637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Tue, 24 Jun 2025 15:45:44 +0200 Subject: [PATCH 30/45] NGSTACK-906 update TagSortBy and TagSortOrder enums, iterate through enum cases in sort_children_tags template and add translation for enums --- bundle/API/Repository/Values/Enums/TagSortBy.php | 8 ++++---- .../API/Repository/Values/Enums/TagSortOrder.php | 4 ++-- .../translations/netgen_tags_admin.en.yml | 10 ++++++++-- .../translations/netgen_tags_admin.fr.yml | 10 ++++++++-- .../views/admin/tag/sort_children_tags.html.twig | 16 ++++++++++------ 5 files changed, 32 insertions(+), 16 deletions(-) diff --git a/bundle/API/Repository/Values/Enums/TagSortBy.php b/bundle/API/Repository/Values/Enums/TagSortBy.php index abddf2c2..af37cdf0 100644 --- a/bundle/API/Repository/Values/Enums/TagSortBy.php +++ b/bundle/API/Repository/Values/Enums/TagSortBy.php @@ -6,8 +6,8 @@ enum TagSortBy: string { - case ID = 'id'; - case KEYWORD = 'keyword'; - case MODIFIED = 'modified'; - case PRIORITY = 'priority'; + case Id = 'id'; + case Keyword = 'keyword'; + case Modified = 'modified'; + case Priority = 'priority'; } diff --git a/bundle/API/Repository/Values/Enums/TagSortOrder.php b/bundle/API/Repository/Values/Enums/TagSortOrder.php index c6d03478..dd1f7bea 100644 --- a/bundle/API/Repository/Values/Enums/TagSortOrder.php +++ b/bundle/API/Repository/Values/Enums/TagSortOrder.php @@ -6,6 +6,6 @@ enum TagSortOrder: string { - case ASC = 'asc'; - case DESC = 'desc'; + case Ascending = 'asc'; + case Descending = 'desc'; } diff --git a/bundle/Resources/translations/netgen_tags_admin.en.yml b/bundle/Resources/translations/netgen_tags_admin.en.yml index 7205e464..31bc96c7 100644 --- a/bundle/Resources/translations/netgen_tags_admin.en.yml +++ b/bundle/Resources/translations/netgen_tags_admin.en.yml @@ -47,8 +47,6 @@ tag.subtree_limitations.no_limitations: 'No subtree limitations' tag.subitems.title: 'Subitems' tag.subitems.sort_by: 'Sort by' tag.subitems.sort_order: 'Sort order' -tag.subitems.sort_order.ascending: 'Ascending' -tag.subitems.sort_order.descending: 'Descending' tag.related_content.title: 'List of content related to tag' tag.related_content.filter.content_type: 'Content type filter' @@ -116,3 +114,11 @@ tag.search.no_results.check_spelling: 'Check spelling of keywords.' tag.search.no_results.change_keywords: 'Try changing some keywords (eg, "car" instead of "cars").' tag.search.no_results.less_specific_keywords: 'Try searching with less specific keywords.' tag.search.no_results.reduce_keywords: 'Reduce number of keywords to get more results.' + +tag.enum.sort_by.id: 'Tag ID' +tag.enum.sort_by.keyword: 'Keyword' +tag.enum.sort_by.modified: 'Modified' +tag.enum.sort_by.priority: 'Priority' + +tag.enum.sort_order.asc: 'Ascending' +tag.enum.sort_order.desc: 'Descending' diff --git a/bundle/Resources/translations/netgen_tags_admin.fr.yml b/bundle/Resources/translations/netgen_tags_admin.fr.yml index 03c1784d..5884bd81 100644 --- a/bundle/Resources/translations/netgen_tags_admin.fr.yml +++ b/bundle/Resources/translations/netgen_tags_admin.fr.yml @@ -47,8 +47,6 @@ tag.subtree_limitations.no_limitations: 'Aucune limite de sous-arbre' tag.subitems.title: 'Sous-éléments' tag.subitems.sort_by: 'Trier par' tag.subitems.sort_order: 'Ordre de tri' -tag.subitems.sort_order.ascending: 'Ascendant' -tag.subitems.sort_order.descending: 'Descendant' tag.related_content.title: 'Liste des contenus liés au tag' @@ -105,3 +103,11 @@ tag.search.no_results.check_spelling: 'Check spelling of keywords.' tag.search.no_results.change_keywords: 'Try changing some keywords (eg, "car" instead of "cars").' tag.search.no_results.less_specific_keywords: 'Try searching with less specific keywords.' tag.search.no_results.reduce_keywords: 'Reduce number of keywords to get more results.' + +tag.enum.sort_by.id: 'Tag ID' +tag.enum.sort_by.keyword: 'Mot-clé' +tag.enum.sort_by.modified: 'Modifié' +tag.enum.sort_by.priority: 'Priorité' + +tag.enum.sort_order.asc: 'Ascendant' +tag.enum.sort_order.desc: 'Descendant' diff --git a/bundle/Resources/views/admin/tag/sort_children_tags.html.twig b/bundle/Resources/views/admin/tag/sort_children_tags.html.twig index f4275ede..64a2d358 100644 --- a/bundle/Resources/views/admin/tag/sort_children_tags.html.twig +++ b/bundle/Resources/views/admin/tag/sort_children_tags.html.twig @@ -5,18 +5,22 @@
    From e16f73f5f80e6e2de49d5556ef63cec7e894c1c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Tue, 24 Jun 2025 15:57:39 +0200 Subject: [PATCH 31/45] NGSTACK-906 throw BadRequestHttpException if sortBy or sortOrder params don't fit the enums when updating children sorting for some tag --- bundle/Controller/Admin/TagController.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bundle/Controller/Admin/TagController.php b/bundle/Controller/Admin/TagController.php index 9f2384fa..968034ed 100644 --- a/bundle/Controller/Admin/TagController.php +++ b/bundle/Controller/Admin/TagController.php @@ -22,6 +22,7 @@ use Pagerfanta\Adapter\AdapterInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use function count; use function in_array; @@ -278,8 +279,10 @@ public function updateTagSortAction(Request $request, Tag $tag): Response $sortOrder = $request->request->get('sort_order'); $tagUpdateStruct = new TagUpdateStruct(); - $tagUpdateStruct->sortBy = TagSortBy::from((string) $sortBy); - $tagUpdateStruct->sortOrder = TagSortOrder::from((string) $sortOrder); + $tagUpdateStruct->sortBy = TagSortBy::tryFrom((string) $sortBy) + ?? throw new BadRequestHttpException('Invalid enum value for sortBy when trying to update children sorting'); + $tagUpdateStruct->sortOrder = TagSortOrder::tryFrom((string) $sortOrder) + ?? throw new BadRequestHttpException('Invalid enum value for sortOrder when trying to update children sorting'); $this->tagsService->updateTag($tag, $tagUpdateStruct); From dba4689f796c3d88d9978845ddd9dc0e1baae79a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Wed, 25 Jun 2025 09:50:10 +0200 Subject: [PATCH 32/45] NGSTACK-906 change sort_by and sort_order column types from enums to varchar in database --- bundle/Resources/sql/mysql/schema.sql | 2 ++ bundle/Resources/sql/postgresql/schema.sql | 2 ++ tests/_fixtures/schema/schema.mysql.sql | 4 ++-- tests/_fixtures/schema/schema.postgresql.sql | 4 ++-- tests/_fixtures/schema/schema.sqlite.sql | 4 ++-- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/bundle/Resources/sql/mysql/schema.sql b/bundle/Resources/sql/mysql/schema.sql index 10263532..262977a6 100644 --- a/bundle/Resources/sql/mysql/schema.sql +++ b/bundle/Resources/sql/mysql/schema.sql @@ -10,6 +10,8 @@ CREATE TABLE `eztags` ( `main_language_id` int(11) NOT NULL default '0', `language_mask` int(11) NOT NULL default '0', `priority` int(11) NOT NULL default '0', + `sort_by` varchar(100) NOT NULL default '', + `sort_order` varchar(100) NOT NULL default '', PRIMARY KEY ( `id` ), KEY `idx_eztags_keyword` ( `keyword`(191) ), KEY `idx_eztags_keyword_id` ( `keyword`(191), `id` ), diff --git a/bundle/Resources/sql/postgresql/schema.sql b/bundle/Resources/sql/postgresql/schema.sql index 21fa5720..e0c19fc4 100644 --- a/bundle/Resources/sql/postgresql/schema.sql +++ b/bundle/Resources/sql/postgresql/schema.sql @@ -11,6 +11,8 @@ CREATE TABLE eztags ( main_language_id integer not null default 0, language_mask integer not null default 0, priority integer not null default 0, + sort_by varchar(100) NOT NULL default '', + sort_order varchar(100) NOT NULL default '', PRIMARY KEY (id), CONSTRAINT idx_eztags_remote_id UNIQUE (remote_id) ); diff --git a/tests/_fixtures/schema/schema.mysql.sql b/tests/_fixtures/schema/schema.mysql.sql index ec09cafa..ef376606 100644 --- a/tests/_fixtures/schema/schema.mysql.sql +++ b/tests/_fixtures/schema/schema.mysql.sql @@ -12,8 +12,8 @@ CREATE TABLE `eztags` ( `main_language_id` int(11) NOT NULL DEFAULT '0', `language_mask` int(11) NOT NULL DEFAULT '0', `priority` int(11) NOT NULL DEFAULT '0', - `sort_by` enum('id', 'keyword', 'modified', 'priority') NOT NULL DEFAULT 'id', - `sort_order` enum('asc', 'desc') NOT NULL DEFAULT 'asc', + `sort_by` varchar(100) NOT NULL DEFAULT '', + `sort_order` varchar(100) NOT NULL DEFAULT '', PRIMARY KEY (`id`), UNIQUE KEY `eztags_remote_id` (`remote_id`), KEY `eztags_keyword` (`keyword`), diff --git a/tests/_fixtures/schema/schema.postgresql.sql b/tests/_fixtures/schema/schema.postgresql.sql index f2564294..3effe3b5 100644 --- a/tests/_fixtures/schema/schema.postgresql.sql +++ b/tests/_fixtures/schema/schema.postgresql.sql @@ -28,8 +28,8 @@ CREATE TABLE eztags ( main_language_id integer DEFAULT 0 NOT NULL, language_mask integer DEFAULT 0 NOT NULL, priority integer DEFAULT 0 NOT NULL, - sort_by enum('id', 'keyword', 'modified', 'priority') DEFAULT 'id' NOT NULL, - sort_order enum('asc', 'desc') DEFAULT 'asc' NOT NULL, + sort_by character varying(100) DEFAULT ''::character varying NOT NULL, + sort_order character varying(100) DEFAULT ''::character varying NOT NULL, ); DROP TABLE IF EXISTS eztags_attribute_link; diff --git a/tests/_fixtures/schema/schema.sqlite.sql b/tests/_fixtures/schema/schema.sqlite.sql index ddad1e21..cec73f99 100644 --- a/tests/_fixtures/schema/schema.sqlite.sql +++ b/tests/_fixtures/schema/schema.sqlite.sql @@ -12,8 +12,8 @@ CREATE TABLE 'eztags' ( 'main_language_id' integer NOT NULL DEFAULT 0, 'language_mask' integer NOT NULL DEFAULT 0, 'priority' integer NOT NULL DEFAULT 0, - 'sort_by' text check (sort_by IN ('id', 'keyword', 'modified', 'priority')) NOT NULL DEFAULT 'id', - 'sort_order' text check (sort_order IN ('asc', 'desc')) NOT NULL DEFAULT 'asc' + 'sort_by' text(100) NOT NULL DEFAULT '', + 'sort_order' text(100) NOT NULL DEFAULT '' ); DROP TABLE IF EXISTS 'eztags_attribute_link'; From a6100aeaa6c5f1cbe7ea0315d06faf4cf6b6cfb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Wed, 25 Jun 2025 10:17:49 +0200 Subject: [PATCH 33/45] NGSTACK-906 add config params for sorting all child tags and for sorting specifically root tag children --- bundle/DependencyInjection/Configuration.php | 31 +++++++++++++++++++ .../NetgenTagsExtension.php | 6 ++++ 2 files changed, 37 insertions(+) diff --git a/bundle/DependencyInjection/Configuration.php b/bundle/DependencyInjection/Configuration.php index dca24605..b89acfc0 100644 --- a/bundle/DependencyInjection/Configuration.php +++ b/bundle/DependencyInjection/Configuration.php @@ -155,6 +155,37 @@ public function getConfigTreeBuilder(): TreeBuilder ->defaultValue(25) ->end() ->end() + ->end() + ->arrayNode('sort') + ->addDefaultsIfNotSet() + ->children() + ->enumNode('by') + ->info('Set the default property by which all child tags will be sorted by') + ->values(['id', 'keyword', 'modified', 'priority']) + ->defaultValue('id') + ->end() + ->enumNode('order') + ->info('Set the default sorting order by which all child tags will be ordered by') + ->values(['asc', 'desc']) + ->defaultValue('asc') + ->end() + ->arrayNode('root') + ->addDefaultsIfNotSet() + ->info('Set the default property and order by which the root tag children will be sorted') + ->children() + ->enumNode('by') + ->info('Set the default property by which root tag children will be sorted by') + ->values(['id', 'keyword', 'modified', 'priority']) + ->defaultValue('id') + ->end() + ->enumNode('order') + ->info('Set the default sorting order by which root tag children will be ordered by') + ->values(['asc', 'desc']) + ->defaultValue('asc') + ->end() + ->end() + ->end() + ->end() ->end(); return $treeBuilder; diff --git a/bundle/DependencyInjection/NetgenTagsExtension.php b/bundle/DependencyInjection/NetgenTagsExtension.php index b2c59365..9ad147f7 100644 --- a/bundle/DependencyInjection/NetgenTagsExtension.php +++ b/bundle/DependencyInjection/NetgenTagsExtension.php @@ -121,6 +121,12 @@ static function (array $config, string $scope, ContextualizerInterface $c): void $c->setContextualParameter('admin.tree_limit', $scope, $config['admin']['tree_limit']); $c->setContextualParameter('admin.related_content_limit', $scope, $config['admin']['related_content_limit']); $c->setContextualParameter('field.autocomplete_limit', $scope, $config['field']['autocomplete_limit']); + + $c->setContextualParameter('sort.by', $scope, $config['sort']['by']); + $c->setContextualParameter('sort.order', $scope, $config['sort']['order']); + + $c->setContextualParameter('sort.root.by', $scope, $config['sort']['root']['by']); + $c->setContextualParameter('sort.root.order', $scope, $config['sort']['root']['order']); }, ); From 5733962fec0a17e6dd707e7668d5821e458bd4b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Wed, 25 Jun 2025 10:21:38 +0200 Subject: [PATCH 34/45] NGSTACK-906 update Mapper and DoctrineDatabase Gateway with logic for determining sortBy and sortOrder properties --- .../Legacy/Tags/Gateway/DoctrineDatabase.php | 27 +++++++++----- .../Core/Persistence/Legacy/Tags/Mapper.php | 36 ++++++++++++++++--- bundle/Resources/config/storage/doctrine.yaml | 2 ++ 3 files changed, 52 insertions(+), 13 deletions(-) diff --git a/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php b/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php index 44b5c812..2aad0eb2 100644 --- a/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php +++ b/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php @@ -9,8 +9,11 @@ use Doctrine\DBAL\Query\QueryBuilder; use Doctrine\DBAL\Types\Types; use Ibexa\Contracts\Core\Persistence\Content\Language\Handler as LanguageHandler; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; use Ibexa\Core\Base\Exceptions\NotFoundException; use Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortBy; +use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder; use Netgen\TagsBundle\Core\Persistence\Legacy\Tags\Gateway; use Netgen\TagsBundle\SPI\Persistence\Tags\CreateStruct; use Netgen\TagsBundle\SPI\Persistence\Tags\SynonymCreateStruct; @@ -35,9 +38,10 @@ final class DoctrineDatabase extends Gateway { public function __construct( - private Connection $connection, - private LanguageHandler $languageHandler, - private LanguageMaskGenerator $languageMaskGenerator, + private readonly Connection $connection, + private readonly LanguageHandler $languageHandler, + private readonly LanguageMaskGenerator $languageMaskGenerator, + private readonly ConfigResolverInterface $configResolver, ) {} public function getBasicTagData(int $tagId): array @@ -128,9 +132,16 @@ public function getFullTagDataByKeywordAndParentId(string $keyword, int $parentI public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $translations = null, bool $useAlwaysAvailable = true): array { - $tagData = $tagId !== 0 ? $this->getBasicTagData($tagId) : []; - $sortBy = $tagData['sort_by'] ?? 'id'; - $sortOrder = $tagData['sort_order'] ?? 'asc'; + if ($tagId === 0) { + $sortBy = TagSortBy::from($this->configResolver->getParameter('sort.root.by', 'netgen_tags')); + $sortOrder = TagSortOrder::from($this->configResolver->getParameter('sort.root.order', 'netgen_tags')); + } else { + $tagData = $this->getBasicTagData($tagId); + $sortBy = TagSortBy::tryFrom($tagData['sort_by']) + ?? TagSortBy::from($this->configResolver->getParameter('sort.by', 'netgen_tags')); + $sortOrder = TagSortOrder::tryFrom($tagData['sort_order']) + ?? TagSortOrder::from($this->configResolver->getParameter('sort.order', 'netgen_tags')); + } $tagIdsQuery = $this->createTagIdsQuery($translations, $useAlwaysAvailable); $tagIdsQuery->andWhere( @@ -142,7 +153,7 @@ public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $tagIdsQuery->expr()->eq('eztags.main_tag_id', 0), ), ) - ->orderBy('eztags.' . $sortBy, $sortOrder) + ->orderBy('eztags.' . $sortBy->value, $sortOrder->value) ->setParameter('parent_id', $tagId, Types::INTEGER) ->setFirstResult($offset) ->setMaxResults($limit > 0 ? $limit : PHP_INT_MAX); @@ -165,7 +176,7 @@ public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array [':id'], ), ) - ->orderBy('eztags.' . $sortBy, $sortOrder) + ->orderBy('eztags.' . $sortBy->value, $sortOrder->value) ->setParameter('id', $tagIds, Connection::PARAM_INT_ARRAY); return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); diff --git a/bundle/Core/Persistence/Legacy/Tags/Mapper.php b/bundle/Core/Persistence/Legacy/Tags/Mapper.php index 2d911120..a373f4ec 100644 --- a/bundle/Core/Persistence/Legacy/Tags/Mapper.php +++ b/bundle/Core/Persistence/Legacy/Tags/Mapper.php @@ -5,6 +5,7 @@ namespace Netgen\TagsBundle\Core\Persistence\Legacy\Tags; use Ibexa\Contracts\Core\Persistence\Content\Language\Handler as LanguageHandler; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; use Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator; use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortBy; use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder; @@ -18,7 +19,11 @@ */ class Mapper { - public function __construct(private LanguageHandler $languageHandler, private LanguageMaskGenerator $languageMaskGenerator) {} + public function __construct( + private readonly LanguageHandler $languageHandler, + private readonly LanguageMaskGenerator $languageMaskGenerator, + private readonly ConfigResolverInterface $configResolver, + ) {} /** * Creates a tag from a $data row. @@ -38,8 +43,18 @@ public function createTagInfoFromRow(array $row): TagInfo $tagInfo->mainLanguageCode = $this->languageHandler->load($row['main_language_id'])->languageCode; $tagInfo->languageIds = $this->languageMaskGenerator->extractLanguageIdsFromMask((int) $row['language_mask']); $tagInfo->priority = (int) $row['priority']; - $tagInfo->sortBy = TagSortBy::from($row['sort_by']); - $tagInfo->sortOrder = TagSortOrder::from($row['sort_order']); + + if ((int) $row['id'] === 0) { + $tagInfo->sortBy = TagSortBy::tryFrom($row['sort_by']) + ?? TagSortBy::from($this->configResolver->getParameter('sort.root.by', 'netgen_tags')); + $tagInfo->sortOrder = TagSortOrder::tryFrom($row['sort_order']) + ?? TagSortOrder::from($this->configResolver->getParameter('sort.root.order', 'netgen_tags')); + } else { + $tagInfo->sortBy = TagSortBy::tryFrom($row['sort_by']) + ?? TagSortBy::from($this->configResolver->getParameter('sort.by', 'netgen_tags')); + $tagInfo->sortOrder = TagSortOrder::tryFrom($row['sort_order']) + ?? TagSortOrder::from($this->configResolver->getParameter('sort.order', 'netgen_tags')); + } return $tagInfo; } @@ -66,8 +81,19 @@ public function extractTagListFromRows(array $rows): array $tag->mainLanguageCode = $this->languageHandler->load($row['main_language_id'])->languageCode; $tag->languageIds = $this->languageMaskGenerator->extractLanguageIdsFromMask((int) $row['language_mask']); $tag->priority = (int) $row['priority']; - $tag->sortBy = TagSortBy::from($row['sort_by']); - $tag->sortOrder = TagSortOrder::from($row['sort_order']); + + if ((int) $row['id'] === 0) { + $tag->sortBy = TagSortBy::tryFrom($row['sort_by']) + ?? TagSortBy::from($this->configResolver->getParameter('sort.root.by', 'netgen_tags')); + $tag->sortOrder = TagSortOrder::tryFrom($row['sort_order']) + ?? TagSortOrder::from($this->configResolver->getParameter('sort.root.order', 'netgen_tags')); + } else { + $tag->sortBy = TagSortBy::tryFrom($row['sort_by']) + ?? TagSortBy::from($this->configResolver->getParameter('sort.by', 'netgen_tags')); + $tag->sortOrder = TagSortOrder::tryFrom($row['sort_order']) + ?? TagSortOrder::from($this->configResolver->getParameter('sort.order', 'netgen_tags')); + } + $tagList[$tagId] = $tag; } diff --git a/bundle/Resources/config/storage/doctrine.yaml b/bundle/Resources/config/storage/doctrine.yaml index 6cefadbb..3581403b 100644 --- a/bundle/Resources/config/storage/doctrine.yaml +++ b/bundle/Resources/config/storage/doctrine.yaml @@ -15,6 +15,7 @@ services: arguments: - "@Ibexa\\Contracts\\Core\\Persistence\\Content\\Language\\Handler" - "@Ibexa\\Core\\Persistence\\Legacy\\Content\\Language\\MaskGenerator" + - "@ibexa.config.resolver" netgen_tags.api.storage_engine.legacy.handler.tags.gateway.inner: class: Netgen\TagsBundle\Core\Persistence\Legacy\Tags\Gateway\DoctrineDatabase @@ -22,6 +23,7 @@ services: - "@ibexa.persistence.connection" - "@Ibexa\\Contracts\\Core\\Persistence\\Content\\Language\\Handler" - "@Ibexa\\Core\\Persistence\\Legacy\\Content\\Language\\MaskGenerator" + - "@ibexa.config.resolver" netgen_tags.api.storage_engine.legacy.handler.tags.gateway.exception_conversion: class: Netgen\TagsBundle\Core\Persistence\Legacy\Tags\Gateway\ExceptionConversion From 54792579a4d616a2fb0a5925d5527cb5b1513ca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Wed, 25 Jun 2025 12:25:18 +0200 Subject: [PATCH 35/45] NGSTACK-906 add support for 'sort' config params in tests --- tests/API/Repository/SetupFactory/Legacy.php | 14 +++++++++++++ .../Tags/Gateway/DoctrineDatabaseTest.php | 11 ++++++++++ .../Persistence/Legacy/Tags/MapperTest.php | 11 ++++++++++ .../Legacy/Tags/TagsHandlerTest.php | 21 +++++++++++++++++++ tests/settings/integration/legacy.yaml | 12 ++++++++++- 5 files changed, 68 insertions(+), 1 deletion(-) diff --git a/tests/API/Repository/SetupFactory/Legacy.php b/tests/API/Repository/SetupFactory/Legacy.php index 8dc2b43b..17d022f7 100644 --- a/tests/API/Repository/SetupFactory/Legacy.php +++ b/tests/API/Repository/SetupFactory/Legacy.php @@ -54,17 +54,31 @@ public function getTagsService(bool $initializeFromScratch = true): APITagsServi /** @var \Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator $languageMaskGenerator */ $languageMaskGenerator = $this->getServiceContainer()->get('netgen_tags.ibexa.persistence.legacy.language.mask_generator'); + /** @var \Netgen\TagsBundle\Tests\Stubs\ConfigResolverStub $configResolver */ + $configResolver = $this->getServiceContainer()->get('ibexa.config.resolver'); + + $parameters = [ + 'netgen_tags' => [ + 'sort.by' => 'id', + 'sort.order' => 'asc', + 'sort.root.id' => 'keyword', + 'sort.root.order' => 'desc', + ], + ]; + $tagsHandler = new Handler( new ExceptionConversion( new DoctrineDatabase( $this->getDatabaseConnection(), $languageHandler, $languageMaskGenerator, + $configResolver, ), ), new Mapper( $languageHandler, $languageMaskGenerator, + $configResolver, ), ); diff --git a/tests/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabaseTest.php b/tests/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabaseTest.php index 2261f0c0..6b4aec94 100644 --- a/tests/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabaseTest.php +++ b/tests/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabaseTest.php @@ -14,6 +14,7 @@ use Netgen\TagsBundle\SPI\Persistence\Tags\SynonymCreateStruct; use Netgen\TagsBundle\SPI\Persistence\Tags\UpdateStruct; use Netgen\TagsBundle\Tests\Core\Persistence\Legacy\Content\LanguageHandlerMock; +use Netgen\TagsBundle\Tests\Stubs\ConfigResolverStub; use function array_filter; use function file_get_contents; @@ -831,10 +832,20 @@ private function getTagsGateway(): DoctrineDatabase $languageHandlerMock = (new LanguageHandlerMock())($this); + $parameters = [ + 'netgen_tags' => [ + 'sort.by' => 'id', + 'sort.order' => 'asc', + 'sort.root.id' => 'keyword', + 'sort.root.order' => 'desc', + ], + ]; + return new DoctrineDatabase( $dbConnection, $languageHandlerMock, new MaskGenerator($languageHandlerMock), + new ConfigResolverStub($parameters), ); } diff --git a/tests/Core/Persistence/Legacy/Tags/MapperTest.php b/tests/Core/Persistence/Legacy/Tags/MapperTest.php index b36b58de..7e3d482c 100644 --- a/tests/Core/Persistence/Legacy/Tags/MapperTest.php +++ b/tests/Core/Persistence/Legacy/Tags/MapperTest.php @@ -11,6 +11,7 @@ use Netgen\TagsBundle\Core\Persistence\Legacy\Tags\Mapper; use Netgen\TagsBundle\SPI\Persistence\Tags\Tag; use Netgen\TagsBundle\Tests\Core\Persistence\Legacy\Content\LanguageHandlerMock; +use Netgen\TagsBundle\Tests\Stubs\ConfigResolverStub; final class MapperTest extends TestCase { @@ -163,9 +164,19 @@ private function getMapper(): Mapper { $languageHandlerMock = (new LanguageHandlerMock())($this); + $parameters = [ + 'netgen_tags' => [ + 'sort.by' => 'id', + 'sort.order' => 'asc', + 'sort.root.id' => 'keyword', + 'sort.root.order' => 'desc', + ], + ]; + return new Mapper( $languageHandlerMock, new MaskGenerator($languageHandlerMock), + new ConfigResolverStub($parameters), ); } } diff --git a/tests/Core/Persistence/Legacy/Tags/TagsHandlerTest.php b/tests/Core/Persistence/Legacy/Tags/TagsHandlerTest.php index cc388a8a..e58703dc 100644 --- a/tests/Core/Persistence/Legacy/Tags/TagsHandlerTest.php +++ b/tests/Core/Persistence/Legacy/Tags/TagsHandlerTest.php @@ -18,6 +18,7 @@ use Netgen\TagsBundle\SPI\Persistence\Tags\TagInfo; use Netgen\TagsBundle\SPI\Persistence\Tags\UpdateStruct; use Netgen\TagsBundle\Tests\Core\Persistence\Legacy\Content\LanguageHandlerMock; +use Netgen\TagsBundle\Tests\Stubs\ConfigResolverStub; use PHPUnit\Framework\MockObject\MockObject; final class TagsHandlerTest extends TestCase @@ -963,11 +964,21 @@ private function getTagsHandler(): HandlerInterface $languageHandlerMock = (new LanguageHandlerMock())($this); + $parameters = [ + 'netgen_tags' => [ + 'sort.by' => 'id', + 'sort.order' => 'asc', + 'sort.root.id' => 'keyword', + 'sort.root.order' => 'desc', + ], + ]; + $this->mapper = $this->getMockBuilder(Mapper::class) ->setConstructorArgs( [ $languageHandlerMock, new MaskGenerator($languageHandlerMock), + new ConfigResolverStub($parameters), ], )->getMock(); @@ -980,11 +991,21 @@ private function getMockedTagsHandler(array $mockedMethods): Handler&MockObject $languageHandlerMock = (new LanguageHandlerMock())($this); + $parameters = [ + 'netgen_tags' => [ + 'sort.by' => 'id', + 'sort.order' => 'asc', + 'sort.root.id' => 'keyword', + 'sort.root.order' => 'desc', + ], + ]; + $this->mapper = $this->getMockBuilder(Mapper::class) ->setConstructorArgs( [ $languageHandlerMock, new MaskGenerator($languageHandlerMock), + new ConfigResolverStub($parameters), ], )->getMock(); diff --git a/tests/settings/integration/legacy.yaml b/tests/settings/integration/legacy.yaml index a854508a..4bccdcef 100644 --- a/tests/settings/integration/legacy.yaml +++ b/tests/settings/integration/legacy.yaml @@ -6,7 +6,17 @@ services: class: Netgen\TagsBundle\Tests\Stubs\ConfigResolverStub arguments: - - netgen_tags: { edit_views: { default: { identifier: Default } } } + netgen_tags: { + edit_views: { + default: { + identifier: Default + } + }, + sort.by: 'id', + sort.order: 'asc', + sort.root.by: 'keyword', + sort.root.order: 'desc', + } ibexa.site_access.config: { user_content_type_identifier: ['user'] } netgen_tags.field_type.eztags: From 148a3c3edeba75fe3c560a7ed4669bc96edae44c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Wed, 25 Jun 2025 13:59:43 +0200 Subject: [PATCH 36/45] NGSTACK-906 add ability to modify 'priority' property when editing a tag and also show tag priority below the tag title --- bundle/API/Repository/Values/Tags/TagStruct.php | 7 +++++++ bundle/Controller/Admin/TagController.php | 1 + .../Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php | 8 ++++++-- bundle/Core/Repository/TagsService.php | 1 + bundle/Form/DataMapper/TagUpdateStructDataMapper.php | 2 ++ bundle/Form/Type/TagType.php | 9 +++++++++ bundle/Resources/views/admin/tag/show.html.twig | 1 + bundle/SPI/Persistence/Tags/UpdateStruct.php | 7 +++++++ 8 files changed, 34 insertions(+), 2 deletions(-) diff --git a/bundle/API/Repository/Values/Tags/TagStruct.php b/bundle/API/Repository/Values/Tags/TagStruct.php index 91631aee..f2c06867 100644 --- a/bundle/API/Repository/Values/Tags/TagStruct.php +++ b/bundle/API/Repository/Values/Tags/TagStruct.php @@ -34,6 +34,13 @@ abstract class TagStruct extends ValueObject */ public ?TagSortOrder $sortOrder; + /** + * Tag priority. + * + * Position of the Tag among its siblings when sorted by priority. + */ + public ?int $priority; + /** * Tag keywords in the target languages * Eg. array( "cro-HR" => "Hrvatska", "eng-GB" => "Croatia" ). diff --git a/bundle/Controller/Admin/TagController.php b/bundle/Controller/Admin/TagController.php index 968034ed..93072315 100644 --- a/bundle/Controller/Admin/TagController.php +++ b/bundle/Controller/Admin/TagController.php @@ -233,6 +233,7 @@ public function updateTagAction(Request $request, Tag $tag, string $languageCode $tagUpdateStruct = $this->tagsService->newTagUpdateStruct(); $tagUpdateStruct->remoteId = $tag->remoteId; $tagUpdateStruct->alwaysAvailable = $tag->alwaysAvailable; + $tagUpdateStruct->priority = $tag->priority; foreach ($tag->keywords as $keywordLanguageCode => $keyword) { $tagUpdateStruct->setKeyword($keyword ?? '', $keywordLanguageCode); diff --git a/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php b/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php index 2aad0eb2..2016ee62 100644 --- a/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php +++ b/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php @@ -462,6 +462,9 @@ public function update(UpdateStruct $updateStruct, int $tagId): void )->set( 'language_mask', ':language_mask', + )->set( + 'priority', + ':priority', )->set( 'sort_by', ':sort_by', @@ -492,8 +495,9 @@ public function update(UpdateStruct $updateStruct, int $tagId): void ), Types::INTEGER, ) - ->setParameter('sort_by', $updateStruct->sortBy->value ?? 'id', Types::STRING) - ->setParameter('sort_order', $updateStruct->sortOrder->value ?? 'asc', Types::STRING); + ->setParameter('priority', $updateStruct->priority, Types::INTEGER) + ->setParameter('sort_by', $updateStruct->sortBy->value, Types::STRING) + ->setParameter('sort_order', $updateStruct->sortOrder->value, Types::STRING); $query->execute(); diff --git a/bundle/Core/Repository/TagsService.php b/bundle/Core/Repository/TagsService.php index 94c89e61..2379dd14 100644 --- a/bundle/Core/Repository/TagsService.php +++ b/bundle/Core/Repository/TagsService.php @@ -453,6 +453,7 @@ public function updateTag(Tag $tag, TagUpdateStruct $tagUpdateStruct): Tag $updateStruct->remoteId = trim($tagUpdateStruct->remoteId ?? $spiTag->remoteId); $updateStruct->mainLanguageCode = $mainLanguageCode; $updateStruct->alwaysAvailable = $tagUpdateStruct->alwaysAvailable ?? $spiTag->alwaysAvailable; + $updateStruct->priority = $tagUpdateStruct->priority ?? $spiTag->priority; $updateStruct->sortBy = $tagUpdateStruct->sortBy ?? $spiTag->sortBy; $updateStruct->sortOrder = $tagUpdateStruct->sortOrder ?? $spiTag->sortOrder; diff --git a/bundle/Form/DataMapper/TagUpdateStructDataMapper.php b/bundle/Form/DataMapper/TagUpdateStructDataMapper.php index df5ca830..19990507 100644 --- a/bundle/Form/DataMapper/TagUpdateStructDataMapper.php +++ b/bundle/Form/DataMapper/TagUpdateStructDataMapper.php @@ -25,6 +25,7 @@ public function mapDataToForms(mixed $viewData, Traversable $forms): void $forms['keyword']->setData($viewData->getKeyword($this->languageCode)); $forms['alwaysAvailable']->setData($viewData->alwaysAvailable); $forms['remoteId']->setData($viewData->remoteId); + $forms['priority']->setData($viewData->priority); } public function mapFormsToData(Traversable $forms, mixed &$viewData): void @@ -43,5 +44,6 @@ public function mapFormsToData(Traversable $forms, mixed &$viewData): void $viewData->alwaysAvailable = $forms['alwaysAvailable']->getData(); $viewData->remoteId = $forms['remoteId']->getData(); + $viewData->priority = $forms['priority']->getData(); } } diff --git a/bundle/Form/Type/TagType.php b/bundle/Form/Type/TagType.php index ba522245..7d4a2d83 100644 --- a/bundle/Form/Type/TagType.php +++ b/bundle/Form/Type/TagType.php @@ -5,6 +5,7 @@ namespace Netgen\TagsBundle\Form\Type; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; +use Symfony\Component\Form\Extension\Core\Type\IntegerType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; @@ -35,6 +36,14 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'label' => 'tag.remote_id', 'required' => false, ], + ) + ->add( + 'priority', + IntegerType::class, + [ + 'label' => 'tag.priority', + 'required' => false, + ], ); } } diff --git a/bundle/Resources/views/admin/tag/show.html.twig b/bundle/Resources/views/admin/tag/show.html.twig index 0cdebad0..1fb77c03 100644 --- a/bundle/Resources/views/admin/tag/show.html.twig +++ b/bundle/Resources/views/admin/tag/show.html.twig @@ -13,6 +13,7 @@ {{ 'tag.tag_id'|trans }}: {{ tag.id }} {{ 'tag.remote_id'|trans }}: {{ tag.remoteId }} {{ 'tag.modified'|trans }}: {{ tag.modificationDate|date }} + {{ 'tag.priority'|trans }}: {{ tag.priority }} {% include '@NetgenTags/admin/flash_messages.html.twig' %} diff --git a/bundle/SPI/Persistence/Tags/UpdateStruct.php b/bundle/SPI/Persistence/Tags/UpdateStruct.php index fc9fb2e8..38c00161 100644 --- a/bundle/SPI/Persistence/Tags/UpdateStruct.php +++ b/bundle/SPI/Persistence/Tags/UpdateStruct.php @@ -36,6 +36,13 @@ final class UpdateStruct extends ValueObject */ public ?bool $alwaysAvailable; + /** + * Tag priority. + * + * Position of the Tag among its siblings when sorted by priority. + */ + public ?int $priority; + /** * Specifies by which property the child tags should be sorted on. */ From 370456611a5713c932d0cc35e18d1295dde6a605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Wed, 25 Jun 2025 14:08:54 +0200 Subject: [PATCH 37/45] NGSTACK-906 fix the update() method in DoctrineDatabase and the test for it --- .../Legacy/Tags/Gateway/DoctrineDatabase.php | 12 ++++++++++-- .../Legacy/Tags/Gateway/DoctrineDatabaseTest.php | 3 +++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php b/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php index 2016ee62..29d4ef64 100644 --- a/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php +++ b/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php @@ -496,8 +496,16 @@ public function update(UpdateStruct $updateStruct, int $tagId): void Types::INTEGER, ) ->setParameter('priority', $updateStruct->priority, Types::INTEGER) - ->setParameter('sort_by', $updateStruct->sortBy->value, Types::STRING) - ->setParameter('sort_order', $updateStruct->sortOrder->value, Types::STRING); + ->setParameter( + 'sort_by', + $updateStruct->sortBy->value ?? $this->configResolver->getParameter('sort.by', 'netgen_tags'), + Types::STRING, + ) + ->setParameter( + 'sort_order', + $updateStruct->sortOrder->value ?? $this->configResolver->getParameter('sort.order', 'netgen_tags'), + Types::STRING, + ); $query->execute(); diff --git a/tests/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabaseTest.php b/tests/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabaseTest.php index 6b4aec94..c4ea08c2 100644 --- a/tests/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabaseTest.php +++ b/tests/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabaseTest.php @@ -650,6 +650,9 @@ public function testUpdate(): void 'remoteId' => 'updatedRemoteId', 'mainLanguageCode' => 'eng-US', 'alwaysAvailable' => true, + 'priority' => 5, + 'sortBy' => null, + 'sortOrder' => null, ], ), 40, From 59beb45a538d7bdbc0523b72afb82741249a4d56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Wed, 25 Jun 2025 16:05:23 +0200 Subject: [PATCH 38/45] NGSTACK-906 update 'sortBy' and 'sortOrder' properties to be null on Tag objects and in the database --- bundle/API/Repository/Values/Tags/Tag.php | 4 ++-- bundle/Resources/sql/mysql/schema.sql | 4 ++-- bundle/Resources/sql/postgresql/schema.sql | 4 ++-- bundle/SPI/Persistence/Tags/Tag.php | 4 ++-- bundle/SPI/Persistence/Tags/TagInfo.php | 4 ++-- tests/_fixtures/schema/schema.mysql.sql | 4 ++-- tests/_fixtures/schema/schema.postgresql.sql | 4 ++-- tests/_fixtures/schema/schema.sqlite.sql | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/bundle/API/Repository/Values/Tags/Tag.php b/bundle/API/Repository/Values/Tags/Tag.php index 2b6f3686..0b66a19f 100644 --- a/bundle/API/Repository/Values/Tags/Tag.php +++ b/bundle/API/Repository/Values/Tags/Tag.php @@ -119,12 +119,12 @@ final class Tag extends ValueObject /** * Specifies by which property the child tags should be sorted on. */ - protected TagSortBy $sortBy; + protected ?TagSortBy $sortBy; /** * Specifies whether the sort order should be ascending or descending. */ - protected TagSortOrder $sortOrder; + protected ?TagSortOrder $sortOrder; /** * Construct object optionally with a set of properties. diff --git a/bundle/Resources/sql/mysql/schema.sql b/bundle/Resources/sql/mysql/schema.sql index 262977a6..393bfa74 100644 --- a/bundle/Resources/sql/mysql/schema.sql +++ b/bundle/Resources/sql/mysql/schema.sql @@ -10,8 +10,8 @@ CREATE TABLE `eztags` ( `main_language_id` int(11) NOT NULL default '0', `language_mask` int(11) NOT NULL default '0', `priority` int(11) NOT NULL default '0', - `sort_by` varchar(100) NOT NULL default '', - `sort_order` varchar(100) NOT NULL default '', + `sort_by` varchar(100), + `sort_order` varchar(100), PRIMARY KEY ( `id` ), KEY `idx_eztags_keyword` ( `keyword`(191) ), KEY `idx_eztags_keyword_id` ( `keyword`(191), `id` ), diff --git a/bundle/Resources/sql/postgresql/schema.sql b/bundle/Resources/sql/postgresql/schema.sql index e0c19fc4..3abf0f64 100644 --- a/bundle/Resources/sql/postgresql/schema.sql +++ b/bundle/Resources/sql/postgresql/schema.sql @@ -11,8 +11,8 @@ CREATE TABLE eztags ( main_language_id integer not null default 0, language_mask integer not null default 0, priority integer not null default 0, - sort_by varchar(100) NOT NULL default '', - sort_order varchar(100) NOT NULL default '', + sort_by varchar(100), + sort_order varchar(100), PRIMARY KEY (id), CONSTRAINT idx_eztags_remote_id UNIQUE (remote_id) ); diff --git a/bundle/SPI/Persistence/Tags/Tag.php b/bundle/SPI/Persistence/Tags/Tag.php index 31ee182e..96a4d5dd 100644 --- a/bundle/SPI/Persistence/Tags/Tag.php +++ b/bundle/SPI/Persistence/Tags/Tag.php @@ -85,10 +85,10 @@ final class Tag extends ValueObject /** * Specifies by which property the child tags should be sorted on. */ - public TagSortBy $sortBy; + public ?TagSortBy $sortBy; /** * Specifies whether the sort order should be ascending or descending. */ - public TagSortOrder $sortOrder; + public ?TagSortOrder $sortOrder; } diff --git a/bundle/SPI/Persistence/Tags/TagInfo.php b/bundle/SPI/Persistence/Tags/TagInfo.php index 09ce7cac..08175ff3 100644 --- a/bundle/SPI/Persistence/Tags/TagInfo.php +++ b/bundle/SPI/Persistence/Tags/TagInfo.php @@ -77,10 +77,10 @@ final class TagInfo extends ValueObject /** * Specifies by which property the child tags should be sorted on. */ - public TagSortBy $sortBy; + public ?TagSortBy $sortBy; /** * Specifies whether the sort order should be ascending or descending. */ - public TagSortOrder $sortOrder; + public ?TagSortOrder $sortOrder; } diff --git a/tests/_fixtures/schema/schema.mysql.sql b/tests/_fixtures/schema/schema.mysql.sql index ef376606..c63dd90b 100644 --- a/tests/_fixtures/schema/schema.mysql.sql +++ b/tests/_fixtures/schema/schema.mysql.sql @@ -12,8 +12,8 @@ CREATE TABLE `eztags` ( `main_language_id` int(11) NOT NULL DEFAULT '0', `language_mask` int(11) NOT NULL DEFAULT '0', `priority` int(11) NOT NULL DEFAULT '0', - `sort_by` varchar(100) NOT NULL DEFAULT '', - `sort_order` varchar(100) NOT NULL DEFAULT '', + `sort_by` varchar(100), + `sort_order` varchar(100), PRIMARY KEY (`id`), UNIQUE KEY `eztags_remote_id` (`remote_id`), KEY `eztags_keyword` (`keyword`), diff --git a/tests/_fixtures/schema/schema.postgresql.sql b/tests/_fixtures/schema/schema.postgresql.sql index 3effe3b5..3ec34ad4 100644 --- a/tests/_fixtures/schema/schema.postgresql.sql +++ b/tests/_fixtures/schema/schema.postgresql.sql @@ -28,8 +28,8 @@ CREATE TABLE eztags ( main_language_id integer DEFAULT 0 NOT NULL, language_mask integer DEFAULT 0 NOT NULL, priority integer DEFAULT 0 NOT NULL, - sort_by character varying(100) DEFAULT ''::character varying NOT NULL, - sort_order character varying(100) DEFAULT ''::character varying NOT NULL, + sort_by character varying(100), + sort_order character varying(100), ); DROP TABLE IF EXISTS eztags_attribute_link; diff --git a/tests/_fixtures/schema/schema.sqlite.sql b/tests/_fixtures/schema/schema.sqlite.sql index cec73f99..e8c28e38 100644 --- a/tests/_fixtures/schema/schema.sqlite.sql +++ b/tests/_fixtures/schema/schema.sqlite.sql @@ -12,8 +12,8 @@ CREATE TABLE 'eztags' ( 'main_language_id' integer NOT NULL DEFAULT 0, 'language_mask' integer NOT NULL DEFAULT 0, 'priority' integer NOT NULL DEFAULT 0, - 'sort_by' text(100) NOT NULL DEFAULT '', - 'sort_order' text(100) NOT NULL DEFAULT '' + 'sort_by' text(100), + 'sort_order' text(100) ); DROP TABLE IF EXISTS 'eztags_attribute_link'; From 84d9758c6e0c75d678bad7ee1f7b18118d790c03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Wed, 25 Jun 2025 16:07:21 +0200 Subject: [PATCH 39/45] NGSTACK-906 update getChildren() and update() methods in DoctrineDatabase to properly handle the case when sortBy or sortOrder is null --- .../Legacy/Tags/Gateway/DoctrineDatabase.php | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php b/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php index 29d4ef64..957f2a90 100644 --- a/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php +++ b/bundle/Core/Persistence/Legacy/Tags/Gateway/DoctrineDatabase.php @@ -137,9 +137,13 @@ public function getChildren(int $tagId, int $offset = 0, int $limit = -1, ?array $sortOrder = TagSortOrder::from($this->configResolver->getParameter('sort.root.order', 'netgen_tags')); } else { $tagData = $this->getBasicTagData($tagId); - $sortBy = TagSortBy::tryFrom($tagData['sort_by']) + $sortBy = $tagData['sort_by'] === null + ? TagSortBy::from($this->configResolver->getParameter('sort.by', 'netgen_tags')) + : TagSortBy::tryFrom($tagData['sort_by']) ?? TagSortBy::from($this->configResolver->getParameter('sort.by', 'netgen_tags')); - $sortOrder = TagSortOrder::tryFrom($tagData['sort_order']) + $sortOrder = $tagData['sort_order'] === null + ? TagSortOrder::from($this->configResolver->getParameter('sort.order', 'netgen_tags')) + : TagSortOrder::tryFrom($tagData['sort_order']) ?? TagSortOrder::from($this->configResolver->getParameter('sort.order', 'netgen_tags')); } @@ -496,16 +500,8 @@ public function update(UpdateStruct $updateStruct, int $tagId): void Types::INTEGER, ) ->setParameter('priority', $updateStruct->priority, Types::INTEGER) - ->setParameter( - 'sort_by', - $updateStruct->sortBy->value ?? $this->configResolver->getParameter('sort.by', 'netgen_tags'), - Types::STRING, - ) - ->setParameter( - 'sort_order', - $updateStruct->sortOrder->value ?? $this->configResolver->getParameter('sort.order', 'netgen_tags'), - Types::STRING, - ); + ->setParameter('sort_by', $updateStruct->sortBy?->value, Types::STRING) + ->setParameter('sort_order', $updateStruct->sortOrder?->value, Types::STRING); $query->execute(); From fd507ddc4873c3335ce1774a99ef067c064c756c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Wed, 25 Jun 2025 16:08:12 +0200 Subject: [PATCH 40/45] NGSTACK-906 update Mapper to properly handle the case when sortBy or sortOrder is null and extracted that logic in a separate function --- .../Core/Persistence/Legacy/Tags/Mapper.php | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/bundle/Core/Persistence/Legacy/Tags/Mapper.php b/bundle/Core/Persistence/Legacy/Tags/Mapper.php index a373f4ec..32600259 100644 --- a/bundle/Core/Persistence/Legacy/Tags/Mapper.php +++ b/bundle/Core/Persistence/Legacy/Tags/Mapper.php @@ -44,17 +44,7 @@ public function createTagInfoFromRow(array $row): TagInfo $tagInfo->languageIds = $this->languageMaskGenerator->extractLanguageIdsFromMask((int) $row['language_mask']); $tagInfo->priority = (int) $row['priority']; - if ((int) $row['id'] === 0) { - $tagInfo->sortBy = TagSortBy::tryFrom($row['sort_by']) - ?? TagSortBy::from($this->configResolver->getParameter('sort.root.by', 'netgen_tags')); - $tagInfo->sortOrder = TagSortOrder::tryFrom($row['sort_order']) - ?? TagSortOrder::from($this->configResolver->getParameter('sort.root.order', 'netgen_tags')); - } else { - $tagInfo->sortBy = TagSortBy::tryFrom($row['sort_by']) - ?? TagSortBy::from($this->configResolver->getParameter('sort.by', 'netgen_tags')); - $tagInfo->sortOrder = TagSortOrder::tryFrom($row['sort_order']) - ?? TagSortOrder::from($this->configResolver->getParameter('sort.order', 'netgen_tags')); - } + $this->mapSortingFromRow($row, $tagInfo); return $tagInfo; } @@ -82,17 +72,7 @@ public function extractTagListFromRows(array $rows): array $tag->languageIds = $this->languageMaskGenerator->extractLanguageIdsFromMask((int) $row['language_mask']); $tag->priority = (int) $row['priority']; - if ((int) $row['id'] === 0) { - $tag->sortBy = TagSortBy::tryFrom($row['sort_by']) - ?? TagSortBy::from($this->configResolver->getParameter('sort.root.by', 'netgen_tags')); - $tag->sortOrder = TagSortOrder::tryFrom($row['sort_order']) - ?? TagSortOrder::from($this->configResolver->getParameter('sort.root.order', 'netgen_tags')); - } else { - $tag->sortBy = TagSortBy::tryFrom($row['sort_by']) - ?? TagSortBy::from($this->configResolver->getParameter('sort.by', 'netgen_tags')); - $tag->sortOrder = TagSortOrder::tryFrom($row['sort_order']) - ?? TagSortOrder::from($this->configResolver->getParameter('sort.order', 'netgen_tags')); - } + $this->mapSortingFromRow($row, $tag); $tagList[$tagId] = $tag; } @@ -102,4 +82,21 @@ public function extractTagListFromRows(array $rows): array return array_values($tagList); } + + private function mapSortingFromRow(array $row, Tag|TagInfo $tag): void + { + $isRootTag = (int) $row['id'] === 0; + $sortByParam = $isRootTag ? 'sort.root.by' : 'sort.by'; + $sortOrderParam = $isRootTag ? 'sort.root.order' : 'sort.order'; + + $tag->sortBy = $row['sort_by'] === null + ? null + : TagSortBy::tryFrom($row['sort_by']) + ?? TagSortBy::from($this->configResolver->getParameter($sortByParam, 'netgen_tags')); + + $tag->sortOrder = $row['sort_order'] === null + ? null + : TagSortOrder::tryFrom($row['sort_order']) + ?? TagSortOrder::from($this->configResolver->getParameter($sortOrderParam, 'netgen_tags')); + } } From a81d3f6e68f79483b012fa389767160b043a726e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Wed, 25 Jun 2025 16:09:34 +0200 Subject: [PATCH 41/45] NGSTACK-906 update sort_children_tags template to check if sortBy or sortOrder is null and fix TagsIntegrationTest --- .../views/admin/tag/sort_children_tags.html.twig | 6 ++++-- .../Repository/FieldType/TagsIntegrationTest.php | 14 ++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bundle/Resources/views/admin/tag/sort_children_tags.html.twig b/bundle/Resources/views/admin/tag/sort_children_tags.html.twig index 64a2d358..3da84083 100644 --- a/bundle/Resources/views/admin/tag/sort_children_tags.html.twig +++ b/bundle/Resources/views/admin/tag/sort_children_tags.html.twig @@ -6,7 +6,8 @@ {% for case in enum_cases('Netgen\\TagsBundle\\API\\Repository\\Values\\Enums\\TagSortOrder') %} - {% endfor %} diff --git a/tests/API/Repository/FieldType/TagsIntegrationTest.php b/tests/API/Repository/FieldType/TagsIntegrationTest.php index ad53e66d..814ed69a 100644 --- a/tests/API/Repository/FieldType/TagsIntegrationTest.php +++ b/tests/API/Repository/FieldType/TagsIntegrationTest.php @@ -8,8 +8,6 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Field; use Ibexa\Core\Base\Exceptions\InvalidArgumentType; use Ibexa\Tests\Integration\Core\Repository\FieldType\BaseIntegrationTest; -use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortBy; -use Netgen\TagsBundle\API\Repository\Values\Enums\TagSortOrder; use Netgen\TagsBundle\API\Repository\Values\Tags\Tag; use Netgen\TagsBundle\Core\FieldType\Tags\Type; use Netgen\TagsBundle\Core\FieldType\Tags\Value as TagsValue; @@ -256,8 +254,8 @@ private function getTag1(): Tag 'languageCodes' => ['eng-GB'], 'prioritizedLanguageCode' => 'eng-GB', 'priority' => 0, - 'sortBy' => TagSortBy::Id, - 'sortOrder' => TagSortOrder::Ascending, + 'sortBy' => null, + 'sortOrder' => null, ], ); } @@ -282,8 +280,8 @@ private function getTag2(): Tag 'languageCodes' => ['eng-GB'], 'prioritizedLanguageCode' => 'eng-GB', 'priority' => 0, - 'sortBy' => TagSortBy::Id, - 'sortOrder' => TagSortOrder::Ascending, + 'sortBy' => null, + 'sortOrder' => null, ], ); } @@ -308,8 +306,8 @@ private function getTag3(): Tag 'languageCodes' => ['eng-GB'], 'prioritizedLanguageCode' => 'eng-GB', 'priority' => 0, - 'sortBy' => TagSortBy::Id, - 'sortOrder' => TagSortOrder::Ascending, + 'sortBy' => null, + 'sortOrder' => null, ], ); } From 0ce8c25a01e0b1b9974b82ba44e2a9e2795a8c17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Thu, 26 Jun 2025 09:21:26 +0200 Subject: [PATCH 42/45] NGSTACK-906 add MySQL and PostgreSQL upgrade scripts for 'priority', 'sort_by' and 'sort_order' columns and updated the UPGRADE doc --- .../upgrade/mysql/5.4/dbupdate-5.0-to-5.4.sql | 4 ++++ .../postgresql/5.4/dbupdate-5.0-to-5.4.sql | 4 ++++ doc/UPGRADE.md | 16 ++++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 bundle/Resources/sql/upgrade/mysql/5.4/dbupdate-5.0-to-5.4.sql create mode 100644 bundle/Resources/sql/upgrade/postgresql/5.4/dbupdate-5.0-to-5.4.sql diff --git a/bundle/Resources/sql/upgrade/mysql/5.4/dbupdate-5.0-to-5.4.sql b/bundle/Resources/sql/upgrade/mysql/5.4/dbupdate-5.0-to-5.4.sql new file mode 100644 index 00000000..3be6f7e5 --- /dev/null +++ b/bundle/Resources/sql/upgrade/mysql/5.4/dbupdate-5.0-to-5.4.sql @@ -0,0 +1,4 @@ +ALTER TABLE `eztags` ADD COLUMN `priority` BIGINT NOT NULL DEFAULT '0'; + +ALTER TABLE `eztags` ADD COLUMN `sort_by` varchar(100); +ALTER TABLE `eztags` ADD COLUMN `sort_order` varchar(100); diff --git a/bundle/Resources/sql/upgrade/postgresql/5.4/dbupdate-5.0-to-5.4.sql b/bundle/Resources/sql/upgrade/postgresql/5.4/dbupdate-5.0-to-5.4.sql new file mode 100644 index 00000000..a6553261 --- /dev/null +++ b/bundle/Resources/sql/upgrade/postgresql/5.4/dbupdate-5.0-to-5.4.sql @@ -0,0 +1,4 @@ +ALTER TABLE `eztags` ADD COLUMN `priority` INTEGER NOT NULL DEFAULT 0; + +ALTER TABLE `eztags` ADD COLUMN `sort_by` VARCHAR(100); +ALTER TABLE `eztags` ADD COLUMN `sort_order` VARCHAR(100); diff --git a/doc/UPGRADE.md b/doc/UPGRADE.md index 67e8aa91..67cb91da 100644 --- a/doc/UPGRADE.md +++ b/doc/UPGRADE.md @@ -1,6 +1,22 @@ Netgen Tags Bundle upgrade instructions ======================================= +Upgrade from 5.0 to 5.4 +----------------------- + +Tags Bundle 5.4 adds the possibility to **sort the tags**. Similarly to Ibexa, below the children list of some tag, you +can find the options to determine by which property you want the children tags to be sorted by and in which direction +(order) you want them to be sorted. + +Besides that, a new field called `priority` was added to `eztags`. You can now personally set the priority of each tag +and sort the tags by priority. \ +Also, right now, the tags can be sorted by either `id`, `keyword`, `modified` and `priority` properties. +The sort direction can be either `ascending` or `descending`. + +Please make sure to update the `eztags` table to include the newly added properties. \ +MySQL upgrade script: `bundle/Resources/sql/upgrade/mysql/5.4/dbupdate-5.0-to-5.4.sql` \ +PostgreSQL upgrade script: `bundle/Resources/sql/upgrade/postgresql/5.4/dbupdate-5.0-to-5.4.sql` + Upgrade from 4.0 to 5.0 ----------------------- From 3e9628a82fdf0ea5914d0d795d74f0fdb3c0c7b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Thu, 26 Jun 2025 13:08:50 +0200 Subject: [PATCH 43/45] NGSTACK-906 don't show 'priority' info on synonyms and remove 'priority' field when adding or editing a synonym --- .../DataMapper/TagUpdateStructDataMapper.php | 10 ++++++-- bundle/Form/Type/TagType.php | 24 ++++++++++++------- .../Resources/views/admin/tag/show.html.twig | 5 +++- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/bundle/Form/DataMapper/TagUpdateStructDataMapper.php b/bundle/Form/DataMapper/TagUpdateStructDataMapper.php index 19990507..cd7d7925 100644 --- a/bundle/Form/DataMapper/TagUpdateStructDataMapper.php +++ b/bundle/Form/DataMapper/TagUpdateStructDataMapper.php @@ -8,6 +8,7 @@ use Symfony\Component\Form\DataMapperInterface; use Traversable; +use function array_key_exists; use function iterator_to_array; final class TagUpdateStructDataMapper implements DataMapperInterface @@ -25,7 +26,10 @@ public function mapDataToForms(mixed $viewData, Traversable $forms): void $forms['keyword']->setData($viewData->getKeyword($this->languageCode)); $forms['alwaysAvailable']->setData($viewData->alwaysAvailable); $forms['remoteId']->setData($viewData->remoteId); - $forms['priority']->setData($viewData->priority); + + if (array_key_exists('priority', $forms)) { + $forms['priority']->setData($viewData->priority); + } } public function mapFormsToData(Traversable $forms, mixed &$viewData): void @@ -44,6 +48,8 @@ public function mapFormsToData(Traversable $forms, mixed &$viewData): void $viewData->alwaysAvailable = $forms['alwaysAvailable']->getData(); $viewData->remoteId = $forms['remoteId']->getData(); - $viewData->priority = $forms['priority']->getData(); + if (array_key_exists('priority', $forms)) { + $viewData->priority = $forms['priority']->getData(); + } } } diff --git a/bundle/Form/Type/TagType.php b/bundle/Form/Type/TagType.php index 7d4a2d83..b6900aa5 100644 --- a/bundle/Form/Type/TagType.php +++ b/bundle/Form/Type/TagType.php @@ -4,11 +4,14 @@ namespace Netgen\TagsBundle\Form\Type; +use Netgen\TagsBundle\API\Repository\Values\Tags\SynonymCreateStruct; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\IntegerType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; +use function array_key_exists; + final class TagType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void @@ -36,14 +39,19 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'label' => 'tag.remote_id', 'required' => false, ], - ) - ->add( - 'priority', - IntegerType::class, - [ - 'label' => 'tag.priority', - 'required' => false, - ], ); + + if (($options['data_class'] !== SynonymCreateStruct::class) + && array_key_exists('tag', $options) && $options['tag']->mainTagId === 0) { + $builder + ->add( + 'priority', + IntegerType::class, + [ + 'label' => 'tag.priority', + 'required' => false, + ], + ); + } } } diff --git a/bundle/Resources/views/admin/tag/show.html.twig b/bundle/Resources/views/admin/tag/show.html.twig index 1fb77c03..18382c83 100644 --- a/bundle/Resources/views/admin/tag/show.html.twig +++ b/bundle/Resources/views/admin/tag/show.html.twig @@ -13,7 +13,10 @@ {{ 'tag.tag_id'|trans }}: {{ tag.id }} {{ 'tag.remote_id'|trans }}: {{ tag.remoteId }} {{ 'tag.modified'|trans }}: {{ tag.modificationDate|date }} - {{ 'tag.priority'|trans }}: {{ tag.priority }} + + {% if tag.mainTagId == 0 %} + {{ 'tag.priority'|trans }}: {{ tag.priority }} + {% endif %} {% include '@NetgenTags/admin/flash_messages.html.twig' %} From c6d1f47396fabb057bc082decdaf94f9e99c0ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Thu, 26 Jun 2025 14:12:10 +0200 Subject: [PATCH 44/45] NGSTACK-906 add 'priority', 'sort_by' and 'sort_order' fields to legacy schema --- bundle/Resources/schema/legacy.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/bundle/Resources/schema/legacy.yaml b/bundle/Resources/schema/legacy.yaml index dbdfd035..ce6be1a8 100644 --- a/bundle/Resources/schema/legacy.yaml +++ b/bundle/Resources/schema/legacy.yaml @@ -55,6 +55,19 @@ tables: nullable: false options: default: '0' + priority: + type: bigint + nullable: false + options: + default: '0' + sort_by: + type: string + nullable: true + length: 100 + sort_order: + type: string + nullable: true + length: 100 indexes: idx_eztags_keyword: fields: [keyword] From 031c1aa1d3f6bc67f99764195bf8f64d2ec84d12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Prka=C4=8Din?= Date: Thu, 26 Jun 2025 14:32:01 +0200 Subject: [PATCH 45/45] NGSTACK-906 add policy for sorting children tags --- bundle/Resources/config/policies.yaml | 1 + bundle/Resources/views/admin/tag/children.html.twig | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/bundle/Resources/config/policies.yaml b/bundle/Resources/config/policies.yaml index 1bedf021..a4e56924 100644 --- a/bundle/Resources/config/policies.yaml +++ b/bundle/Resources/config/policies.yaml @@ -9,3 +9,4 @@ tags: deletesynonym: ~ makesynonym: ~ merge: ~ + sort: ~ diff --git a/bundle/Resources/views/admin/tag/children.html.twig b/bundle/Resources/views/admin/tag/children.html.twig index fff6d3e5..88f3dd9a 100644 --- a/bundle/Resources/views/admin/tag/children.html.twig +++ b/bundle/Resources/views/admin/tag/children.html.twig @@ -5,6 +5,7 @@ {% set can_add = is_granted('ibexa:tags:add', tag is defined ? tag : null) %} {% set can_edit = is_granted('ibexa:tags:edit') %} {% set can_delete = is_granted('ibexa:tags:delete') %} +{% set can_sort = is_granted('ibexa:tags:sort') %}

    {{ 'tag.children.title'|trans }} ({{ childrenTags|length }})

    @@ -74,7 +75,7 @@ - {% if tag is defined and not tag.isSynonym %} + {% if tag is defined and not tag.isSynonym and can_sort %} {% include '@NetgenTags/admin/tag/sort_children_tags.html.twig' %} {% endif %} {% else %}