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_view_alter().
*
* @param array $build
* @param \Drupal\Core\Entity\EntityInterface $entity
* @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
*/
function block_generation_entity_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
if ($entity->getEntityTypeId() === 'block_content') {
$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;
}
