Skip to content
Snippets Groups Projects
Commit 8436ee8d authored by Mikael Meulle's avatar Mikael Meulle
Browse files

better filter methodology

parent 4052117e
No related branches found
No related tags found
1 merge request!147Disallow some blocks
...@@ -9,7 +9,6 @@ use Drupal\Component\Utility\Html; ...@@ -9,7 +9,6 @@ use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Block\BlockManagerInterface; use Drupal\Core\Block\BlockManagerInterface;
use Drupal\Core\Block\BlockPluginInterface; use Drupal\Core\Block\BlockPluginInterface;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Entity\EntityDisplayBase; use Drupal\Core\Entity\EntityDisplayBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState; use Drupal\Core\Form\SubformState;
...@@ -32,13 +31,10 @@ use Symfony\Component\DependencyInjection\ContainerInterface; ...@@ -32,13 +31,10 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
#[Source( #[Source(
id: 'block', id: 'block',
label: new TranslatableMarkup('Block'), label: new TranslatableMarkup('Block'),
description: new TranslatableMarkup('instantiate a block'), description: new TranslatableMarkup('instantiate a block plugin'),
prop_types: ['slot'] prop_types: ['slot']
)] )]
class BlockSource extends SourcePluginBase { class BlockSource extends SourcePluginBase {
const CACHE_KEY = 'ui_patterns_block_metadata';
/** /**
* Block to be rendered. * Block to be rendered.
* *
...@@ -60,7 +56,6 @@ class BlockSource extends SourcePluginBase { ...@@ -60,7 +56,6 @@ class BlockSource extends SourcePluginBase {
protected BlockManagerInterface $blockManager, protected BlockManagerInterface $blockManager,
protected PluginFormFactoryInterface $pluginFormFactory, protected PluginFormFactoryInterface $pluginFormFactory,
protected ContextHandlerInterface $contextHandler, protected ContextHandlerInterface $contextHandler,
protected CacheBackendInterface $cacheBackend,
) { ) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $propTypeManager, $contextRepository, $routeMatch, $sampleEntityGenerator); parent::__construct($configuration, $plugin_id, $plugin_definition, $propTypeManager, $contextRepository, $routeMatch, $sampleEntityGenerator);
} }
...@@ -84,8 +79,7 @@ class BlockSource extends SourcePluginBase { ...@@ -84,8 +79,7 @@ class BlockSource extends SourcePluginBase {
$container->get('ui_patterns.sample_entity_generator'), $container->get('ui_patterns.sample_entity_generator'),
$container->get('plugin.manager.block'), $container->get('plugin.manager.block'),
$container->get('plugin_form.factory'), $container->get('plugin_form.factory'),
$container->get('context.handler'), $container->get('context.handler')
$container->get('cache.default'),
); );
return $instance; return $instance;
} }
...@@ -278,67 +272,19 @@ class BlockSource extends SourcePluginBase { ...@@ -278,67 +272,19 @@ class BlockSource extends SourcePluginBase {
protected function listBlockDefinitions() : array { protected function listBlockDefinitions() : array {
$context_for_block_discovery = $this->context; $context_for_block_discovery = $this->context;
$definitions = $this->blockManager->getFilteredDefinitions('ui_patterns', $context_for_block_discovery, []); $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); $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. // Order by category, and then by admin label.
$definitions = $this->blockManager->getSortedDefinitions($definitions); $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; 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. * Get options for block select.
*/ */
......
...@@ -21,9 +21,18 @@ function ui_patterns_element_info_alter(array &$types) { ...@@ -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(). * Implements hook_plugin_filter_TYPE__CONSUMER_alter().
*/ */
function ui_patterns_plugin_filter_block__ui_patterns_alter(array &$definitions, array $extra) { 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. // Remove blocks that are not useful within Layout Builder.
unset($definitions['system_messages_block']); unset($definitions['system_messages_block']);
unset($definitions['help_block']); unset($definitions['help_block']);
...@@ -33,19 +42,42 @@ function ui_patterns_plugin_filter_block__ui_patterns_alter(array &$definitions, ...@@ -33,19 +42,42 @@ function ui_patterns_plugin_filter_block__ui_patterns_alter(array &$definitions,
unset($definitions['system_main_block']); unset($definitions['system_main_block']);
// @todo Restore the page title block in https://www.drupal.org/node/2938129. // @todo Restore the page title block in https://www.drupal.org/node/2938129.
unset($definitions['page_title_block']); 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;
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment