Skip to content
Snippets Groups Projects
Commit 07d825b4 authored by David Galeano's avatar David Galeano
Browse files

Issue #3480374 by gxleano: Add Iconify Icons Provider submodule to be integrated with UI Icons

parent 75cfde65
No related branches found
No related tags found
1 merge request!21Issue #3480374 by gxleano: Add Iconify Icons Provider submodule to be integrated with UI Icons
Pipeline #309636 passed with warnings
......@@ -17,3 +17,24 @@ Hurtado
alvar
hurtad
autocompleteselect
ionicons
Boxicons
tabler
Tabler
cilx
devicon
Devicons
uicons
typicons
Typicons
vsicons
Themify
remixicon
Pixelart
teenyicons
cssgg
coreui
boxicons
healthicons
vaadin
pixelarticons
name: 'Iconify Icons Provider'
type: module
description: 'Iconify Icons provider to allow definition of Icons from Iconify.'
package: User interface
core_version_requirement: ^10.3 || ^11
dependencies:
- iconify_icons:iconify_icons
- ui_icons:ui_icons
This diff is collapsed.
<?php
declare(strict_types=1);
namespace Drupal\iconify_icons_provider\Plugin\IconExtractor;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\iconify_icons\IconifyServiceInterface;
use Drupal\ui_icons\Attribute\IconExtractor;
use Drupal\ui_icons\Exception\IconPackConfigErrorException;
use Drupal\ui_icons\Plugin\IconExtractorBase;
use Drupal\ui_icons\PluginForm\IconPackExtractorForm;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Plugin implementation of the ui_icons_extractor.
*/
#[IconExtractor(
id: 'iconify',
label: new TranslatableMarkup('Iconify'),
description: new TranslatableMarkup('Provides Iconify list of Icons.'),
forms: [
'settings' => IconPackExtractorForm::class,
]
)]
class IconifyExtractor extends IconExtractorBase implements ContainerFactoryPluginInterface {
/**
* Constructs a IconifyExtractor object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\iconify_icons\IconifyServiceInterface $iconify
* Drupal Iconify service.
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
protected IconifyServiceInterface $iconify,
) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
/**
* {@inheritdoc}
*/
public static function create(
ContainerInterface $container,
array $configuration,
$plugin_id,
$plugin_definition,
): self {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('iconify_icons.iconify_service')
);
}
/**
* {@inheritdoc}
*/
public function discoverIcons(): array {
$config = $this->configuration['config'] ?? [];
if (!isset($config['collections'])) {
throw new IconPackConfigErrorException(sprintf('Missing `config: collections` in your definition, extractor %s require this value.', $this->getPluginId()));
}
unset($this->configuration['config']);
$icons = [];
foreach ($config['collections'] as $collection) {
$icons_collection = $this->iconify->getIconsByCollection($collection);
if (empty($icons_collection)) {
continue;
}
foreach ($icons_collection as $icon_id) {
if (!is_string($icon_id)) {
continue;
}
$icon_full_id = $this->configuration['icon_pack_id'] . ':' . $icon_id;
$source = sprintf($this->iconify::DESIGN_DOWNLOAD_API_ENDPOINT, $collection, $icon_id);
$icons[$icon_full_id] = self::createIcon($icon_id, $this->configuration, $source);
}
}
return $icons;
}
}
......@@ -2,8 +2,11 @@
namespace Drupal\iconify_icons;
use Drupal\Component\Serialization\Json;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\ServerException;
use GuzzleHttp\Promise\Utils;
use Psr\Log\LoggerInterface;
......@@ -13,10 +16,14 @@ use Psr\Log\LoggerInterface;
class IconifyService implements IconifyServiceInterface {
public const SEARCH_API_ENDPOINT = 'https://api.iconify.design/search';
public const COLLECTION_API_ENDPOINT = 'https://api.iconify.design/collection';
public const COLLECTIONS_API_ENDPOINT = 'https://api.iconify.design/collections';
// Arguments [$collection, $icon_name].
public const DESIGN_DOWNLOAD_API_ENDPOINT = 'https://api.iconify.design/%s/%s.svg';
public const API_VERSION = 'https://api.iconify.design/version';
/**
......@@ -68,7 +75,8 @@ class IconifyService implements IconifyServiceInterface {
],
]);
$data = json_decode($response->getBody()->getContents(), TRUE, 512, JSON_THROW_ON_ERROR);
$data = json_decode($response->getBody()
->getContents(), TRUE, 512, JSON_THROW_ON_ERROR);
return $data['icons'] ?? [];
}
catch (RequestException $e) {
......@@ -88,7 +96,8 @@ class IconifyService implements IconifyServiceInterface {
public function getCollections(): array {
try {
$response = $this->httpClient->request('GET', $this::COLLECTIONS_API_ENDPOINT, []);
return json_decode($response->getBody()->getContents(), TRUE, 512, JSON_THROW_ON_ERROR);
return json_decode($response->getBody()
->getContents(), TRUE, 512, JSON_THROW_ON_ERROR);
}
catch (RequestException $e) {
// Handle request exception (e.g., log error, return empty array)
......@@ -106,20 +115,51 @@ class IconifyService implements IconifyServiceInterface {
*/
public function getIconsByCollection(string $collection): array {
try {
$response = $this->httpClient->request('GET', $this::COLLECTIONS_API_ENDPOINT, [
$response = $this->httpClient->request('GET', $this::COLLECTION_API_ENDPOINT, [
'query' => [
'prefixes' => $collection,
'prefix' => $collection,
],
]);
$data = json_decode($response->getBody(), TRUE, 512, JSON_THROW_ON_ERROR);
return $data['icons'];
$icons_list = Json::decode((string) $response->getBody());
if (!is_array($icons_list)) {
$param = [
'@collection' => $collection,
'@error' => (string) $response->getBody(),
];
$this->logger->error('Iconify error for @collection: @error', $param);
return [];
}
$icons = [];
if (!empty($icons_list['categories'])) {
foreach ($icons_list['categories'] as $list) {
if (is_array($list)) {
foreach ($list as $icon) {
$icons[] = $icon;
}
}
}
return $icons;
}
return $icons_list['uncategorized'] ?? [];
}
catch (RequestException $e) {
// Handle request exception (e.g., log error, return empty array)
$this->logger->error('Error fetching icons: ' . $e->getMessage());
catch (ClientException | ServerException $e) {
$param = [
'@errorType' => ($e instanceof ClientException) ? 'client' : 'server',
'@collection' => $collection,
'@error' => $e->getResponse(),
];
$this->logger->error(
'Iconify @errorType error for @collection: @error',
$param
);
return [];
}
}
/**
......
......@@ -4,14 +4,14 @@ declare(strict_types=1);
namespace Drupal\iconify_icons\Plugin\CKEditor5Plugin;
use Drupal\ckeditor5\Plugin\CKEditor5PluginConfigurableInterface;
use Drupal\ckeditor5\Plugin\CKEditor5PluginConfigurableTrait;
use Drupal\ckeditor5\Plugin\CKEditor5PluginDefault;
use Drupal\ckeditor5\Plugin\CKEditor5PluginDefinition;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Url;
use Drupal\ckeditor5\Plugin\CKEditor5PluginConfigurableInterface;
use Drupal\ckeditor5\Plugin\CKEditor5PluginConfigurableTrait;
use Drupal\ckeditor5\Plugin\CKEditor5PluginDefault;
use Drupal\ckeditor5\Plugin\CKEditor5PluginDefinition;
use Drupal\editor\EditorInterface;
use Drupal\iconify_icons\IconifyServiceInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
......
......@@ -2,9 +2,9 @@
namespace Drupal\Tests\iconify_icons\Unit;
use Drupal\Tests\UnitTestCase;
use Drupal\iconify_icons\IconifyService;
use Drupal\iconify_icons\IconsCacheInterface;
use Drupal\Tests\UnitTestCase;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Promise\PromiseInterface;
use Psr\Http\Message\ResponseInterface;
......
......@@ -3,8 +3,8 @@
namespace Drupal\Tests\iconify_icons\Unit;
use Drupal\Core\File\FileSystemInterface;
use Drupal\iconify_icons\IconsCache;
use Drupal\Tests\UnitTestCase;
use Drupal\iconify_icons\IconsCache;
/**
* Tests for IconsCache Service.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment