block_generation-8.x-1.x-dev/block_generation.module
block_generation.module
<?php use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\Display\EntityViewDisplayInterface; use Drupal\views\ViewExecutable; /** * @file * Provides Block improvements. */ use Drupal\block_content\Entity\BlockContent; use Drupal\Core\Cache\Cache; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Render\Element; use Drupal\Core\Template\Attribute; /** * Implements hook_ENTITY_TYPE_view_alter(). * * @param array $build * @param \Drupal\Core\Entity\EntityInterface $entity * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display */ function block_generation_block_content_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) { $block_generation_bundles = _block_generation_bundles(); if ($entity instanceof BlockContent && array_key_exists($entity->bundle(), $block_generation_bundles)) { /** @var \Drupal\Core\Utility\ThemeRegistry $theme_registry */ $theme_registry = \Drupal::service('theme.registry')->getRuntime(); // Generalize the entity-type-specific defaults for easier default theming. if ($theme_registry->has('block__block_generation__' . $entity->bundle())) { $build['#theme'] = 'block__block_generation__' . $entity->bundle(); } // Update the theme for specific fields. if (isset($build['title']['#object']) && $theme_registry->has('field__block_generation__title')) { $build['title']['#theme'] = 'field__block_generation__title'; } if (isset($build['link']['#object']) && $theme_registry->has('field__block_generation__link')) { $build['link']['#theme'] = 'field__block_generation__link'; } } } /** * Suggests block templates depending on "machine_name" field of the block. * Implements hook_theme_suggestions_HOOK_alter(). * * @param array $suggestions * @param array $variables */ function block_generation_theme_suggestions_block_alter(array &$suggestions, array $variables) { if (isset($variables['elements']['content']['#block_content']) && $variables['elements']['content']['#block_content']->hasField('machine_name')) { $block_machine_name = $variables['elements']['content']['#block_content']->get('machine_name')->value; $suggestions[] = 'block__block_content__' . $block_machine_name; }; } /** * Adds classes to content blocks. * Implements hook_preprocess_HOOK() for block templates. * * @param $variables */ function block_generation_preprocess_block(&$variables) { if ($variables['base_plugin_id'] == 'block_content' && $variables['content']['#block_content']->hasField('machine_name')) { $variables['attributes']['class'][] = 'block--' . strtr($variables['content']['#block_content']->get('machine_name')->value, '_', '-'); } } /** * Helper function returns all block content bundles what are introduced in block_generation. */ function _block_generation_bundles() { $block_generation_bundles = &drupal_static(__FUNCTION__, []); if (!$block_generation_bundles) { $cid = 'block_generation:bundles'; if ($cache = \Drupal::cache()->get($cid)) { $block_generation_bundles = $cache->data; } else { $block_generation_bundles = \Drupal::entityTypeManager() ->getStorage('block_content_type') ->loadMultiple(); foreach ($block_generation_bundles as $block_generation_bundle) { if (strpos($block_generation_bundle->id(), 'block_generation_') === false) { unset($block_generation_bundles[$block_generation_bundle->id()]); } } \Drupal::cache() ->set($cid, $block_generation_bundles, Cache::PERMANENT, [ 'block_generation', 'block_generation_bundles', ]); } } return $block_generation_bundles; } /** * Alter the theme registry to add templates for existing bundles of custom blocks. * Implements hook_theme_registry_alter(). * * @param $theme_registry * * @see \Drupal\Core\Theme\Registry::processExtension() * @see hook_theme() */ function block_generation_theme_registry_alter(&$theme_registry) { $block_generation_theme_registry = &drupal_static(__FUNCTION__, []); if (!$block_generation_theme_registry) { $cid = 'block_generation:theme_registry'; $block_generation_theme_registry = []; if ($cache = \Drupal::cache()->get($cid)) { $block_generation_theme_registry = $cache->data; } else { $module_handler = \Drupal::moduleHandler(); $app_root = \Drupal::getContainer()->getParameter('app.root'); // Make theme registry changes for every bundle of block content introduced in block_generation. foreach (_block_generation_bundles() as $bundle => $definition) { // Possible modules name what can contain templates and preprocess // functions for different bundle definitions of block_content. // So we're going through the modules from main to specific. $block_generation_modules = ['block_generation', $bundle]; foreach ($block_generation_modules as $block_generation_module) { // Make theme registry changes if the module enabled. if ($module_handler->moduleExists($block_generation_module)) { // Possible file path of the template name containing the bundle name. $template_name = 'block--block-generation--' . strtr($bundle, '_', '-'); $file_path = $app_root . '/' . \Drupal::service('extension.list.module')->getPath($block_generation_module) . '/templates/' . $template_name . '.html.twig'; // If file exists then make registry changes. if (file_exists($file_path)) { // Add possible preprocesses, template with template path. $block_generation_theme_registry['block__block_generation__' . $bundle] = [ 'path' => \Drupal::service('extension.list.module')->getPath($block_generation_module) . '/templates', 'preprocess functions' => [ 'template_preprocess', 'template_preprocess_block__block_generation', 'template_preprocess_block__block_generation__' . $bundle, ], 'render element' => 'elements', 'template' => $template_name, 'theme path' => \Drupal::service('extension.list.module')->getPath($block_generation_module), 'type' => 'module', ]; } // Make theme registry changes for every view mode of block_content. $view_modes = \Drupal::service('entity_display.repository')->getViewModeOptions('block_content'); foreach ($view_modes as $view_mode_name => $view_mode_definition) { // Possible file path of the template name containing the bundle name and view mode name. $template_name = 'block--block-generation--' . strtr($bundle, '_', '-') . '--' . strtr($view_mode_name, '_', '-'); $file_path = $app_root . '/' . \Drupal::service('extension.list.module')->getPath($block_generation_module) . '/templates/' . $template_name . '.html.twig'; // If file exists then make registry changes. if (file_exists($file_path)) { // Add possible preprocesses, template with template path. $block_generation_theme_registry['block__block_generation__' . $bundle . '__' . $view_mode_name] = [ 'path' => \Drupal::service('extension.list.module')->getPath($block_generation_module) . '/templates', 'preprocess functions' => [ 'template_preprocess', 'template_preprocess_block__block_generation', 'template_preprocess_block__block_generation__' . $bundle, 'template_preprocess_block__block_generation__' . $bundle . '__' . $view_mode_name, ], 'render element' => 'elements', 'template' => $template_name, 'theme path' => \Drupal::service('extension.list.module')->getPath($block_generation_module), 'type' => 'module', ]; } } // Make theme registry changes for every field of block content. $fields = \Drupal::service('entity_field.manager')->getFieldStorageDefinitions('block_content'); foreach ($fields as $fieldName => $fieldDefinition) { // Possible file path of the template name containing the field name. $template_name = 'field--block-generation--' . strtr($fieldName, '_', '-'); $file_path = $app_root . '/' . \Drupal::service('extension.list.module')->getPath($block_generation_module) . '/templates/' . $template_name . '.html.twig'; // If file exists then make registry changes. if (file_exists($file_path)) { // Add possible preprocesses, template with template path. $block_generation_theme_registry['field__block_generation__' . $fieldName] = [ 'path' => \Drupal::service('extension.list.module')->getPath($block_generation_module) . '/templates', 'preprocess functions' => [ 'template_preprocess', 'template_preprocess_field', 'template_preprocess_field__block_generation', 'template_preprocess_field__block_generation__' . $fieldName, ], 'render element' => 'element', 'template' => $template_name, 'theme path' => \Drupal::service('extension.list.module')->getPath($block_generation_module), 'type' => 'module', ]; } // Possible file path of the template name containing the field name. $template_name = 'field--block-generation--' . strtr($bundle, '_', '-') . '--' . strtr($fieldName, '_', '-'); $file_path = $app_root . '/' . \Drupal::service('extension.list.module')->getPath($block_generation_module) . '/templates/' . $template_name . '.html.twig'; // If file exists then make registry changes. if (file_exists($file_path)) { // Add possible preprocesses, template with template path. $block_generation_theme_registry['field__block_generation__' . $bundle . '__' . $fieldName] = [ 'path' => \Drupal::service('extension.list.module')->getPath($block_generation_module) . '/templates', 'preprocess functions' => [ 'template_preprocess', 'template_preprocess_field', 'template_preprocess_field__block_generation', 'template_preprocess_field__block_generation__' . $bundle, 'template_preprocess_field__block_generation__' . $bundle . '__' . $fieldName, ], 'render element' => 'element', 'template' => $template_name, 'theme path' => \Drupal::service('extension.list.module')->getPath($block_generation_module), 'type' => 'module', ]; } } } } } // Set the cache for block_generation_theme_registry. \Drupal::cache() ->set($cid, $block_generation_theme_registry, Cache::PERMANENT, [ 'block_generation', 'block_generation_theme_registry', ]); } } $theme_registry += $block_generation_theme_registry; } /** * Process block content settings. * Implements hook_preprocess_HOOK(). * * @param array $variables */ function template_preprocess_block__block_generation(array &$variables) { if (isset($variables['elements']['#block_content']) && $variables['elements']['#block_content'] instanceof BlockContent) { // Block content object. /** @var \Drupal\block_content\Entity\BlockContent $block */ $block = $variables['elements']['#block_content']; $wrappers = [ 'wrapper_external', 'wrapper_main', 'wrapper_internal', 'wrapper_inner', ]; foreach ($wrappers as $wrapper) { _block_generation_process_wrapper($wrapper, $block, $variables); // _block_generation_process_effects($wrapper, $block, $variables); } if (isset($variables['settings']['wrapper_main']['attributes']) && $variables['settings']['wrapper_main']['attributes']) { $variables['attributes'] = $variables['settings']['wrapper_main']['attributes']->toArray() + $variables['attributes']; } } } /** * Wrap the value with a tag and set the attributes to the fields. * Implements hook_preprocess_HOOK(). * * @param array $variables */ function template_preprocess_field__block_generation(array &$variables) { if (isset($variables['element']['#object']) && $variables['element']['#object'] instanceof BlockContent) { // BlockContent object. /** @var \Drupal\block_content\Entity\BlockContent $block */ $block = $variables['element']['#object']; $wrappers = [ 'wrapper_' . $variables['field_name'], $variables['field_name'], ]; foreach ($wrappers as $wrapper) { _block_generation_process_wrapper($wrapper, $block, $variables); } // Process contexts. $block_context = _block_generation_get_contexts($variables['element']['#object']); // Replaces tokens with appropriate values. if (is_array($variables['items']) && is_array($block_context) && $block_context) { foreach ($variables['items'] as &$item) { $item['content']['#context']['value'] = \Drupal::token()->replace($item['content']['#context']['value'], $block_context); } } } } /** * Prepares variables for views fields templates. * * Default template: views-view-fields.html.twig. * * @param array $variables * An associative array containing: * - view: The view object. * - options: An array of options. Each option contains: * - inline: An array that contains the fields that are to be * displayed inline. * - default_field_elements: If default field wrapper * elements are to be provided. * - hide_empty: Whether the field is to be hidden if empty. * - element_default_classes: If the default classes are to be added. * - separator: A string to be placed between inline fields to keep them * visually distinct. * - row: An array containing information about the current row. */ function template_preprocess_block_generation_views_view_fields(&$variables) { template_preprocess_views_view_fields($variables); $wrappers = [ 'field_wrapper_external', 'field_wrapper_main', 'field_wrapper_internal', 'field_wrapper_inner', ]; foreach ($wrappers as $wrapper) { _block_generation_process_wrapper($wrapper, $variables["row"]->_entity, $variables); _block_generation_process_effects($wrapper, $variables["row"]->_entity, $variables); } } /** * Helper function for skin processing. * * @param $skin_wrapper * @param \Drupal\Core\Entity\ContentEntityInterface $entity * @param array $variables * @param bool $inner */ function _block_generation_process_skin($skin_wrapper, ContentEntityInterface $entity, array &$variables, $inner = FALSE) { if ($entity->hasField($skin_wrapper) && $entity->get($skin_wrapper) && $entity->get($skin_wrapper)->value) { $variables['settings'][$skin_wrapper]['attributes'] = new Attribute(); $variables['settings'][$skin_wrapper]['tag'] = 'div'; $variables['settings'][$skin_wrapper]['value'] = $entity->get($skin_wrapper)->value; $skin_classes = []; if ($inner) { $skin_classes[] = 'page-skin--inner'; } $skin_classes[] = 'page-skin--' . $entity->get($skin_wrapper)->value; $variables['settings'][$skin_wrapper]['attributes']->addClass($skin_classes); if (isset($variables['view']) && $variables['view'] instanceof ViewExecutable && isset($variables['fields'][$skin_wrapper])) { unset($variables['fields'][$skin_wrapper]); } } } /** * Helper function for wrapper processing. * * @param $wrapper * @param \Drupal\Core\Entity\ContentEntityInterface $entity * @param array $variables */ function _block_generation_process_wrapper($wrapper, ContentEntityInterface $entity, array &$variables) { // Define if the wrapper should be processed. if ($entity->hasField($wrapper) && $entity->getFieldDefinition($wrapper)->getType() == 'boolean') { if ($entity->get($wrapper) && $entity->get($wrapper)->value) { $processing = TRUE; } else { $processing = FALSE; } } elseif ( ($entity->hasField($wrapper) && $entity->get($wrapper) && $entity->get($wrapper)->value) || ($entity->hasField($wrapper . '_tag') && $entity->get($wrapper . '_tag') && $entity->get($wrapper . '_tag')->value) || ($entity->hasField($wrapper . '_attr') && $entity->get($wrapper . '_attr') && $entity->get($wrapper . '_attr')->value) || ($entity->hasField($wrapper . '_effects') && $entity->get($wrapper . '_effects') && $entity->get($wrapper . '_effects')->value) ) { $processing = TRUE; } else { $processing = FALSE; } // Process wrapper. if ($processing) { // The tag of the wrapper. _block_generation_process_wrapper_tag($wrapper, $entity, $variables); // The attributes of the wrapper. _block_generation_process_attributes($wrapper, $entity, $variables); // The effects of the wrapper. _block_generation_process_effects($wrapper, $entity, $variables); } } /** * Helper function for wrapper's tag processing. * * @param $wrapper * @param \Drupal\Core\Entity\ContentEntityInterface $entity * @param array $variables */ function _block_generation_process_wrapper_tag($wrapper, ContentEntityInterface $entity, array &$variables) { if ($entity->hasField($wrapper . '_tag')) { $variables['settings'][$wrapper]['tag'] = ($entity->get($wrapper . '_tag') && $entity->get($wrapper . '_tag')->value) ? $entity->get($wrapper . '_tag')->value : 'div'; } else { $variables['settings'][$wrapper]['tag'] = 'div'; } } /** * Helper function for attributes processing of the block. * * @param $element * @param \Drupal\Core\Entity\ContentEntityInterface $entity * @param array $variables * @param string $destination */ function _block_generation_process_attributes($element, ContentEntityInterface $entity, array &$variables) { // Attributes for wrapper. if (!isset($variables['settings'][$element]['attributes']) || !$variables['settings'][$element]['attributes'] instanceof Attribute) { $variables['settings'][$element]['attributes'] = new Attribute(); } if ($entity->hasField($element . '_attr')) { if ($entity->get($element . '_attr') && $entity->get($element . '_attr')->getValue()) { foreach ($entity->get($element . '_attr')->getValue() as $attribute_delta => $attribute) { $attribute_id = $attribute['first']; $attribute_value = $attribute['second']; if ($attribute_id === 'class') { $classes = explode(' ', $attribute_value); foreach ($classes as $class) { if (!$variables['settings'][$element]['attributes']->hasClass($class)) { $variables['settings'][$element]['attributes']->addClass($class); } } } elseif ($attribute_id === 'style') { $styles = explode(';', $attribute_value); foreach ($styles as $style_delta => &$style) { $style = trim($style); if ($style === '') { unset($styles[$style_delta]); } } $variables['settings'][$element]['attributes']->setAttribute('style', implode('; ', $styles) . ';'); } else { $variables['settings'][$element]['attributes']->setAttribute($attribute_id, $attribute_value); } } } } } /** * Helper function for attributes processing of the field. * * @param $element * @param array $variables */ function _block_generation_process_field_attributes($element, array &$variables) { if (isset($variables['element']['#object']) && $entity = $variables['element']['#object']) { if ($entity->hasField($element . '_attr')) { if ($entity->get($element . '_attr') && $entity->get($element . '_attr')->getValue()) { foreach ($entity->get($element . '_attr')->getValue() as $attribute_delta => $attribute) { $attribute_id = $attribute['first']; $attribute_value = $attribute['second']; if ($attribute_id === 'class') { $classes = explode(' ', $attribute_value); foreach ($classes as $class) { foreach ($variables['items'] as &$content_item) { if (!isset($content_item['attributes']) || !$content_item['attributes'] instanceof Attribute) { $content_item['attributes'] = new Attribute(); } if (!$content_item['attributes']->hasClass($class)) { $content_item['attributes']->addClass($class); } } } } elseif ($attribute_id === 'style') { $styles = explode(';', $attribute_value); foreach ($styles as $style_delta => &$style) { $style = trim($style); if ($style === '') { unset($styles[$style_delta]); } } foreach ($variables['items'] as &$content_item) { if (!isset($content_item['attributes']) || !$content_item['attributes'] instanceof Attribute) { $content_item['attributes'] = new Attribute(); } $content_item['attributes']->setAttribute('style', implode('; ', $styles) . ';'); } } else { foreach ($variables['items'] as &$content_item) { if (!isset($content_item['attributes']) || !$content_item['attributes'] instanceof Attribute) { $content_item['attributes'] = new Attribute(); } $content_item['attributes']->setAttribute($attribute_id, $attribute_value); } } } } } } } /** * Helper function for effects processing. * * @param $wrapper * @param \Drupal\Core\Entity\ContentEntityInterface $entity * @param array $variables */ function _block_generation_process_effects($wrapper, ContentEntityInterface $entity, array &$variables) { if ($entity->hasField($wrapper . '_effects') && $entity->get($wrapper . '_effects')->getValue()) { if (!isset($variables['settings'][$wrapper]['attributes'])) { $variables['settings'][$wrapper]['attributes'] = new Attribute(); } if (!isset($variables['settings'][$wrapper]['tag'])) { $variables['settings'][$wrapper]['tag'] = 'div'; } $effects = $entity->get($wrapper . '_effects')->getValue(); foreach ($effects as $effect) { $plugin = \Drupal::service('plugin.manager.block_generation_effect')->createInstance($effect['plugin_id'], $effect['plugin_configuration']); $plugin->applyEffect($wrapper, $variables); } } } /** * Helper function for content processing. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * @param array $variables * @param array $content_composition */ function _block_generation_process_content(ContentEntityInterface $entity, array &$variables, array $content_composition) { foreach ($content_composition as $content_item) { if (isset($variables['elements'][$content_item])) { $variables['content'][$content_item] = $variables['elements'][$content_item]; } } } /** * Helper function for order processing. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * @param array $variables * @param array $content_composition */ function _block_generation_process_order(ContentEntityInterface $entity, array &$variables, array $content_composition) { if ($entity->hasField('order')) { // Prepare ordering. $ordering = $entity->get('order')->getValue(); $order = []; foreach ($ordering as $order_key => $order_value) { $order[] = $order_value['value']; } foreach (Element::children($variables['elements']) as $element_id) { if (!in_array($element_id, $order) && $element_id[0] != '_') { $order[] = $element_id; } } // Compose ordering in settings. $variables['settings']['order'] = $order; } } /** * Get context of the entity. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * * @return array */ function _block_generation_get_contexts(ContentEntityInterface $entity) { $contexts = $entity->__get('contexts'); $data = []; if ($contexts) { foreach ($contexts as $context_id => $context) { $data[$context_id] = $context->getContextValue(); } } return $data; }