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;
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.
*/
......
......@@ -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;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment