diff --git a/src/Plugin/UiPatterns/Source/BlockSource.php b/src/Plugin/UiPatterns/Source/BlockSource.php index 12aa4b8b2ba907aaedd802e1255cca24cfe9f4d7..1b8f503cd6cbe2b7fff77649980965c3b19f4465 100644 --- a/src/Plugin/UiPatterns/Source/BlockSource.php +++ b/src/Plugin/UiPatterns/Source/BlockSource.php @@ -9,6 +9,7 @@ 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; @@ -36,6 +37,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface; )] class BlockSource extends SourcePluginBase { + const CACHE_KEY = 'ui_patterns_block_metadata'; + /** * Block to be rendered. * @@ -57,6 +60,7 @@ 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); } @@ -80,7 +84,8 @@ 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('context.handler'), + $container->get('cache.default'), ); return $instance; } @@ -274,6 +279,34 @@ class BlockSource extends SourcePluginBase { $context_for_block_discovery = $this->context; $definitions = $this->blockManager->getFilteredDefinitions('ui_patterns', $context_for_block_discovery, []); $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", "ui_patterns", "ui_patterns_blocks", "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); /* @@ -284,6 +317,28 @@ class BlockSource extends SourcePluginBase { 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. */