Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IBX-7808: Added possibility to define icons for content type group #1225

Merged
merged 6 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Bundle\AdminUi\DependencyInjection\Configuration\Parser;

use Ibexa\Bundle\Core\DependencyInjection\Configuration\AbstractParser;
use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface;
use Symfony\Component\Config\Definition\Builder\NodeBuilder;

/**
* Configuration parser for content type group icons.
*
* Example configuration:
*
* ```yaml
* ibexa:
* system:
* default: # configuration per siteaccess or siteaccess group
* content_type_group:
alongosz marked this conversation as resolved.
Show resolved Hide resolved
* foo:
* thumbnail: '/assets/images/foo.svg'
* bar:
* thumbnail: '/assets/images/bar.svg'
* ```
*
* @internal
*/
final class ContentTypeGroup extends AbstractParser
{
public function addSemanticConfig(NodeBuilder $nodeBuilder): void
{
$nodeBuilder
->arrayNode('content_type_group')
->useAttributeAsKey('identifier')
->arrayPrototype()
->children()
->scalarNode('thumbnail')->defaultNull()->end()
->end()
->end()
->end();
}

/**
* @param array<string,mixed> $scopeSettings
*/
public function mapConfig(array &$scopeSettings, $currentScope, ContextualizerInterface $contextualizer): void
{
if (empty($scopeSettings['content_type_group'])) {
return;
}

foreach ($scopeSettings['content_type_group'] as $identifier => $config) {
$contextualizer->setContextualParameter("content_type_group.$identifier", $currentScope, $config);
}
}
}
1 change: 1 addition & 0 deletions src/bundle/IbexaAdminUiBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ private function getConfigParsers(): array
new Parser\ContentTranslateView(),
new Parser\AdminUiForms(),
new Parser\ContentType(),
new Parser\ContentTypeGroup(),
new Parser\SubtreePath(),
new Parser\LimitationValueTemplates(),
new Parser\Assets(),
Expand Down
9 changes: 9 additions & 0 deletions src/bundle/Resources/config/default_parameters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ parameters:
ibexa.site_access.config.default.content_type.default-config:
thumbnail: '/bundles/ibexaadminui/img/ibexa-icons.svg#file'

ibexa.site_access.config.default.content_type_group.default-config:
thumbnail: '/bundles/ibexaadminui/img/ibexa-icons.svg#file'
ibexa.site_access.config.default.content_type_group.content:
thumbnail: '/bundles/ibexaadminui/img/ibexa-icons.svg#content-type-content'
ibexa.site_access.config.default.content_type_group.users:
thumbnail: '/bundles/ibexaadminui/img/ibexa-icons.svg#user-type'
ibexa.site_access.config.default.content_type_group.media:
thumbnail: '/bundles/ibexaadminui/img/ibexa-icons.svg#media-type'

ibexa.content_view.tabs.default_template: '@@ibexadesign/ui/tab/default.html.twig'

ibexa.multifile_upload.location.mappings: []
Expand Down
9 changes: 8 additions & 1 deletion src/bundle/Resources/config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,14 @@ services:
Ibexa\Bundle\AdminUi\Templating\Twig\UserPreferencesGlobalExtension:
lazy: true

Ibexa\AdminUi\UI\Service\ContentTypeIconResolver: ~
Ibexa\AdminUi\UI\Service\IconResolver:
abstract: true

Ibexa\AdminUi\UI\Service\ContentTypeIconResolver:
parent: Ibexa\AdminUi\UI\Service\IconResolver

Ibexa\AdminUi\UI\Service\ContentTypeGroupIconResolver:
parent: Ibexa\AdminUi\UI\Service\IconResolver

Ibexa\ContentForms\ConfigResolver\MaxUploadSize: ~

Expand Down
2 changes: 2 additions & 0 deletions src/bundle/Resources/config/services/ui_config/common.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ services:

Ibexa\Bundle\AdminUi\Templating\Twig\ContentTypeIconExtension: ~

Ibexa\Bundle\AdminUi\Templating\Twig\ContentTypeGroupIconExtension: ~

Ibexa\Bundle\AdminUi\Templating\Twig\EmbeddedItemEditFormExtension: ~

Ibexa\AdminUi\UI\Config\Provider\UserContentTypes:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@
content: col_raw,
raw: true,
}]) %}
{% set col_raw %}
<svg class="ibexa-icon ibexa-icon--small">
<use xlink:href="{{ ibexa_content_type_group_icon(content_type_group.identifier) }}"></use>
</svg>
{% endset %}
{% set body_row_cols = body_row_cols|merge([{
has_icon: true,
content: col_raw,
raw: true,
}]) %}

{% set col_raw %}
{% set view_url = path('ibexa.content_type_group.view', {
Expand Down Expand Up @@ -118,6 +128,7 @@
headline: custom_results_headline ?? results_headline(pager.getNbResults()),
head_cols: [
{ has_checkbox: true },
{ has_icon: true },
{ content: 'content_type_group.view.list.column.identifier'|trans|desc('Name') },
{ content: 'content_type_group.view.list.column.id'|trans|desc('ID') },
{ content: 'content_type_group.view.list.column.content_types_count'|trans|desc('Number of content types') },
Expand Down
39 changes: 39 additions & 0 deletions src/bundle/Templating/Twig/ContentTypeGroupIconExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Bundle\AdminUi\Templating\Twig;

use Ibexa\AdminUi\UI\Service\ContentTypeGroupIconResolver;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

/**
* @internal
*/
final class ContentTypeGroupIconExtension extends AbstractExtension
mikadamczyk marked this conversation as resolved.
Show resolved Hide resolved
{
private ContentTypeGroupIconResolver $contentTypeGroupIconResolver;

public function __construct(ContentTypeGroupIconResolver $contentTypeGroupIconResolver)
{
$this->contentTypeGroupIconResolver = $contentTypeGroupIconResolver;
}

public function getFunctions(): array
{
return [
new TwigFunction(
'ibexa_content_type_group_icon',
[$this->contentTypeGroupIconResolver, 'getContentTypeGroupIcon'],
[
'is_safe' => ['html'],
]
),
];
}
}
9 changes: 1 addition & 8 deletions src/bundle/Templating/Twig/ContentTypeIconExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,13 @@

class ContentTypeIconExtension extends AbstractExtension
{
/** @var \Ibexa\AdminUi\UI\Service\ContentTypeIconResolver */
private $contentTypeIconResolver;
private ContentTypeIconResolver $contentTypeIconResolver;

/**
* @param \Ibexa\AdminUi\UI\Service\ContentTypeIconResolver $contentTypeIconResolver
*/
public function __construct(ContentTypeIconResolver $contentTypeIconResolver)
{
$this->contentTypeIconResolver = $contentTypeIconResolver;
}

/**
* {@inheritdoc}
*/
public function getFunctions(): array
{
return [
Expand Down
33 changes: 33 additions & 0 deletions src/lib/UI/Service/ContentTypeGroupIconResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\AdminUi\UI\Service;

use Symfony\Component\String\Slugger\AsciiSlugger;

/**
* @internal
*/
final class ContentTypeGroupIconResolver extends IconResolver
mikadamczyk marked this conversation as resolved.
Show resolved Hide resolved
{
private const PARAM_NAME_FORMAT = 'content_type_group.%s';

/**
* Returns path to content type group icon.
*
* Path is resolved based on configuration (ibexa.system.<SCOPE>.content_type.<IDENTIFIER>). If there isn't
* corresponding entry for given content type, then path to default icon will be returned.
*/
public function getContentTypeGroupIcon(string $identifier): string
{
$slugger = new AsciiSlugger();
$identifier = (string)$slugger->slug($identifier, '_')->lower();

return $this->getIcon(self::PARAM_NAME_FORMAT, $identifier);
}
}
64 changes: 3 additions & 61 deletions src/lib/UI/Service/ContentTypeIconResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,77 +8,19 @@

namespace Ibexa\AdminUi\UI\Service;

use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface;
use Symfony\Component\Asset\Packages;

final class ContentTypeIconResolver
final class ContentTypeIconResolver extends IconResolver
{
private const DEFAULT_IDENTIFIER = 'default-config';
private const PARAM_NAME_FORMAT = 'content_type.%s';

private const ICON_KEY = 'thumbnail';

/** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */
private $configResolver;

/** @var \Symfony\Component\Asset\Packages */
private $packages;

/**
* @param \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface $configResolver
* @param \Symfony\Component\Asset\Packages $packages
*/
public function __construct(ConfigResolverInterface $configResolver, Packages $packages)
{
$this->configResolver = $configResolver;
$this->packages = $packages;
}

/**
* Returns path to content type icon.
*
* Path is resolved based on configuration (ezpublish.system.<SCOPE>.content_type.<IDENTIFIER>). If there isn't
* Path is resolved based on configuration (ibexa.system.<SCOPE>.content_type.<IDENTIFIER>). If there isn't
* corresponding entry for given content type, then path to default icon will be returned.
*
* @throws \Ibexa\AdminUi\Exception\ContentTypeIconNotFoundException
*/
public function getContentTypeIcon(string $identifier): string
{
$icon = $this->resolveIcon($identifier);

$fragment = null;
if (strpos($icon, '#') !== false) {
[$icon, $fragment] = explode('#', $icon);
}

return $this->packages->getUrl($icon) . ($fragment ? '#' . $fragment : '');
}

/**
* @throws \Ibexa\AdminUi\Exception\ContentTypeIconNotFoundException
*/
private function resolveIcon(string $identifier): string
{
$parameterName = $this->getConfigParameterName($identifier);
$defaultParameterName = $this->getConfigParameterName(self::DEFAULT_IDENTIFIER);

if ($this->configResolver->hasParameter($parameterName)) {
$config = $this->configResolver->getParameter($parameterName);
}

if ((empty($config) || empty($config[self::ICON_KEY])) && $this->configResolver->hasParameter($defaultParameterName)) {
$config = $this->configResolver->getParameter($defaultParameterName);
}

return $config[self::ICON_KEY] ?? '';
}

/**
* Return configuration parameter name for given content type identifier.
*/
private function getConfigParameterName(string $identifier): string
{
return sprintf(self::PARAM_NAME_FORMAT, $identifier);
return $this->getIcon(self::PARAM_NAME_FORMAT, $identifier);
}
}

Expand Down
66 changes: 66 additions & 0 deletions src/lib/UI/Service/IconResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\AdminUi\UI\Service;

use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface;
use Symfony\Component\Asset\Packages;

abstract class IconResolver
{
protected const DEFAULT_IDENTIFIER = 'default-config';
protected const ICON_KEY = 'thumbnail';

private ConfigResolverInterface $configResolver;

protected Packages $packages;

public function __construct(ConfigResolverInterface $configResolver, Packages $packages)
{
$this->configResolver = $configResolver;
$this->packages = $packages;
}

protected function getIcon(string $format, string $identifier): string
{
$icon = $this->resolveIcon($format, $identifier);
$fragment = null;
if (strpos($icon, '#') !== false) {
[$icon, $fragment] = explode('#', $icon);
}

return $this->packages->getUrl($icon) . ($fragment ? '#' . $fragment : '');
}

private function resolveIcon(string $format, string $identifier): string
{
$parameterName = $this->getConfigParameterName($format, $identifier);
$defaultParameterName = $this->getConfigParameterName($format, static::DEFAULT_IDENTIFIER);
konradoboza marked this conversation as resolved.
Show resolved Hide resolved

if ($this->configResolver->hasParameter($parameterName)) {
$config = $this->configResolver->getParameter($parameterName);
}

if (
(empty($config) || empty($config[static::ICON_KEY]))
konradoboza marked this conversation as resolved.
Show resolved Hide resolved
&& $this->configResolver->hasParameter($defaultParameterName)
) {
$config = $this->configResolver->getParameter($defaultParameterName);
}

return $config[static::ICON_KEY] ?? '';
konradoboza marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Returns configuration parameter name for given content type identifier.
*/
private function getConfigParameterName(string $format, string $identifier): string
{
return sprintf($format, $identifier);
}
}
Loading