Loading src/Plugin/Block/ComponentBlock.php +82 −12 Original line number Diff line number Diff line Loading @@ -5,14 +5,19 @@ namespace Drupal\cl_block\Plugin\Block; use Drupal\cl_components\Component\Component; use Drupal\cl_components\Component\ComponentDiscovery; use Drupal\cl_components\Exception\ComponentNotFoundException; use Drupal\Component\Plugin\Exception\ContextException; use Drupal\Component\Serialization\Json; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Block\BlockBase; use Drupal\Core\Cache\RefinableCacheableDependencyInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Render\BubbleableMetadata; use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\Core\Utility\Token; use Psr\Log\LoggerInterface; use SchemaForms\Drupal\FormGeneratorDrupal; use Shaper\Transformation\TransformationInterface; use Shaper\Util\Context; use Symfony\Component\DependencyInjection\ContainerInterface; Loading @@ -22,6 +27,14 @@ use Symfony\Component\DependencyInjection\ContainerInterface; * @Block( * id = "cl_component_block", * deriver = "\Drupal\cl_block\Plugin\Derivative\ComponentBlockDeriver", * context_definitions = { * "language" = @ContextDefinition("language", required = FALSE, label = @Translation("Language")), * "node" = @ContextDefinition("entity:node", required = FALSE, * label = @Translation("Node")), * "user" = @ContextDefinition("entity:user", required = FALSE, * label = @Translation("User Context"), constraints = { "NotNull" = {} }, * ), * } * ) * * @internal Loading @@ -46,9 +59,9 @@ class ComponentBlock extends BlockBase implements ContainerFactoryPluginInterfac /** * The form generator from schema. * * @var \SchemaForms\Drupal\FormGeneratorDrupal * @var \Shaper\Transformation\TransformationInterface */ protected FormGeneratorDrupal $formGenerator; protected TransformationInterface $formGenerator; /** * The component Loading @@ -64,12 +77,17 @@ class ComponentBlock extends BlockBase implements ContainerFactoryPluginInterfac */ protected LoggerInterface $logger; /** * @var \Drupal\Core\Utility\Token */ protected Token $token; /** * Does the site support inject. * * @var bool */ protected $supportsInject = FALSE; protected bool $supportsInject = FALSE; /** * Constructs a new FieldBlock. Loading @@ -84,6 +102,10 @@ class ComponentBlock extends BlockBase implements ContainerFactoryPluginInterfac * The component discovery. * @param \Psr\Log\LoggerInterface $logger * The logger. * @param \Shaper\Transformation\TransformationInterface $form_generator * The form generator. * @param \Drupal\Core\Utility\Token $token * The token replacement service. * @param bool $supports_inject * TRUE if the site supports the inject syntax. */ Loading @@ -93,12 +115,14 @@ class ComponentBlock extends BlockBase implements ContainerFactoryPluginInterfac $plugin_definition, ComponentDiscovery $component_discovery, LoggerInterface $logger, FormGeneratorDrupal $form_generator, TransformationInterface $form_generator, Token $token, bool $supports_inject ) { $this->logger = $logger; $this->componentDiscovery = $component_discovery; $this->formGenerator = $form_generator; $this->token = $token; // Get the entity type and field name from the plugin ID. [, $component_name] = explode(static::DERIVATIVE_SEPARATOR, $plugin_id, 4); Loading @@ -119,6 +143,7 @@ class ComponentBlock extends BlockBase implements ContainerFactoryPluginInterfac $container->get(ComponentDiscovery::class), $container->get('logger.channel.cl_block'), $container->get('cl_block.form_generator'), $container->get('token'), $container->get('module_handler')->moduleExists('cl_inject') ); } Loading @@ -132,17 +157,21 @@ class ComponentBlock extends BlockBase implements ContainerFactoryPluginInterfac $twig_blocks = $config['component']['twig_blocks'] ?? []; $variant = $config['component']['variant'] ?? NULL; $component = $this->getComponent(); $bubbleable_metadata = new BubbleableMetadata(); $template = $this->generateComponentTemplate( $component, $variant, $twig_blocks, $context $context, $bubbleable_metadata ); return [ $build = [ '#type' => 'inline_template', '#template' => $template, '#context' => $context, ]; $bubbleable_metadata->applyTo($build); return $build; } /** Loading Loading @@ -213,7 +242,12 @@ class ComponentBlock extends BlockBase implements ContainerFactoryPluginInterfac ], ]; try { $component = $this->getComponent(); } catch (ComponentNotFoundException $e) { return $form; } // Here is where we derive the form from the metadata. $metadata = $component->getMetadata(); $schemas = $metadata->getSchemas(); Loading @@ -238,7 +272,10 @@ class ComponentBlock extends BlockBase implements ContainerFactoryPluginInterfac '#suffix' => '</div>', '#weight' => 5, ]; $form['tree'] = [ '#theme' => 'token_tree_link', '#token_types' => ['node', 'user'], ]; return $form; } Loading Loading @@ -385,23 +422,56 @@ class ComponentBlock extends BlockBase implements ContainerFactoryPluginInterfac * * @throws \Drupal\cl_components\Exception\TemplateNotFoundException */ private function generateComponentTemplate(Component $component, string $variant, array $twig_blocks, array $context): string { private function generateComponentTemplate(Component $component, string $variant, array $twig_blocks, array $context, RefinableCacheableDependencyInterface $bubbleable_metadata): string { $token_data = array_filter($this->getContextValues()); $metadata = $component->getMetadata(); $component_path = $metadata->getPath(); $template_name = $component->getTemplateName($variant); $template_path = $component_path . DIRECTORY_SEPARATOR . $template_name; $template = '{# This template was dynamically generated by cl_block #}' . PHP_EOL; // Replace possible tokens in the context mapping. $context = array_map( fn($item) => $this->token->replacePlain( $item, $token_data, ['clear' => TRUE], $bubbleable_metadata ), $context ); $template .= empty($context) ? sprintf('{%% embed \'%s\' %%}', $template_path) : sprintf('{%% embed \'%s\' with %s %%}', $template_path, Json::encode($context)); $template .= PHP_EOL; $options = ['clear' => TRUE]; try { // If there is a language in the context of the block, use it. $language = $this->getContextValue('language'); if ($language instanceof LanguageInterface) { $options['langcode'] = $language->getId(); } } catch (ContextException $e) { // Intentionally left blank. } foreach ($twig_blocks as $block_name => $block_value) { $bl_val = $this->token->replace( $block_value['value'], $token_data, $options, $bubbleable_metadata ); $block_build = [ '#type' => 'processed_text', '#text' => $block_value['value'], '#text' => $bl_val, '#format' => is_array($block_value) ? $block_value['format'] : 'full_html_with_twig', ]; try { $value = \Drupal::service('renderer')->render($block_build); } catch (\Exception $e) { $value = $bl_val; } $template .= " {% block $block_name %}" . PHP_EOL . " $value" . PHP_EOL . " {% endblock %}" . PHP_EOL; Loading Loading
src/Plugin/Block/ComponentBlock.php +82 −12 Original line number Diff line number Diff line Loading @@ -5,14 +5,19 @@ namespace Drupal\cl_block\Plugin\Block; use Drupal\cl_components\Component\Component; use Drupal\cl_components\Component\ComponentDiscovery; use Drupal\cl_components\Exception\ComponentNotFoundException; use Drupal\Component\Plugin\Exception\ContextException; use Drupal\Component\Serialization\Json; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Block\BlockBase; use Drupal\Core\Cache\RefinableCacheableDependencyInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Render\BubbleableMetadata; use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\Core\Utility\Token; use Psr\Log\LoggerInterface; use SchemaForms\Drupal\FormGeneratorDrupal; use Shaper\Transformation\TransformationInterface; use Shaper\Util\Context; use Symfony\Component\DependencyInjection\ContainerInterface; Loading @@ -22,6 +27,14 @@ use Symfony\Component\DependencyInjection\ContainerInterface; * @Block( * id = "cl_component_block", * deriver = "\Drupal\cl_block\Plugin\Derivative\ComponentBlockDeriver", * context_definitions = { * "language" = @ContextDefinition("language", required = FALSE, label = @Translation("Language")), * "node" = @ContextDefinition("entity:node", required = FALSE, * label = @Translation("Node")), * "user" = @ContextDefinition("entity:user", required = FALSE, * label = @Translation("User Context"), constraints = { "NotNull" = {} }, * ), * } * ) * * @internal Loading @@ -46,9 +59,9 @@ class ComponentBlock extends BlockBase implements ContainerFactoryPluginInterfac /** * The form generator from schema. * * @var \SchemaForms\Drupal\FormGeneratorDrupal * @var \Shaper\Transformation\TransformationInterface */ protected FormGeneratorDrupal $formGenerator; protected TransformationInterface $formGenerator; /** * The component Loading @@ -64,12 +77,17 @@ class ComponentBlock extends BlockBase implements ContainerFactoryPluginInterfac */ protected LoggerInterface $logger; /** * @var \Drupal\Core\Utility\Token */ protected Token $token; /** * Does the site support inject. * * @var bool */ protected $supportsInject = FALSE; protected bool $supportsInject = FALSE; /** * Constructs a new FieldBlock. Loading @@ -84,6 +102,10 @@ class ComponentBlock extends BlockBase implements ContainerFactoryPluginInterfac * The component discovery. * @param \Psr\Log\LoggerInterface $logger * The logger. * @param \Shaper\Transformation\TransformationInterface $form_generator * The form generator. * @param \Drupal\Core\Utility\Token $token * The token replacement service. * @param bool $supports_inject * TRUE if the site supports the inject syntax. */ Loading @@ -93,12 +115,14 @@ class ComponentBlock extends BlockBase implements ContainerFactoryPluginInterfac $plugin_definition, ComponentDiscovery $component_discovery, LoggerInterface $logger, FormGeneratorDrupal $form_generator, TransformationInterface $form_generator, Token $token, bool $supports_inject ) { $this->logger = $logger; $this->componentDiscovery = $component_discovery; $this->formGenerator = $form_generator; $this->token = $token; // Get the entity type and field name from the plugin ID. [, $component_name] = explode(static::DERIVATIVE_SEPARATOR, $plugin_id, 4); Loading @@ -119,6 +143,7 @@ class ComponentBlock extends BlockBase implements ContainerFactoryPluginInterfac $container->get(ComponentDiscovery::class), $container->get('logger.channel.cl_block'), $container->get('cl_block.form_generator'), $container->get('token'), $container->get('module_handler')->moduleExists('cl_inject') ); } Loading @@ -132,17 +157,21 @@ class ComponentBlock extends BlockBase implements ContainerFactoryPluginInterfac $twig_blocks = $config['component']['twig_blocks'] ?? []; $variant = $config['component']['variant'] ?? NULL; $component = $this->getComponent(); $bubbleable_metadata = new BubbleableMetadata(); $template = $this->generateComponentTemplate( $component, $variant, $twig_blocks, $context $context, $bubbleable_metadata ); return [ $build = [ '#type' => 'inline_template', '#template' => $template, '#context' => $context, ]; $bubbleable_metadata->applyTo($build); return $build; } /** Loading Loading @@ -213,7 +242,12 @@ class ComponentBlock extends BlockBase implements ContainerFactoryPluginInterfac ], ]; try { $component = $this->getComponent(); } catch (ComponentNotFoundException $e) { return $form; } // Here is where we derive the form from the metadata. $metadata = $component->getMetadata(); $schemas = $metadata->getSchemas(); Loading @@ -238,7 +272,10 @@ class ComponentBlock extends BlockBase implements ContainerFactoryPluginInterfac '#suffix' => '</div>', '#weight' => 5, ]; $form['tree'] = [ '#theme' => 'token_tree_link', '#token_types' => ['node', 'user'], ]; return $form; } Loading Loading @@ -385,23 +422,56 @@ class ComponentBlock extends BlockBase implements ContainerFactoryPluginInterfac * * @throws \Drupal\cl_components\Exception\TemplateNotFoundException */ private function generateComponentTemplate(Component $component, string $variant, array $twig_blocks, array $context): string { private function generateComponentTemplate(Component $component, string $variant, array $twig_blocks, array $context, RefinableCacheableDependencyInterface $bubbleable_metadata): string { $token_data = array_filter($this->getContextValues()); $metadata = $component->getMetadata(); $component_path = $metadata->getPath(); $template_name = $component->getTemplateName($variant); $template_path = $component_path . DIRECTORY_SEPARATOR . $template_name; $template = '{# This template was dynamically generated by cl_block #}' . PHP_EOL; // Replace possible tokens in the context mapping. $context = array_map( fn($item) => $this->token->replacePlain( $item, $token_data, ['clear' => TRUE], $bubbleable_metadata ), $context ); $template .= empty($context) ? sprintf('{%% embed \'%s\' %%}', $template_path) : sprintf('{%% embed \'%s\' with %s %%}', $template_path, Json::encode($context)); $template .= PHP_EOL; $options = ['clear' => TRUE]; try { // If there is a language in the context of the block, use it. $language = $this->getContextValue('language'); if ($language instanceof LanguageInterface) { $options['langcode'] = $language->getId(); } } catch (ContextException $e) { // Intentionally left blank. } foreach ($twig_blocks as $block_name => $block_value) { $bl_val = $this->token->replace( $block_value['value'], $token_data, $options, $bubbleable_metadata ); $block_build = [ '#type' => 'processed_text', '#text' => $block_value['value'], '#text' => $bl_val, '#format' => is_array($block_value) ? $block_value['format'] : 'full_html_with_twig', ]; try { $value = \Drupal::service('renderer')->render($block_build); } catch (\Exception $e) { $value = $bl_val; } $template .= " {% block $block_name %}" . PHP_EOL . " $value" . PHP_EOL . " {% endblock %}" . PHP_EOL; Loading