Loading component_library.module +29 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ declare(strict_types=1); */ use Drupal\Component\Serialization\Exception\InvalidDataTypeException; use Drupal\component_library\Form\ComponentOverrideForm; use Drupal\Component\Serialization\Json; use Drupal\Component\Serialization\Yaml; use Drupal\component_library\Entity\ComponentLibraryPattern; Loading Loading @@ -216,3 +217,31 @@ function component_library_system_info_alter(array &$info, Extension $file, $typ $info['engine'] = 'component_library_engine'; } } /** * Implements hook_modules_installed(). */ function component_library_modules_installed($modules, $is_syncing) { \Drupal::cache('default')->delete(ComponentOverrideForm::COLLECTED_LIBRARIES_CID); } /** * Implements hook_modules_uninstalled(). */ function component_library_modules_uninstalled($modules, $is_syncing) { \Drupal::cache('default')->delete(ComponentOverrideForm::COLLECTED_LIBRARIES_CID); } /** * Implements hook_themes_installed(). */ function component_library_themes_installed($themes) { \Drupal::cache('default')->delete(ComponentOverrideForm::COLLECTED_LIBRARIES_CID); } /** * Implements hook_themes_uninstalled(). */ function component_library_themes_uninstalled($themes) { \Drupal::cache('default')->delete(ComponentOverrideForm::COLLECTED_LIBRARIES_CID); } config/schema/component_library.schema.yml +6 −0 Original line number Diff line number Diff line Loading @@ -70,3 +70,9 @@ component_library.override.*: variables: type: string label: Variables libraries: type: sequence label: Libraries sequence: type: string label: Library src/Entity/ComponentOverride.php +6 −0 Original line number Diff line number Diff line Loading @@ -59,6 +59,7 @@ use Drupal\Core\Form\FormState; * "plugin_data", * "template", * "variables", * "libraries", * } * ) */ Loading Loading @@ -104,6 +105,11 @@ final class ComponentOverride extends ConfigEntityBase { */ protected string $variables; /** * The libraries attached to the template. */ protected array $libraries = []; /** * Set variables. * Loading src/Form/ComponentOverrideForm.php +221 −4 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ namespace Drupal\component_library\Form; use Drupal\Core\Url; use Twig\Error\SyntaxError; use Drupal\Core\Cache\Cache; use Drupal\Core\Ajax\HtmlCommand; use Drupal\Core\Form\SubformState; use Drupal\Core\Ajax\AjaxResponse; Loading @@ -15,11 +16,16 @@ use Drupal\Core\Ajax\PrependCommand; use Drupal\Core\Ajax\RedirectCommand; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Extension\ExtensionList; use Drupal\Core\Render\RendererInterface; use Drupal\Core\Template\TwigEnvironment; use Drupal\component_library\OverrideMode; use Drupal\Core\TempStore\PrivateTempStore; use Drupal\Core\Ajax\CloseModalDialogCommand; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Extension\ThemeExtensionList; use Drupal\Core\Extension\ModuleExtensionList; use Drupal\Core\Extension\ProfileExtensionList; use Drupal\Core\Asset\LibraryDiscoveryInterface; use Drupal\Core\Extension\ThemeHandlerInterface; use Drupal\Core\TempStore\PrivateTempStoreFactory; use Drupal\Core\StringTranslation\TranslatableMarkup; Loading @@ -37,6 +43,13 @@ final class ComponentOverrideForm extends EntityForm { use ComponentOverrideFormTrait; /** * Cached ID for libraries gathered from all available/installed extensions. * * @var string */ const COLLECTED_LIBRARIES_CID = 'component_library:component_override_form:collected_libraries'; /** * Theme. * Loading @@ -52,10 +65,20 @@ final class ComponentOverrideForm extends EntityForm { protected PrivateTempStore $cache; protected OverrideMode $overrideMode; private PrepareOverrideEvent $prepareOverrideEvent; protected CacheBackendInterface $cacheDefault; protected LibraryDiscoveryInterface $libraryDiscovery; protected ModuleExtensionList $moduleExtensionList; protected ExtensionList $profileExtensionList; protected ThemeExtensionList $themeExtensionList; public function __construct(ThemeHandlerInterface $theme_handler, ComponentOverrideManager $override_manager, TwigEnvironment $twig, EventDispatcherInterface $event_dispatcher, RendererInterface $renderer, PrivateTempStoreFactory $temp_store, OverrideMode $override_mode) { public function __construct(ThemeHandlerInterface $theme_handler, CacheBackendInterface $cache_default, ComponentOverrideManager $override_manager, LibraryDiscoveryInterface $library_discovery, ModuleExtensionList $module_list, ProfileExtensionList $profile_list, ThemeExtensionList $theme_list, TwigEnvironment $twig, EventDispatcherInterface $event_dispatcher, RendererInterface $renderer, PrivateTempStoreFactory $temp_store, OverrideMode $override_mode) { $this->themeHandler = $theme_handler; $this->cacheDefault = $cache_default; $this->overrideManager = $override_manager; $this->libraryDiscovery = $library_discovery; $this->moduleExtensionList = $module_list; $this->profileExtensionList = $profile_list; $this->themeExtensionList = $theme_list; $this->twig = $twig; $this->dispatcher = $event_dispatcher; $this->renderer = $renderer; Loading @@ -69,7 +92,12 @@ final class ComponentOverrideForm extends EntityForm { public static function create(ContainerInterface $container): self { return new self( $container->get('theme_handler'), $container->get('cache.default'), $container->get('plugin.manager.component_override'), $container->get('library.discovery'), $container->get('extension.list.module'), $container->get('extension.list.profile'), $container->get('extension.list.theme'), $container->get('twig'), $container->get('event_dispatcher'), $container->get('renderer'), Loading Loading @@ -189,7 +217,7 @@ final class ComponentOverrideForm extends EntityForm { (empty($input['template']) && $this->prepareOverrideEvent->isPrepopulated()) && $override ) { $template = $this->getTemplate($override); $template = $this->getTemplate($override, TRUE); $input['template'] = $template; $form_state->setUserInput($input); } Loading Loading @@ -226,6 +254,47 @@ final class ComponentOverrideForm extends EntityForm { '#suffix' => '<output id="override-preview"></output>', ]; if ($override && empty($form_state->get('selected_libraries')[$override])) { $selected_libraries = $form_state->get('selected_libraries') ?: []; $selected_libraries[$override] = $this->parseAttachedLibraries( $this->getTemplate($override, FALSE) ); $form_state->set('selected_libraries', $selected_libraries); } $attached_libraries = $form_state->get('selected_libraries')[$override]; $available_libraries = $this->getAvailableLibraries(); $form['dynamic_elements_container']['libraries_wrapper'] = [ '#type' => 'details', '#prefix' => '<div id="libraries-wrapper">', '#suffix' => '</div>', '#open' => !empty($attached_libraries), '#title' => $this->t('Libraries'), 'libraries_selector' => [ '#type' => 'select', '#title' => $this->t('Add library'), '#empty_option' => $this->t('- Select -'), '#options' => $available_libraries, '#ajax' => [ 'callback' => [$this, 'ajaxUpdateLibraries'], 'wrapper' => 'libraries-wrapper', 'effect' => 'fade', ], ], 'libraries_list' => [ '#prefix' => '<h6>' . $this->t('Attached libraries') . '</h6>', '#type' => 'table', '#header' => [$this->t('Library'), $this->t('Remove')], '#empty' => $this->t('No libraries are attached.'), ], 'libraries' => [ '#type' => 'value', '#value' => $attached_libraries ?? [], ], ]; if ($attached_libraries) { $this->buildLibrariesList($attached_libraries, $form); } $form['#prefix'] = '<div id="status-messages"></div>'; $form['#attached']['library'][] = 'component_library/component_override'; return $form; Loading @@ -235,6 +304,29 @@ final class ComponentOverrideForm extends EntityForm { * {@inheritdoc} */ public function validateForm(array &$form, FormStateInterface $form_state): void { // Validate attached libraries. $override = $form_state->getValue('override') ?? $this->entity->get('override'); $libraries = $form_state->get('selected_libraries'); $trigger = $form_state->getTriggeringElement(); if ((isset($trigger['#name']) && $trigger['#name'] === 'override') && $override) { $theme_registry = $this->overrideManager->getThemeRegistry()[$form_state->getValue('override')]; $template_path = sprintf('%s/%s.html.twig', $theme_registry['path'], $theme_registry['template']); $template = file_get_contents($template_path); $libraries[$override] = $libraries[$override] ?? $this->parseAttachedLibraries($template); } if ((isset($trigger['#name']) && $trigger['#name'] === 'libraries_selector')) { $added_library = $form_state->getValue('libraries_selector'); if (!empty($added_library) && !in_array($added_library, $libraries)) { $libraries[$override][] = $added_library; } } if (isset($trigger['#parents'][1]) && isset($trigger['#name']) && strpos($trigger['#name'], 'library_remove') !== FALSE) { array_splice($libraries[$override], $trigger['#parents'][1], 1); } $form_state->set('selected_libraries', $libraries); // Validate the plugin. $plugin_values = $form_state->getValue('plugin_container'); if (!empty($plugin_values['plugin'])) { Loading Loading @@ -435,6 +527,123 @@ final class ComponentOverrideForm extends EntityForm { return NULL; } /** * AJAX callback to load the libraries list. */ public function ajaxUpdateLibraries(array &$form, FormStateInterface $form_state) { return $form['dynamic_elements_container']['libraries_wrapper']; } /** * Builds the rows of the libraries list widget. * * @param array $selected_libraries * The currently selected libraries. * @param array $form * The form array. */ protected function buildLibrariesList(array $selected_libraries, array &$form) { $row = 0; foreach ($selected_libraries as $key => $library) { $form['dynamic_elements_container']['libraries_wrapper']['libraries_list'][$row]['library'] = [ '#markup' => $library, ]; $button_key = 'library_remove' . $key; $form['dynamic_elements_container']['libraries_wrapper']['libraries_list'][$row][$button_key] = [ '#type' => 'submit', '#value' => $this->t('Remove'), '#name' => $button_key, '#limit_validation_errors' => TRUE, '#executes_submit_callback' => FALSE, '#ajax' => [ 'callback' => [$this, 'ajaxUpdateLibraries'], 'wrapper' => 'libraries-wrapper', 'effect' => 'fade', ], ]; $row++; } } /** * Parses libraries from a given template string. * * @param string $template * The template to parse from. * * @return array * List of libraries. */ protected function parseAttachedLibraries($template) { $matches = []; $libraries = []; \preg_match_all('/attach_library\(.+\)/', $template, $matches); if (!empty($matches[0])) { foreach ($matches[0] as $attach_statement) { $libraries[] = \mb_substr( $attach_statement, \mb_strlen("attach_library('"), \mb_strlen($attach_statement) - \mb_strlen("attach_library('") - \mb_strlen("')") ); } } return $libraries; } /** * Gathers libraries from core, profiles, installed modules and themes. * * @return array * Nested array of libraries in the form of extension => [libraries]. */ protected function getAvailableLibraries() { $cache = $this->cacheDefault->get(static::COLLECTED_LIBRARIES_CID); if (!empty($cache->data)) { return $cache->data; } else { $available_libraries = []; $modules = $this->getInstalledList($this->moduleExtensionList); $profiles = $this->getInstalledList($this->profileExtensionList); $themes = $this->getInstalledList($this->themeExtensionList); $core_libraries = $this->libraryDiscovery->getLibrariesByExtension('core'); foreach ($core_libraries as $library_name => $library) { $available_libraries['core']['core/' . $library_name] = 'core/' . $library_name; } /** @var \Drupal\Core\Extension\Extension $extension */ foreach (\array_merge($modules, $profiles, $themes) as $extension_name => $extension) { $extension_info = (array) $extension; if ($extension->getType() == 'profile'|| !empty($extension_info['status'])) { $extension_libraries = $this->libraryDiscovery->getLibrariesByExtension($extension_name); $libraries_list = []; foreach ($extension_libraries as $library_name => $library) { $libraries_list[$extension_name][$extension_name . '/' . $library_name] = $extension_name . '/' . $library_name; } $available_libraries = \array_merge($available_libraries, $libraries_list); } } $this->cacheDefault->set(static::COLLECTED_LIBRARIES_CID, $available_libraries, Cache::PERMANENT, ['library_info']); return $available_libraries; } } /** * Get installed list. * * @param \Drupal\Core\Extension\ExtensionList $extension_list * An extension list. * * @return array * An array of installed extensions for the given list. */ protected function getInstalledList(ExtensionList $extension_list) { $list = []; foreach ($extension_list->getAllInstalledInfo() as $name => $info) { $list[$name] = $extension_list->get($name); } return $list; } /** * Get template. * Loading @@ -442,13 +651,15 @@ final class ComponentOverrideForm extends EntityForm { * * @param string $override * The theme suggestion being overridden. * @param bool $strip_libraries * Whether to strip attach_library twig statements from the template. * * @return string * The file contents of the template. * * @throws \Twig\Error\LoaderError */ protected function getTemplate(string $override): string { protected function getTemplate(string $override, $strip_libraries): string { $registry = $this->overrideManager->getThemeRegistry(); $pieces = \explode('__', $override); for ($i = \count($pieces); $i > 0; $i--) { Loading @@ -456,6 +667,12 @@ final class ComponentOverrideForm extends EntityForm { if (!empty($registry[$possible_theme_suggestion])) { $path = \sprintf('%s/%s.html.twig', $registry[$possible_theme_suggestion]['path'], $registry[$possible_theme_suggestion]['template']); $template = $this->twig->getLoader()->getSourceContext($path)->getCode(); if ($strip_libraries) { // Strip attach_library statements as libraries get displayed in // dedicated field. $template = \preg_replace('/\{\{ attach_library\(.+\) \}\}[\r\n|\r]/', '', $template); $template = \preg_replace('/\{\{attach_library\(.+\)\}\}[\r\n|\r]/', '', $template); } return $template; } } Loading src/Twig/Loader.php +19 −3 Original line number Diff line number Diff line Loading @@ -30,9 +30,25 @@ final class Loader implements LoaderInterface, SourceContextLoaderInterface { * {@inheritdoc} */ public function getSourceContext($name): Source { $override = $this->getOverrideByName($name); if ($override) { return new Source($override->get('template'), $name); $override_name = $this->getOverrideNameFromName($name); $overrides = NULL; $theme = $this->themeManager->getActiveTheme(); if ($theme) { $overrides = $this->overrides->loadByProperties([ 'override' => $override_name, 'theme' => $theme->getName(), ]); } if ($overrides) { $override = \reset($overrides); $template = $override->get('template'); $libraries = $override->get('libraries'); $attach_statements = ''; foreach ($libraries as $library) { $attach_statements .= "{{ attach_library('$library') }}\n"; } $template = $attach_statements . $template; return new Source($template, $name); } throw new LoaderError($name); Loading Loading
component_library.module +29 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ declare(strict_types=1); */ use Drupal\Component\Serialization\Exception\InvalidDataTypeException; use Drupal\component_library\Form\ComponentOverrideForm; use Drupal\Component\Serialization\Json; use Drupal\Component\Serialization\Yaml; use Drupal\component_library\Entity\ComponentLibraryPattern; Loading Loading @@ -216,3 +217,31 @@ function component_library_system_info_alter(array &$info, Extension $file, $typ $info['engine'] = 'component_library_engine'; } } /** * Implements hook_modules_installed(). */ function component_library_modules_installed($modules, $is_syncing) { \Drupal::cache('default')->delete(ComponentOverrideForm::COLLECTED_LIBRARIES_CID); } /** * Implements hook_modules_uninstalled(). */ function component_library_modules_uninstalled($modules, $is_syncing) { \Drupal::cache('default')->delete(ComponentOverrideForm::COLLECTED_LIBRARIES_CID); } /** * Implements hook_themes_installed(). */ function component_library_themes_installed($themes) { \Drupal::cache('default')->delete(ComponentOverrideForm::COLLECTED_LIBRARIES_CID); } /** * Implements hook_themes_uninstalled(). */ function component_library_themes_uninstalled($themes) { \Drupal::cache('default')->delete(ComponentOverrideForm::COLLECTED_LIBRARIES_CID); }
config/schema/component_library.schema.yml +6 −0 Original line number Diff line number Diff line Loading @@ -70,3 +70,9 @@ component_library.override.*: variables: type: string label: Variables libraries: type: sequence label: Libraries sequence: type: string label: Library
src/Entity/ComponentOverride.php +6 −0 Original line number Diff line number Diff line Loading @@ -59,6 +59,7 @@ use Drupal\Core\Form\FormState; * "plugin_data", * "template", * "variables", * "libraries", * } * ) */ Loading Loading @@ -104,6 +105,11 @@ final class ComponentOverride extends ConfigEntityBase { */ protected string $variables; /** * The libraries attached to the template. */ protected array $libraries = []; /** * Set variables. * Loading
src/Form/ComponentOverrideForm.php +221 −4 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ namespace Drupal\component_library\Form; use Drupal\Core\Url; use Twig\Error\SyntaxError; use Drupal\Core\Cache\Cache; use Drupal\Core\Ajax\HtmlCommand; use Drupal\Core\Form\SubformState; use Drupal\Core\Ajax\AjaxResponse; Loading @@ -15,11 +16,16 @@ use Drupal\Core\Ajax\PrependCommand; use Drupal\Core\Ajax\RedirectCommand; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Extension\ExtensionList; use Drupal\Core\Render\RendererInterface; use Drupal\Core\Template\TwigEnvironment; use Drupal\component_library\OverrideMode; use Drupal\Core\TempStore\PrivateTempStore; use Drupal\Core\Ajax\CloseModalDialogCommand; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Extension\ThemeExtensionList; use Drupal\Core\Extension\ModuleExtensionList; use Drupal\Core\Extension\ProfileExtensionList; use Drupal\Core\Asset\LibraryDiscoveryInterface; use Drupal\Core\Extension\ThemeHandlerInterface; use Drupal\Core\TempStore\PrivateTempStoreFactory; use Drupal\Core\StringTranslation\TranslatableMarkup; Loading @@ -37,6 +43,13 @@ final class ComponentOverrideForm extends EntityForm { use ComponentOverrideFormTrait; /** * Cached ID for libraries gathered from all available/installed extensions. * * @var string */ const COLLECTED_LIBRARIES_CID = 'component_library:component_override_form:collected_libraries'; /** * Theme. * Loading @@ -52,10 +65,20 @@ final class ComponentOverrideForm extends EntityForm { protected PrivateTempStore $cache; protected OverrideMode $overrideMode; private PrepareOverrideEvent $prepareOverrideEvent; protected CacheBackendInterface $cacheDefault; protected LibraryDiscoveryInterface $libraryDiscovery; protected ModuleExtensionList $moduleExtensionList; protected ExtensionList $profileExtensionList; protected ThemeExtensionList $themeExtensionList; public function __construct(ThemeHandlerInterface $theme_handler, ComponentOverrideManager $override_manager, TwigEnvironment $twig, EventDispatcherInterface $event_dispatcher, RendererInterface $renderer, PrivateTempStoreFactory $temp_store, OverrideMode $override_mode) { public function __construct(ThemeHandlerInterface $theme_handler, CacheBackendInterface $cache_default, ComponentOverrideManager $override_manager, LibraryDiscoveryInterface $library_discovery, ModuleExtensionList $module_list, ProfileExtensionList $profile_list, ThemeExtensionList $theme_list, TwigEnvironment $twig, EventDispatcherInterface $event_dispatcher, RendererInterface $renderer, PrivateTempStoreFactory $temp_store, OverrideMode $override_mode) { $this->themeHandler = $theme_handler; $this->cacheDefault = $cache_default; $this->overrideManager = $override_manager; $this->libraryDiscovery = $library_discovery; $this->moduleExtensionList = $module_list; $this->profileExtensionList = $profile_list; $this->themeExtensionList = $theme_list; $this->twig = $twig; $this->dispatcher = $event_dispatcher; $this->renderer = $renderer; Loading @@ -69,7 +92,12 @@ final class ComponentOverrideForm extends EntityForm { public static function create(ContainerInterface $container): self { return new self( $container->get('theme_handler'), $container->get('cache.default'), $container->get('plugin.manager.component_override'), $container->get('library.discovery'), $container->get('extension.list.module'), $container->get('extension.list.profile'), $container->get('extension.list.theme'), $container->get('twig'), $container->get('event_dispatcher'), $container->get('renderer'), Loading Loading @@ -189,7 +217,7 @@ final class ComponentOverrideForm extends EntityForm { (empty($input['template']) && $this->prepareOverrideEvent->isPrepopulated()) && $override ) { $template = $this->getTemplate($override); $template = $this->getTemplate($override, TRUE); $input['template'] = $template; $form_state->setUserInput($input); } Loading Loading @@ -226,6 +254,47 @@ final class ComponentOverrideForm extends EntityForm { '#suffix' => '<output id="override-preview"></output>', ]; if ($override && empty($form_state->get('selected_libraries')[$override])) { $selected_libraries = $form_state->get('selected_libraries') ?: []; $selected_libraries[$override] = $this->parseAttachedLibraries( $this->getTemplate($override, FALSE) ); $form_state->set('selected_libraries', $selected_libraries); } $attached_libraries = $form_state->get('selected_libraries')[$override]; $available_libraries = $this->getAvailableLibraries(); $form['dynamic_elements_container']['libraries_wrapper'] = [ '#type' => 'details', '#prefix' => '<div id="libraries-wrapper">', '#suffix' => '</div>', '#open' => !empty($attached_libraries), '#title' => $this->t('Libraries'), 'libraries_selector' => [ '#type' => 'select', '#title' => $this->t('Add library'), '#empty_option' => $this->t('- Select -'), '#options' => $available_libraries, '#ajax' => [ 'callback' => [$this, 'ajaxUpdateLibraries'], 'wrapper' => 'libraries-wrapper', 'effect' => 'fade', ], ], 'libraries_list' => [ '#prefix' => '<h6>' . $this->t('Attached libraries') . '</h6>', '#type' => 'table', '#header' => [$this->t('Library'), $this->t('Remove')], '#empty' => $this->t('No libraries are attached.'), ], 'libraries' => [ '#type' => 'value', '#value' => $attached_libraries ?? [], ], ]; if ($attached_libraries) { $this->buildLibrariesList($attached_libraries, $form); } $form['#prefix'] = '<div id="status-messages"></div>'; $form['#attached']['library'][] = 'component_library/component_override'; return $form; Loading @@ -235,6 +304,29 @@ final class ComponentOverrideForm extends EntityForm { * {@inheritdoc} */ public function validateForm(array &$form, FormStateInterface $form_state): void { // Validate attached libraries. $override = $form_state->getValue('override') ?? $this->entity->get('override'); $libraries = $form_state->get('selected_libraries'); $trigger = $form_state->getTriggeringElement(); if ((isset($trigger['#name']) && $trigger['#name'] === 'override') && $override) { $theme_registry = $this->overrideManager->getThemeRegistry()[$form_state->getValue('override')]; $template_path = sprintf('%s/%s.html.twig', $theme_registry['path'], $theme_registry['template']); $template = file_get_contents($template_path); $libraries[$override] = $libraries[$override] ?? $this->parseAttachedLibraries($template); } if ((isset($trigger['#name']) && $trigger['#name'] === 'libraries_selector')) { $added_library = $form_state->getValue('libraries_selector'); if (!empty($added_library) && !in_array($added_library, $libraries)) { $libraries[$override][] = $added_library; } } if (isset($trigger['#parents'][1]) && isset($trigger['#name']) && strpos($trigger['#name'], 'library_remove') !== FALSE) { array_splice($libraries[$override], $trigger['#parents'][1], 1); } $form_state->set('selected_libraries', $libraries); // Validate the plugin. $plugin_values = $form_state->getValue('plugin_container'); if (!empty($plugin_values['plugin'])) { Loading Loading @@ -435,6 +527,123 @@ final class ComponentOverrideForm extends EntityForm { return NULL; } /** * AJAX callback to load the libraries list. */ public function ajaxUpdateLibraries(array &$form, FormStateInterface $form_state) { return $form['dynamic_elements_container']['libraries_wrapper']; } /** * Builds the rows of the libraries list widget. * * @param array $selected_libraries * The currently selected libraries. * @param array $form * The form array. */ protected function buildLibrariesList(array $selected_libraries, array &$form) { $row = 0; foreach ($selected_libraries as $key => $library) { $form['dynamic_elements_container']['libraries_wrapper']['libraries_list'][$row]['library'] = [ '#markup' => $library, ]; $button_key = 'library_remove' . $key; $form['dynamic_elements_container']['libraries_wrapper']['libraries_list'][$row][$button_key] = [ '#type' => 'submit', '#value' => $this->t('Remove'), '#name' => $button_key, '#limit_validation_errors' => TRUE, '#executes_submit_callback' => FALSE, '#ajax' => [ 'callback' => [$this, 'ajaxUpdateLibraries'], 'wrapper' => 'libraries-wrapper', 'effect' => 'fade', ], ]; $row++; } } /** * Parses libraries from a given template string. * * @param string $template * The template to parse from. * * @return array * List of libraries. */ protected function parseAttachedLibraries($template) { $matches = []; $libraries = []; \preg_match_all('/attach_library\(.+\)/', $template, $matches); if (!empty($matches[0])) { foreach ($matches[0] as $attach_statement) { $libraries[] = \mb_substr( $attach_statement, \mb_strlen("attach_library('"), \mb_strlen($attach_statement) - \mb_strlen("attach_library('") - \mb_strlen("')") ); } } return $libraries; } /** * Gathers libraries from core, profiles, installed modules and themes. * * @return array * Nested array of libraries in the form of extension => [libraries]. */ protected function getAvailableLibraries() { $cache = $this->cacheDefault->get(static::COLLECTED_LIBRARIES_CID); if (!empty($cache->data)) { return $cache->data; } else { $available_libraries = []; $modules = $this->getInstalledList($this->moduleExtensionList); $profiles = $this->getInstalledList($this->profileExtensionList); $themes = $this->getInstalledList($this->themeExtensionList); $core_libraries = $this->libraryDiscovery->getLibrariesByExtension('core'); foreach ($core_libraries as $library_name => $library) { $available_libraries['core']['core/' . $library_name] = 'core/' . $library_name; } /** @var \Drupal\Core\Extension\Extension $extension */ foreach (\array_merge($modules, $profiles, $themes) as $extension_name => $extension) { $extension_info = (array) $extension; if ($extension->getType() == 'profile'|| !empty($extension_info['status'])) { $extension_libraries = $this->libraryDiscovery->getLibrariesByExtension($extension_name); $libraries_list = []; foreach ($extension_libraries as $library_name => $library) { $libraries_list[$extension_name][$extension_name . '/' . $library_name] = $extension_name . '/' . $library_name; } $available_libraries = \array_merge($available_libraries, $libraries_list); } } $this->cacheDefault->set(static::COLLECTED_LIBRARIES_CID, $available_libraries, Cache::PERMANENT, ['library_info']); return $available_libraries; } } /** * Get installed list. * * @param \Drupal\Core\Extension\ExtensionList $extension_list * An extension list. * * @return array * An array of installed extensions for the given list. */ protected function getInstalledList(ExtensionList $extension_list) { $list = []; foreach ($extension_list->getAllInstalledInfo() as $name => $info) { $list[$name] = $extension_list->get($name); } return $list; } /** * Get template. * Loading @@ -442,13 +651,15 @@ final class ComponentOverrideForm extends EntityForm { * * @param string $override * The theme suggestion being overridden. * @param bool $strip_libraries * Whether to strip attach_library twig statements from the template. * * @return string * The file contents of the template. * * @throws \Twig\Error\LoaderError */ protected function getTemplate(string $override): string { protected function getTemplate(string $override, $strip_libraries): string { $registry = $this->overrideManager->getThemeRegistry(); $pieces = \explode('__', $override); for ($i = \count($pieces); $i > 0; $i--) { Loading @@ -456,6 +667,12 @@ final class ComponentOverrideForm extends EntityForm { if (!empty($registry[$possible_theme_suggestion])) { $path = \sprintf('%s/%s.html.twig', $registry[$possible_theme_suggestion]['path'], $registry[$possible_theme_suggestion]['template']); $template = $this->twig->getLoader()->getSourceContext($path)->getCode(); if ($strip_libraries) { // Strip attach_library statements as libraries get displayed in // dedicated field. $template = \preg_replace('/\{\{ attach_library\(.+\) \}\}[\r\n|\r]/', '', $template); $template = \preg_replace('/\{\{attach_library\(.+\)\}\}[\r\n|\r]/', '', $template); } return $template; } } Loading
src/Twig/Loader.php +19 −3 Original line number Diff line number Diff line Loading @@ -30,9 +30,25 @@ final class Loader implements LoaderInterface, SourceContextLoaderInterface { * {@inheritdoc} */ public function getSourceContext($name): Source { $override = $this->getOverrideByName($name); if ($override) { return new Source($override->get('template'), $name); $override_name = $this->getOverrideNameFromName($name); $overrides = NULL; $theme = $this->themeManager->getActiveTheme(); if ($theme) { $overrides = $this->overrides->loadByProperties([ 'override' => $override_name, 'theme' => $theme->getName(), ]); } if ($overrides) { $override = \reset($overrides); $template = $override->get('template'); $libraries = $override->get('libraries'); $attach_statements = ''; foreach ($libraries as $library) { $attach_statements .= "{{ attach_library('$library') }}\n"; } $template = $attach_statements . $template; return new Source($template, $name); } throw new LoaderError($name); Loading