gutenberg.module in Gutenberg 8
Same filename and directory in other branches
Provides integration with the Gutenberg editor.
File
gutenberg.moduleView source
<?php
/**
* @file
* Provides integration with the Gutenberg editor.
*/
use Drupal\Core\Entity\EntityFormInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\node\NodeInterface;
use Drupal\views\ViewExecutable;
use Symfony\Component\Yaml\Yaml;
use Drupal\Core\Discovery\YamlDiscovery;
use Drupal\image\Entity\ImageStyle;
use Drupal\gutenberg\Controller\UtilsController;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\CloseModalDialogCommand;
/**
* Implements hook_form_alter().
*/
function gutenberg_form_alter(&$form, FormStateInterface $form_state, $form_id) {
// TODO: There's got to be a better way to alter all media forms regardless
// the type.
if ($form_id == 'media_video_edit_form' || $form_id == 'media_image_edit_form' || $form_id == 'media_audio_edit_form' || $form_id == 'media_file_edit_form' || $form_id == 'media_remote_video_edit_form') {
_gutenberg_media_form_alter($form, $form_state, $form_id);
}
if ($form_id == 'node_type_edit_form' || $form_id == 'node_type_add_form') {
$config = \Drupal::service('config.factory')
->getEditable('gutenberg.settings');
$form['gutenberg'] = [
'#type' => 'details',
'#title' => t('Gutenberg experience'),
'#description' => '',
'#group' => 'additional_settings',
'#weight' => 999,
'enable_gutenberg_experience' => [
'#type' => 'checkbox',
'#title' => t('Enable Gutenberg experience'),
'#description' => t('Turn the node edit form into a full Gutenberg UI experience. At least one field of long text type is necessary.'),
'#default_value' => $config
->get($form['type']['#default_value'] . '_enable_full'),
],
];
$form['gutenberg']['categories'] = [
'#type' => 'hidden',
'#default_value' => [],
];
$example_code = "<br/><code>[<br/> [\"core/heading\", {}],<br/> [\"core/paragraph\", {\"placeholder\": \"Insert text\"}]<br/>]</code>";
$form['gutenberg']['gutenberg_template'] = [
'#type' => 'textarea',
'#description' => t('JSON structure of blocks. Example:') . $example_code,
'#title' => t('Template'),
'#default_value' => $config
->get($form['type']['#default_value'] . '_template'),
'#states' => [
'visible' => [
'input[name="enable_gutenberg_experience"]' => [
'checked' => TRUE,
],
],
],
];
$form['gutenberg']['gutenberg_template_lock'] = [
'#type' => 'select',
'#title' => t('Template lock'),
'#description' => t('<code>All</code> will fully lock the page template, not able to delete, create or move blocks just edit the content.<br/><code>Insert</code> will allow moving blocks.'),
'#default_value' => $config
->get($form['type']['#default_value'] . '_template_lock'),
'#options' => [
'none' => t('None'),
'insert' => t('Insert'),
'all' => t('All'),
],
'#states' => [
'visible' => [
'input[name="enable_gutenberg_experience"]' => [
'checked' => TRUE,
],
],
],
];
$form['gutenberg']['allowed_blocks_details'] = [
'#type' => 'details',
'#title' => t('Allowed Gutenberg blocks'),
'#states' => [
'visible' => [
'input[name="enable_gutenberg_experience"]' => [
'checked' => TRUE,
],
],
],
];
$settings = _gutenberg_get_allowed_blocks();
$blocks_settings = UtilsController::getBlocksSettings();
foreach ($settings['categories'] as $category) {
$category['reference'] = str_replace('/', '-', $category['reference']);
$form['gutenberg']['categories']['#default_value'][] = $category['reference'];
$form['gutenberg']['allowed_blocks_details'][$category['reference']] = [
'#type' => 'fieldset',
'#title' => $category['name'],
];
$options = [
$category['reference'] . '/all' => t('All'),
];
foreach ($category['blocks'] as $block) {
if (!in_array($block['id'], $blocks_settings['blacklist'])) {
$options[$block['id']] = $block['name'];
}
}
$default_values = array_combine(array_map(function ($block) {
return $block['id'];
}, $category['blocks']), array_map(function ($block) {
return $block['default'] ? $block['id'] : 0;
}, $category['blocks']));
$config_values = $config
->get($form['type']['#default_value'] . '_allowed_blocks');
$form['gutenberg']['allowed_blocks_details'][$category['reference']]['allowed_blocks_' . $category['reference']] = [
'#type' => 'checkboxes',
'#options' => $options,
'#default_value' => array_merge($default_values, $config_values ? $config_values : []),
];
}
$form['gutenberg']['allowed_drupal_blocks_details'] = [
'#type' => 'details',
'#title' => t('Allowed Drupal blocks'),
'#states' => [
'visible' => [
'input[name="enable_gutenberg_experience"]' => [
'checked' => TRUE,
],
],
],
];
$blockManager = \Drupal::service('plugin.manager.block');
$contextRepository = \Drupal::service('context.repository');
// Get blocks definition
// $definitions = $blockManager->getDefinitionsForContexts($contextRepository->getAvailableContexts());
$definitions = $blockManager
->getFilteredDefinitions('block_ui', $contextRepository
->getAvailableContexts());
$groups = $blockManager
->getGroupedDefinitions($definitions);
$form['gutenberg']['categories_drupal'] = [
'#type' => 'hidden',
'#default_value' => [],
];
$default_values = array_fill_keys(array_map(function ($key) {
return str_replace('drupalblock/', '', $key);
}, $settings['default_drupal_blocks']), TRUE);
foreach ($groups as $key => $blocks) {
$group_reference = preg_replace('@[^a-z0-9-]+@', '_', strtolower($key));
$options = [];
$input_default_values = [];
foreach ($blocks as $key_block => $block) {
if (!in_array('drupalblock/' . $key_block, $blocks_settings['blacklist'])) {
$options[$key_block] = $block['admin_label'];
if (isset($default_values[$key_block]) && $default_values[$key_block]) {
$input_default_values[$key_block] = $key_block;
}
}
}
if (count($options) > 0) {
$form['gutenberg']['categories_drupal']['#default_value'][] = $group_reference;
$form['gutenberg']['allowed_drupal_blocks_details'][$group_reference] = [
'#type' => 'fieldset',
'#title' => $key,
];
$config_values = $config
->get($form['type']['#default_value'] . '_allowed_drupal_blocks');
$form['gutenberg']['allowed_drupal_blocks_details'][$group_reference]['allowed_drupal_blocks_' . $group_reference] = [
'#type' => 'checkboxes',
'#options' => $options,
'#default_value' => array_merge($input_default_values, $config_values ? $config_values : []),
];
}
}
$form['#attached']['library'][] = 'gutenberg/admin';
$form['actions']['submit']['#submit'][] = '_gutenberg_node_type_form_submit';
if (isset($form['actions']['save_continue']['#submit'])) {
$form['actions']['save_continue']['#submit'][] = '_gutenberg_node_type_form_submit';
}
}
}
/**
* Alters the node form submit.
*
* @param array $form
* The form definition array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*/
function _gutenberg_node_type_form_submit(array $form, FormStateInterface $form_state) {
$gutenberg_enabled = $form_state
->getValue('enable_gutenberg_experience');
$template = $form_state
->getValue('gutenberg_template');
$template_lock = $form_state
->getValue('gutenberg_template_lock');
$config = \Drupal::service('config.factory')
->getEditable('gutenberg.settings');
$config
->set($form_state
->getValue('type') . '_enable_full', $gutenberg_enabled)
->save();
if (!$gutenberg_enabled) {
$config
->clear($form_state
->getValue('type') . '_allowed_blocks')
->save();
$config
->clear($form_state
->getValue('type') . '_allowed_drupal_blocks')
->save();
$config
->clear($form_state
->getValue('type') . '_gutenberg_template')
->save();
$config
->clear($form_state
->getValue('type') . '_gutenberg_template_lock')
->save();
return;
}
// Save template settings.
$config
->set($form_state
->getValue('type') . '_template', $template)
->save();
$config
->set($form_state
->getValue('type') . '_template_lock', $template_lock)
->save();
// Save Gutenberg core blocks settings.
$categories = explode(' ', $form_state
->getValue('categories'));
$values = [];
foreach ($categories as $category) {
$values = array_merge($values, $form_state
->getValue('allowed_blocks_' . $category));
}
$config
->set($form_state
->getValue('type') . '_allowed_blocks', $values)
->save();
// Save Drupal blocks settings.
$categories = explode(' ', $form_state
->getValue('categories_drupal'));
$values = [];
foreach ($categories as $category) {
$values = array_merge($values, $form_state
->getValue('allowed_drupal_blocks_' . $category));
}
$config
->set($form_state
->getValue('type') . '_allowed_drupal_blocks', $values)
->save();
}
/**
* Implements hook_form_node_form_alter().
*/
function gutenberg_form_node_form_alter(&$form, FormStateInterface $form_state) {
$config = \Drupal::service('config.factory')
->getEditable('gutenberg.settings');
$node = $form_state
->getFormObject()
->getEntity();
$node_type = $node->type
->getString();
$gutenberg_enabled = $config
->get($node_type . '_enable_full');
// Leave early if Gutenberg not enabled.
if (!$gutenberg_enabled) {
return;
}
// Set template options to global var.
$form['#attached']['drupalSettings']['gutenberg']['template'] = json_decode($config
->get($node_type . '_template'));
$form['#attached']['drupalSettings']['gutenberg']['template-lock'] = $config
->get($node_type . '_template_lock');
/*
* DEPRECATED >>>
*/
$mapped_fields = _gutenberg_get_mapped_fields(json_decode($config
->get($node_type . '_template')));
foreach ($mapped_fields as $item) {
$form[$item['field']]['#access'] = FALSE;
}
/*
* <<< DEPRECATED
*/
$mapped_fields = _gutenberg_get_mapping_fields(json_decode($config
->get($node_type . '_template')));
foreach ($mapped_fields as $item) {
$form[$item['field']]['#access'] = FALSE;
}
$module_settings = _gutenberg_get_all_modules_settings();
foreach ($module_settings as $settings) {
if (isset($settings['libraries-edit'])) {
foreach ($settings['libraries-edit'] as $library) {
$form['#attached']['library'][] = $library;
}
}
}
$theme_settings = _gutenberg_get_default_theme_settings();
if (isset($theme_settings['libraries-edit'])) {
foreach ($theme_settings['libraries-edit'] as $value) {
$form['#attached']['library'][] = $value;
}
}
if (isset($theme_settings['theme-support'])) {
$form['#attached']['drupalSettings']['gutenberg']['theme-support'] = $theme_settings['theme-support'];
}
// Set available image sizes for editor.
$styles = ImageStyle::loadMultiple();
$sizes = [
[
'slug' => 'full',
'name' => t('Original'),
],
];
foreach ($styles as $style) {
$sizes[] = [
'slug' => $style
->getName(),
'name' => $style
->label(),
];
}
$form['#attached']['drupalSettings']['gutenberg']['image-sizes'] = $sizes;
$text_fields = [];
// Iterate over all node fields and apply gutenberg text format
// on first text field found.
$field_names = array_keys($node
->getFields());
foreach ($field_names as $value) {
$field_properties = array_keys($node
->getFieldDefinition($value)
->getFieldStorageDefinition()
->getPropertyDefinitions());
if (in_array('format', $field_properties)) {
$text_fields[] = $value;
}
}
// No point going forward when no text fields on the form.
if (count($text_fields) === 0) {
return;
}
$form['#attributes']['class'][] = 'metabox-base-form';
$form[$text_fields[0]]['widget'][0]['#format'] = 'gutenberg';
$form[$text_fields[0]]['#attributes']['class'][] = 'field--gutenberg';
// Hide the field label.
$form[$text_fields[0]]['widget'][0]['#title_display'] = 'hidden';
// Disable the summary field.
if (isset($form[$text_fields[0]]['widget'][0]['summary'])) {
$form[$text_fields[0]]['widget'][0]['summary']['#access'] = FALSE;
}
foreach ($text_fields as $fieldname) {
// For the rest of the text fields call after build to remove
// Gutenberg from text format options.
if ($gutenberg_enabled) {
if ($text_fields[0] !== $fieldname) {
$form[$fieldname]['widget']['#after_build'][] = 'gutenberg_form_node_form_after_build';
}
}
else {
$form[$fieldname]['widget']['#after_build'][] = 'gutenberg_form_node_form_after_build';
}
}
// Let's move the remaining fields to a "special"
// form group that can be used later by JS to move to
// Gutenberg's sidebar.
$form['metabox_fields'] = [
'#type' => 'details',
'#access' => TRUE,
'#title' => t('More settings'),
'#weight' => 99,
// Group fallback in case JS fails to move to metaboxes.
'#group' => 'advanced',
];
// Some other module might have already init this container.
if (!isset($form['additional_fields'])) {
$form['additional_fields'] = [
'#type' => 'container',
'#title' => 'Additional',
'#weight' => -100,
];
}
// Move title to Published/meta pane.
$form['title']['#group'] = 'meta';
// Move status to Published/meta pane.
$form['status']['#group'] = 'meta';
// Move langcode to Published/meta pane.
if (isset($form['langcode'])) {
$form['langcode']['#group'] = 'meta';
}
$excluded_fields = [
'status',
'title',
'uid',
'created',
'changed',
'promote',
'sticky',
'path',
'comment',
'revision_log',
'langcode',
];
/*
* Rationale behind this "messy" algo:
* If there's any details fieldset on the form, add it to a special array
* and then, on the form after build, add its #id to a JS array.
* For any other type of fields, group them on the metabox_fields fieldset.
* This fieldset will also move to metaboxes area.
*/
$metabox_has_fields = FALSE;
$fields_with_details = [];
foreach ($field_names as $value) {
if (array_key_exists($value, $form) && $value !== $text_fields[0] && !in_array($value, $excluded_fields)) {
if (isset($form[$value]['widget']) && isset($form[$value]['widget'][0]) && isset($form[$value]['widget'][0]['#type']) && $form[$value]['widget'][0]['#type'] === 'details') {
$fields_with_details[] = $value;
}
else {
$form[$value]['#group'] = 'metabox_fields';
$metabox_has_fields = TRUE;
}
}
}
$form['#after_build'][] = 'gutenberg_form_node_form_details_after_build';
$form['#fields_with_details'] = $fields_with_details;
if (!$metabox_has_fields) {
unset($form['metabox_fields']);
}
// Is Bartik the default theme? Add some custom styles
// to look even better.
$default_theme = \Drupal::config('system.theme')
->get('default');
if ($default_theme === 'bartik') {
$form['#attached']['library'][] = 'gutenberg/bartik';
}
// Check if current theme is Seven (admin)
$theme = \Drupal::theme()
->getActiveTheme();
if ($theme
->getName() === 'seven') {
$form['#attached']['library'][] = 'gutenberg/seven';
}
$form['#attached']['drupalSettings']['gutenberg']['metaboxes'][] = 'edit-metabox-fields';
/** @var \Drupal\Core\Extension\ModuleHandlerInterface $module_handler */
$module_handler = \Drupal::service('module_handler');
$form['#attached']['drupalSettings']['gutenberg']['media-enabled'] = $module_handler
->moduleExists('media');
if ($form['#attached']['drupalSettings']['gutenberg']['media-library-enabled'] = $module_handler
->moduleExists('media_library')) {
$form['#attached']['library'][] = 'media_library/ui';
}
}
/**
* Handle messages.
*/
function gutenberg_handle_messages(&$form) {
$messages = \Drupal::messenger()
->deleteAll();
$form['#attached']['drupalSettings']['gutenberg']['messages'] = $messages;
}
/**
* Called by after build text fields on the form.
*/
function gutenberg_form_node_form_after_build(array $element, FormStateInterface $form_state) {
unset($element[0]['format']['format']['#options']['gutenberg']);
return $element;
}
/**
* Called by form after build.
*/
function gutenberg_form_node_form_details_after_build(array $element, FormStateInterface $form_state) {
// gutenberg_handle_messages($element);
foreach ($element['#fields_with_details'] as $value) {
$element['#attached']['drupalSettings']['gutenberg']['metaboxes'][] = $element[$value]['widget'][0]['#id'];
}
return $element;
}
/**
* Alter media form.
*/
function _gutenberg_media_form_alter(array &$form, FormStateInterface $form_state, string $form_id) {
$is_gutenberg = !is_null(\Drupal::request()->query
->get('gutenberg'));
if (!$is_gutenberg) {
return;
}
$form['actions']['delete']['#access'] = FALSE;
unset($form['actions']['delete']);
$form['#after_build'][] = 'gutenberg_form_media_edit_form_after_build';
$form['actions']['submit']['#submit'][] = 'gutenberg_form_media_edit_form_submit';
$form['actions']['submit']['#ajax'] = [
'callback' => 'gutenberg_form_media_edit_form_submit',
'event' => 'click',
];
$form['actions']['cancel'] = [
'#weight' => 99,
'#type' => 'button',
'#value' => t('Cancel'),
'#ajax' => [
'callback' => 'gutenberg_form_media_edit_form_cancel',
'event' => 'click',
],
];
$form['#submit'][] = 'gutenberg_form_media_edit_form_submit';
}
/**
* Alter media edit form submit.
*/
function gutenberg_form_media_edit_form_submit(array $form, FormStateInterface $form_state) {
$form_state
->disableRedirect();
$command = new CloseModalDialogCommand();
$response = new AjaxResponse();
$response
->addCommand($command);
return $response;
}
/**
* Alter media edit form cancel.
*/
function gutenberg_form_media_edit_form_cancel(array $form, FormStateInterface $form_state) {
$form_state
->disableRedirect();
$command = new CloseModalDialogCommand();
$response = new AjaxResponse();
$response
->addCommand($command);
return $response;
}
/**
* Alter media edit form.
*/
function gutenberg_form_media_edit_form_after_build(array $element, FormStateInterface $form_state) {
return $element;
}
/**
* Implements template_preprocess_node().
*/
function gutenberg_preprocess_node(&$variables) {
$config = \Drupal::service('config.factory')
->getEditable('gutenberg.settings');
$node = $variables['elements']['#node'];
$node_type = $node->type
->getString();
$gutenberg_enabled = $config
->get($node_type . '_enable_full');
if (!$gutenberg_enabled) {
return;
}
$variables['#attached']['library'][] = 'gutenberg/blocks-view';
$module_settings = _gutenberg_get_all_modules_settings();
foreach ($module_settings as $settings) {
if (isset($settings['libraries-view'])) {
foreach ($settings['libraries-view'] as $library) {
$variables['#attached']['library'][] = $library;
}
}
}
$default_theme = \Drupal::config('system.theme')
->get('default');
if ($default_theme === 'bartik') {
$variables['#attached']['library'][] = 'gutenberg/bartik';
}
}
/**
* Implements hook_entity_presave().
*/
function gutenberg_entity_presave($entity) {
if (!$entity
->getEntityTypeId() === 'node') {
return;
}
$config = \Drupal::service('config.factory')
->getEditable('gutenberg.settings');
$node_type = $entity
->bundle();
$gutenberg_enabled = $config
->get($node_type . '_enable_full');
if (!$gutenberg_enabled) {
return;
}
/*
* DEPRECATED >>>
* To be removed on 1.12 or 1.13 (2.0?)
*/
// Get first text field, that's the one that'll have
// gutenberg text format
// TODO: We already have this on node_edit_form_alter,
// let's move it to a function.
$value_fields = [
'title',
];
$text_fields = [];
// Used for media.
$file_fields = [];
// Iterate over all node fields and apply gutenberg text format
// on first text field found.
$field_names = array_keys($entity
->getFields());
foreach ($field_names as $value) {
$definition = $entity
->getFieldDefinition($value);
$field_properties = $definition
->getFieldStorageDefinition()
->getPropertyDefinitions();
if (in_array('value', array_keys($field_properties))) {
$value_fields[] = $value;
}
// Check for "rich-text" fields.
if (in_array('format', array_keys($field_properties))) {
$text_fields[] = $value;
}
if (isset($field_properties['entity'])) {
$settings = $definition
->getSettings();
// Check for images/files.
if ($settings['target_type'] === 'file') {
$file_fields[] = $value;
}
if ($settings['target_type'] === 'media') {
$file_fields[] = $value;
}
}
}
if (count($text_fields) === 0) {
return;
}
$str = $entity
->get($text_fields[0])
->getString();
// Iterate over all mapped fields and set its values.
$mapped_fields = _gutenberg_get_mapped_fields(json_decode($config
->get($node_type . '_template')));
foreach ($mapped_fields as $item) {
$re = '/((<!-- .*?\\{.*"mappingField":"' . $item['field'] . '".*} -->)([\\s\\S]*?)(<!-- \\/[\\s\\S]*?-->|\\/-->)|(<!-- .*?\\{.*"mappingField":"' . $item['field'] . '".*} \\/-->))/m';
// matches:
// 0: all block
// 1: opening block comment tag (get attributes from here)
// 2: content
// 3: closing block comment tag.
preg_match_all($re, $str, $matches, PREG_SET_ORDER, 0);
// Get block attributes.
$re = '/((<!-- .*?({[\\s\\S]*}).*-->)|(<!-- .*?({[\\s\\S]*}).*\\/-->))/m';
$tag = $matches[0][2] !== '' ? $matches[0][2] : $matches[0][1];
preg_match_all($re, $tag, $attrs, PREG_SET_ORDER, 0);
$attributes = json_decode($attrs[0][3], TRUE);
// Remove line breaks.
$re = '/\\r?\\n|\\r/';
$value = $matches[0][3];
$value = preg_replace($re, '', $value);
if (in_array($item['field'], $value_fields)) {
$entity
->set($item['field'], strip_tags($value));
}
// Set text fields.
if (in_array($item['field'], $text_fields)) {
$entity
->set($item['field'], $matches[0][3]);
}
if (in_array($item['field'], $file_fields)) {
$value = is_array($attributes[$item['attribute']]) ? $attributes[$item['attribute']][0] : $attributes[$item['attribute']];
$entity
->set($item['field'], [
'target_id' => $value,
]);
}
}
/*
* <<< DEPRECATED
*/
$mapped_fields = _gutenberg_get_mapping_fields(json_decode($config
->get($node_type . '_template')));
$re = '/((<!-- .*?\\{.*"mappingFields":.*} -->)([\\s\\S]*?)(<!-- \\/[\\s\\S]*?-->|\\/-->)|(<!-- .*?\\{.*"mappingFields":.*} \\/-->))/m';
preg_match_all($re, $str, $blocks, PREG_SET_ORDER, 0);
// Let's build the field's array of values.
$fields = [];
// For each block match.
foreach ($blocks as $block) {
// Get block attributes.
$re = '/((^<!-- .*?({[\\s\\S]*}).*-->$))/m';
$tag = $block[2] !== '' ? $block[2] : $block[1];
preg_match_all($re, $tag, $attrs, PREG_SET_ORDER, 0);
$attributes = json_decode($attrs[0][3], TRUE);
// Remove line breaks from block's "inner" content.
// $content is the "inner" content.
$re = '/\\r?\\n|\\r/';
$content = $block[3];
$content = preg_replace($re, '', $content);
foreach ($attributes['mappingFields'] as $mField) {
if (!isset($fields[$mField['field']])) {
$fields[$mField['field']] = [];
}
$value = isset($mField['attribute']) ? $attributes[$mField['attribute']] : $content;
// Value doesn't support array yet.
if (is_array($value)) {
$value = $value[0];
}
$value = strip_tags($value);
if (isset($mField['property'])) {
$fields[$mField['field']][$mField['property']] = $value;
}
else {
$fields[$mField['field']] = $value;
}
}
}
foreach ($fields as $key => $value) {
try {
$entity
->set($key, $value);
} catch (\Exception $e) {
\Drupal::logger('gutenberg')
->error(t('Mapping field: @message', [
'@message' => $e
->getMessage(),
]));
}
}
}
/**
* Gets all mapped fields on the template (recursive).
*
* @deprecated in gutenberg:8.x-1.11
* @see https://www.drupal.org/node/3109876
*/
function _gutenberg_get_mapped_fields($template, &$result = []) {
if (empty($template)) {
return [];
}
foreach ($template as $key => $block) {
if (isset($block[1]) && isset($block[1]->mappingField)) {
$item = [];
$item['field'] = $block[1]->mappingField;
if (isset($block[1]->mappingAttribute)) {
$item['attribute'] = $block[1]->mappingAttribute;
}
$result[] = $item;
}
if (isset($block[2])) {
_gutenberg_get_mapped_fields($block[2], $result);
}
}
return $result;
}
/**
* Gets all mapping fields on the template (recursive).
*/
function _gutenberg_get_mapping_fields($template, &$result = []) {
if (empty($template)) {
return [];
}
foreach ($template as $key => $block) {
if (isset($block[1]) && isset($block[1]->mappingFields)) {
foreach ($block[1]->mappingFields as $field) {
$item = [];
$item['field'] = $field->field;
if (isset($field->property)) {
$item['property'] = $field->property;
}
if (isset($field->attribute)) {
$item['attribute'] = $field->attribute;
}
$result[] = $item;
}
}
if (isset($block[2])) {
_gutenberg_get_mapping_fields($block[2], $result);
}
}
return $result;
}
/**
* Gets default theme settings.
*/
function _gutenberg_get_default_theme_settings() {
$settings =& drupal_static(__FUNCTION__);
if (!isset($settings)) {
$theme_name = \Drupal::config('system.theme')
->get('default');
$theme_path = drupal_get_path('theme', $theme_name);
$file_path = DRUPAL_ROOT . '/' . $theme_path . '/' . $theme_name . '.gutenberg.yml';
if (file_exists($file_path)) {
$file_contents = file_get_contents($file_path);
$settings = Yaml::parse($file_contents);
}
else {
$settings = [];
}
}
return $settings;
}
/**
* Gets settings from all modules.
*/
function _gutenberg_get_all_modules_settings() {
$settings =& drupal_static(__FUNCTION__);
if (!isset($settings)) {
$directories = \Drupal::service('module_handler')
->getModuleDirectories();
$discovery = new YamlDiscovery('gutenberg', $directories);
$settings = $discovery
->findAll();
}
return $settings;
}
/**
* Gets allowed blocks.
*/
function _gutenberg_get_allowed_blocks() {
$settings =& drupal_static(__FUNCTION__);
if (!isset($settings)) {
$module_handler = \Drupal::service('module_handler');
$path = $module_handler
->getModule('gutenberg')
->getPath();
$file_path = DRUPAL_ROOT . '/' . $path . '/' . 'gutenberg.blocks.yml';
if (file_exists($file_path)) {
$file_contents = file_get_contents($file_path);
$settings = Yaml::parse($file_contents);
}
}
return $settings;
}
/**
* Implements hook_theme_suggestions_HOOK_alter().
*/
function gutenberg_theme_suggestions_node_edit_form_alter(array &$suggestions, array $variables) {
$config = \Drupal::service('config.factory')
->getEditable('gutenberg.settings');
$node = \Drupal::routeMatch()
->getParameter('node');
if (!$node) {
$route_match = \Drupal::service('current_route_match');
if (!$route_match
->getParameter('node_type')) {
return;
}
$node_type = $route_match
->getParameter('node_type')
->get('type');
}
else {
$node_type = $node->type
->getString();
}
$gutenberg_enabled = $config
->get($node_type . '_enable_full');
if (!$gutenberg_enabled) {
return;
}
$suggestions = [
'node_edit_form__gutenberg',
];
}
/**
* Implements hook_theme_suggestions_HOOK_alter().
*/
function gutenberg_theme_suggestions_page_alter(array &$suggestions, array $variables) {
if (!in_array('page__node__edit', $suggestions) && !in_array('page__node__add', $suggestions)) {
return;
}
$config = \Drupal::service('config.factory')
->getEditable('gutenberg.settings');
$node = \Drupal::routeMatch()
->getParameter('node');
if (!$node) {
$route_match = \Drupal::service('current_route_match');
if (!$route_match
->getParameter('node_type')) {
return;
}
$node_type = $route_match
->getParameter('node_type')
->get('type');
}
else {
$node_type = $node->type
->getString();
}
$gutenberg_enabled = $config
->get($node_type . '_enable_full');
if ($gutenberg_enabled) {
if (in_array('page__node__edit', $suggestions)) {
$suggestions = [
'page__node__edit__gutenberg',
];
}
if (in_array('page__node__add', $suggestions)) {
$suggestions = [
'page__node__add__gutenberg',
];
}
}
}
/**
* Implements hook_theme().
*/
function gutenberg_theme() {
return [
'page__node__edit__gutenberg' => [
'template' => 'page--node--edit--gutenberg',
],
'page__node__add__gutenberg' => [
'template' => 'page--node--add--gutenberg',
],
'node_edit_form__gutenberg' => [
'template' => 'node-edit-form--gutenberg',
],
'gutenberg_palette' => [
'variables' => [
'colors' => [],
],
],
];
}
/**
* Implements hook_element_info_alter().
*/
function gutenberg_element_info_alter(array &$info) {
if (!empty($info['text_format'])) {
// Add custom processor to eliminate the format if needed.
$info['text_format']['#process'][] = '_gutenberg_text_format_processor';
}
}
/**
* Process the text format element to eliminate the gutenberg format.
*
* On the fields that don't belong to content types with enabled gutenberg
* experience there is no need to have the gutenberg format.
*
* @param array $element
* Render Element.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Form state object.
* @param array $complete_form
* Complete form array.
*
* @return array
* Processed render element.
*/
function _gutenberg_text_format_processor(array $element, FormStateInterface $form_state, array &$complete_form) {
// Check first if the format is in the list. It might be disabled or the
// current user has not rights to access it.
if (!empty($element['format']) && isset($element['format']['format']['#options']['gutenberg'])) {
// By default let's assume that gutenberg format is not allowed.
$gutenberg_allowed = FALSE;
/** @var \Drupal\Core\Entity\ContentEntityForm $form */
$form = $form_state
->getFormObject();
// Check whether the form that contains the element is an EntityForm.
if ($form instanceof EntityFormInterface) {
// Get the entity from the form object for further processing.
$entity = $form
->getEntity();
// Check whether entity is of node type, because currently only them are
// supported.
if ($entity instanceof NodeInterface) {
// Get the node type to get the Gutenberg experience setting.
$node_type = $entity
->bundle();
/** @var \Drupal\Core\Config\Config $config */
$config = \Drupal::service('config.factory')
->getEditable('gutenberg.settings');
$gutenberg_enabled = $config
->get($node_type . '_enable_full');
if (!empty($gutenberg_enabled)) {
// Gutenberg experience is enabled for current content type and
// the current user is allowed to use the format.
$gutenberg_allowed = TRUE;
}
}
}
// If Gutenberg experience is not enabled for the current form or
// current user is not allowed to use the format, disable the choice of
// Gutenberg format for this element.
if (!$gutenberg_allowed) {
unset($element['format']['format']['#options']['gutenberg']);
}
}
return $element;
}
/**
* Implements hook_views_pre_render().
*/
function gutenberg_views_pre_render(ViewExecutable $view) {
if ($view
->id() == "reusable_blocks" && $view->current_display == 'page_1') {
// Attached Gutenberg's basic style to reusable blocks view.
$view->element['#attached']['library'][] = 'gutenberg/admin';
$view->element['#attached']['library'][] = 'gutenberg/blocks-view';
}
}
/**
* Implements hook_page_attachments().
*/
function gutenberg_page_attachments(array &$page) {
$settings = _gutenberg_get_default_theme_settings();
if (!isset($settings['theme-support']['colors'])) {
return;
}
$palette = [
'#theme' => 'gutenberg_palette',
'#colors' => $settings['theme-support']['colors'],
];
$renderPalette = \Drupal::service('renderer')
->renderRoot($palette);
// Sanitizing the output, like comments etc.
$renderPalette = strip_tags($renderPalette);
$renderPalette = trim($renderPalette);
$page['#attached']['html_head'][] = [
[
'#tag' => 'style',
'#value' => $renderPalette,
],
'gutenberg_palette',
];
}
/**
* Implements hook_library_info_alter().
*/
function gutenberg_library_info_alter(&$libraries, $extension) {
if ($extension === 'gutenberg') {
$moduleHandler = \Drupal::moduleHandler();
$js_files_edit = [];
$css_files_edit = [];
$css_files_view = [];
$moduleHandler
->alter('gutenberg_blocks', $js_files_edit, $css_files_edit, $css_files_view);
foreach ($js_files_edit as $file) {
$libraries['blocks-edit']['js'][$file] = [];
}
foreach ($css_files_edit as $file) {
$libraries['blocks-edit']['css']['base'][$file] = [];
}
foreach ($css_files_view as $file) {
$libraries['blocks-edit']['css']['base'][$file] = [];
$libraries['blocks-view']['css']['base'][$file] = [];
}
}
}
/**
* Implements hook_help().
*/
function gutenberg_help($route_name, RouteMatchInterface $route_match) {
if ($route_name === 'help.page.gutenberg') {
$readme_file = file_exists(__DIR__ . '/README.md') ? __DIR__ . '/README.md' : __DIR__ . '/README.txt';
if (!file_exists($readme_file)) {
return NULL;
}
$text = file_get_contents($readme_file);
if (!\Drupal::moduleHandler()
->moduleExists('markdown')) {
return '<pre>' . $text . '</pre>';
}
else {
// Use the Markdown filter to render the README.
$filter_manager = \Drupal::service('plugin.manager.filter');
$settings = \Drupal::configFactory()
->get('markdown.settings')
->getRawData();
$config = [
'settings' => $settings,
];
$filter = $filter_manager
->createInstance('markdown', $config);
return $filter
->process($text, 'en');
}
}
return NULL;
}