diff --git a/src/Plugin/UiPatterns/Source/BlockSource.php b/src/Plugin/UiPatterns/Source/BlockSource.php index f553ea00dc78be8b4d5e9fd0f38991a9a3201b1a..beaaac3cda9fba4f9faf3a0e029b42b452d5fc12 100644 --- a/src/Plugin/UiPatterns/Source/BlockSource.php +++ b/src/Plugin/UiPatterns/Source/BlockSource.php @@ -9,7 +9,6 @@ use Drupal\Component\Utility\Html; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Block\BlockManagerInterface; use Drupal\Core\Block\BlockPluginInterface; -use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Entity\EntityDisplayBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\SubformState; @@ -32,13 +31,10 @@ use Symfony\Component\DependencyInjection\ContainerInterface; #[Source( id: 'block', label: new TranslatableMarkup('Block'), - description: new TranslatableMarkup('instantiate a block'), + description: new TranslatableMarkup('instantiate a block plugin'), prop_types: ['slot'] )] class BlockSource extends SourcePluginBase { - - const CACHE_KEY = 'ui_patterns_block_metadata'; - /** * Block to be rendered. * @@ -60,7 +56,6 @@ class BlockSource extends SourcePluginBase { protected BlockManagerInterface $blockManager, protected PluginFormFactoryInterface $pluginFormFactory, protected ContextHandlerInterface $contextHandler, - protected CacheBackendInterface $cacheBackend, ) { parent::__construct($configuration, $plugin_id, $plugin_definition, $propTypeManager, $contextRepository, $routeMatch, $sampleEntityGenerator); } @@ -84,8 +79,7 @@ class BlockSource extends SourcePluginBase { $container->get('ui_patterns.sample_entity_generator'), $container->get('plugin.manager.block'), $container->get('plugin_form.factory'), - $container->get('context.handler'), - $container->get('cache.default'), + $container->get('context.handler') ); return $instance; } @@ -278,67 +272,19 @@ class BlockSource extends SourcePluginBase { protected function listBlockDefinitions() : array { $context_for_block_discovery = $this->context; $definitions = $this->blockManager->getFilteredDefinitions('ui_patterns', $context_for_block_discovery, []); + // Filter plugins based on the flag 'ui_patterns_compatibility'. + // @see function ui_patterns_plugin_filter_block__ui_patterns_alter + // from ui_patterns.module file + $definitions = array_filter($definitions, function ($definition, $plugin_id) { + return !isset($definition['_ui_patterns_compatible']) || $definition['_ui_patterns_compatible']; + }, ARRAY_FILTER_USE_BOTH); + // Filter based on contexts. $definitions = $this->contextHandler->filterPluginDefinitionsByContexts($context_for_block_discovery, $definitions); - // --- - $metadataAboutBlocks = []; - $cached_version = $this->cacheBackend->get(static::CACHE_KEY); - if ($cached_version) { - $metadataAboutBlocks = $cached_version->data; - } - // @todo Add a way to alter this list properly (hook, event, etc). - $whitelisted_blocks = [ - "id" => ["search_form_block", "system_menu_block"], - "provider" => ["layout_builder", "views"], - ]; - foreach ($definitions as $plugin_id => $definition) { - if (!isset($metadataAboutBlocks[$plugin_id])) { - $blockAccepted = TRUE; - if (in_array($definition['provider'], $whitelisted_blocks["provider"]) || - in_array($definition["id"], $whitelisted_blocks["id"])) { - $blockAccepted = TRUE; - } - elseif (isset($definition['class']) && static::classImplementsMethod($definition['class'], 'blockSubmit')) { - $blockAccepted = FALSE; - } - $metadataAboutBlocks[$plugin_id] = $blockAccepted; - } - } - $definitions = array_filter($definitions, function ($plugin_id) use ($metadataAboutBlocks) { - return $metadataAboutBlocks[$plugin_id]; - }, ARRAY_FILTER_USE_KEY); - $this->cacheBackend->set(static::CACHE_KEY, $metadataAboutBlocks, CacheBackendInterface::CACHE_PERMANENT); // Order by category, and then by admin label. $definitions = $this->blockManager->getSortedDefinitions($definitions); - /* - // Filter out definitions that are not intended to be placed by the UI. - $definitions = array_filter($definitions, function (array $definition) { - return empty($definition['_block_ui_hidden']); - });*/ return $definitions; } - /** - * Checks if a class implements a method. - * - * @param string $class_name - * The fully qualified class name. - * @param string $method_name - * Method name to check. - * - * @return bool - * TRUE if the class implements the method, FALSE otherwise. - */ - protected static function classImplementsMethod(string $class_name, string $method_name) : bool { - try { - $reflector = new \ReflectionClass($class_name); - return ($reflector->hasMethod($method_name) && $reflector->getMethod($method_name)->getDeclaringClass()->getName() === $class_name); - } - catch (\ReflectionException $e) { - // Handle exception if class does not exist or method not found. - } - return TRUE; - } - /** * Get options for block select. */ diff --git a/ui_patterns.module b/ui_patterns.module index f75c3c9f6595798069b9b3b1ef5a30253e1a3c6b..324fdc4c8727e20c5001310cb90f12dbc8c2a963 100644 --- a/ui_patterns.module +++ b/ui_patterns.module @@ -21,9 +21,18 @@ function ui_patterns_element_info_alter(array &$types) { } /** + * Prepare list of block plugins returned when using consumer 'ui_patterns'. + * + * @see \Drupal\ui_patterns\Plugin\UiPatterns\Source\BlockSource::listBlockDefinitions() + * * Implements hook_plugin_filter_TYPE__CONSUMER_alter(). */ function ui_patterns_plugin_filter_block__ui_patterns_alter(array &$definitions, array $extra) { + // @todo Determine the 'inline_block' blocks should be allowed outside + // of layout_builder https://www.drupal.org/node/2979142. + $definitions = array_filter($definitions, function ($definition) { + return $definition['id'] !== 'inline_block'; + }); // Remove blocks that are not useful within Layout Builder. unset($definitions['system_messages_block']); unset($definitions['help_block']); @@ -33,19 +42,42 @@ function ui_patterns_plugin_filter_block__ui_patterns_alter(array &$definitions, unset($definitions['system_main_block']); // @todo Restore the page title block in https://www.drupal.org/node/2938129. unset($definitions['page_title_block']); -} + // Add a boolean marker '_ui_patterns_compatible' to all remaining definitions + // Other modules can use the same hook to modify this value. + // This allows to whitelist or blacklist blocks. + $whitelisted_blocks = [ + "id" => ["search_form_block", "system_menu_block"], + "provider" => ["views"], + ]; + foreach ($definitions as $id => &$definition) { + if (isset($definitions[$id]['_ui_patterns_compatible'])) { + // When a block plugin already has 'ui_patterns_compatibilty' + // It probably means it has been marked by another code. + // Honor what the other code has done and do not override. + continue; + } + $compatibilityFlag = TRUE; + if (in_array($definition['provider'], $whitelisted_blocks["provider"]) || + in_array($definition["id"], $whitelisted_blocks["id"])) { + // Those blacks are accepted. + } + elseif (isset($definition['class'])) { + try { + $class_name = $definition['class']; + $reflector = new \ReflectionClass($class_name); + if ($reflector->hasMethod("blockSubmit") && ($reflector->getMethod("blockSubmit")->getDeclaringClass()->getName() === $class_name)) { + // Blocks having a custom implementation are discarded, + // because some blocks may store in their configuration + // a different structure than the form structure. + // We can't support this properly yet. + $compatibilityFlag = FALSE; + } + } + catch (\ReflectionException $e) { -/** - * Implements hook_plugin_filter_TYPE_alter(). - */ -function ui_patterns_plugin_filter_block_alter(array &$definitions, array $extra, $consumer) { - // @todo Determine the 'inline_block' blocks should be allowed outside - // of layout_builder https://www.drupal.org/node/2979142. - if ($consumer !== 'layout_builder' || !isset($extra['list']) || $extra['list'] !== 'inline_blocks') { - foreach ($definitions as $id => $definition) { - if ($definition['id'] === 'inline_block') { - unset($definitions[$id]); } } + // Filter out blocks with _block_ui_hidden ? + $definitions[$id]['_ui_patterns_compatible'] = $compatibilityFlag; } }