'Sulu CMS - Page icon for navigation context

I want to present a set of selectable icons to choose one from for each page. When selected, the icon should be available in navigation context data, e.g. via Sulu\HeadlessBundle:

{
    "_embedded": {
        "items": [
            {
                "id": "ffffffff-ffff-ffff-ffff-fffffffffff",
                "uuid": "ffffffff-ffff-ffff-ffff-fffffffffff",
                "nodeType": 1,
                "changed": "2000-01-01T12:00:00",
                "changer": 1,
                "created": "2022-01-01T12:00:01",
                "publishedState": true,
                "published": "2000-01-01T12:00:01",
                "creator": 1,
                "title": "Foo",
                "locale": "de",
                "webspaceKey": "bar",
                "template": "default",
                "parent": "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee",
                "url": "/foo",
                "urls": {
                    "de": "/foo"
                },
                "author": "1",
                "authored": "2000-01-01T12:00:00",
                "order": 20,
                "children": [],
                "nav_icon": {} // Either a Media entity from a system collection, or just a filename to reference the icon with
            }
        ]
    }
}

I am aware of the possibility of storing icons as Media data and then applying them via the page excerpt data, but I don't want my users to arbitrarily upload and pick icons. Guessing from my own research I also cannot limit a single_media_selection to a system collection, let alone the icons selection in the excerpt tab.

Is there an approach to my task? I tried adding UI tabs to the page editing form, but I can't seem to extend the actual structure data so that the icon information is serialized.



Solution 1:[1]

I devised a solution that utilizes the Symfony kernel listeners. Since I only need the top-level navigation items to display an icon, I hooked into the kernel.response of the HeadlessBundle's navigations API route and iterated through the response content, fetching document information for each top-level page and extracting the relevant extension data that I already constructed with the Sulu cookbook.

<?php declare(strict_types=1);

namespace App\EventListener;

use Sulu\Component\DocumentManager\DocumentManagerInterface;
use Sulu\Component\DocumentManager\Exception\DocumentManagerException;
use Symfony\Component\HttpKernel\Event\ResponseEvent;

class NavigationListener
{
    public function __construct(private DocumentManagerInterface $documentManager)
    {
    }

    public function onKernelResponse(ResponseEvent $event): void
    {
        $request = $event->getRequest();
        if ($request->attributes->get('_route') !== 'sulu_headless.api.navigation') {
            return;
        }
        $responseObj = json_decode($event->getResponse()->getContent(), true);
        foreach ($responseObj['_embedded']['items'] as &$item) {
            $uuid = $item['uuid'];
            $doc = $this->documentManager->find($uuid);
            $documentExtension = $doc->getExtensionsData()->toArray();
            if (isset($documentExtension['navigation'])) {
                $item['navigationIcon'] = $documentExtension['navigation']['icon'];
            }
        }
        $event->getResponse()->setContent(json_encode($responseObj, JSON_PRETTY_PRINT));
    }
}

In the long term, it would be interesting to pass extension loading information to the HeadlessBundle.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Leon Willens