Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
mercury_editor.module 27.42 KiB
<?php

use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Component\Serialization\Yaml;
use Drupal\Core\Entity\EntityFormInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Url;
use Drupal\mercury_editor\EntityTypeInfo;
use Laminas\Diactoros\Response\RedirectResponse;

/**
 * @file
 * Mercury editor module.
 */

/**
 * Implements hook_library_info_alter().
 *
 * Attaches our mercury_editor dialog libraries where needed.
 */
function mercury_editor_library_info_alter(&$libraries, $extension) {
  if ($extension == 'core' && isset($libraries['drupal.dialog'])) {
    $libraries['drupal.dialog']['dependencies'][] = 'mercury_editor/dialog.drupal';
  }
  if ($extension == 'entity_browser' && isset($libraries['modal_selection'])) {
    $libraries['modal_selection']['dependencies'][] = 'mercury_editor/entity_browser.modal_selection';
  }
  if ($extension == 'layout_paragraphs' && isset($libraries['builder'])) {
    $libraries['builder']['dependencies'][] = 'mercury_editor/dialog.ajax';
  }
  if ($extension == 'image_radios' && isset($libraries['image_radios'])) {
    $libraries['image_radios']['dependencies'][] = 'mercury_editor/image_radios';
  }
  if ($extension == 'claro' && isset($libraries['media_library.theme'])) {
    $libraries['media_library.theme']['dependencies'][] = 'mercury_editor/claro.media_library.theme';
  }

  if ($extension == 'gin' && isset($libraries['media_library'])) {
    // When using the gin_toolbar module, we need to add the gin_base and
    // gin_accent libraries as dependencies to the media_library library as
    // they contain CSS custom property definitions that the media library
    // styles rely on. This should probably be fixed in Gin itself.
    if (\Drupal::moduleHandler()->moduleExists('gin_toolbar')) {
      $libraries['media_library']['dependencies'][] = 'gin/gin_base';
      $libraries['media_library']['dependencies'][] = 'gin/gin_accent';
    }
  }
}

/**
 * Implements hook_ajax_render_alter().
 *
 * Replaces Drupal's ajax Dialog commands with MercuryDialog commands.
 */
function mercury_editor_ajax_render_alter(array &$data): void {
  $current_route_name = \Drupal::routeMatch()->getRouteName();
  if (
    isset($current_route_name)
    && str_starts_with($current_route_name, 'mercury_editor')
  ) {
    foreach ($data as &$command) {
      if ($command['command'] == 'openDialog') {
        $command['command'] = 'openMercuryDialog';
      }
      if ($command['command'] == 'closeDialog') {
        $command['command'] = 'closeMercuryDialog';
      }
    }
    Drupal::service('mercury_editor.ajax_adapter')->ajaxRenderAlter($data);
  }
}

/**
 * Implements hook_preprocess().
 *
 * @see https://www.drupal.org/project/mercury_editor/issues/3379180
 * @see contextual_preprocess()
 */
function mercury_editor_preprocess(array &$variables, $hook, $info) {

  // Set a css class if the entity is being edited with mercury.
  if (isset($variables['elements']['#is_mercury_edit_mode'])) {
    $route_match = \Drupal::routeMatch();
    $route_name = $route_match->getRouteName();
    if ($route_name == 'mercury_editor.preview' || $route_name == 'mercury_editor.editor') {
      $variables['page'] = TRUE;
    }
    $variables['attributes']['class'][] = 'is-mercury-edit-mode';
  }

  // Determine the primary theme function argument.
  if (!empty($info['variables'])) {
    $keys = array_keys($info['variables']);
    $key = $keys[0];
  }
  elseif (!empty($info['render element'])) {
    $key = $info['render element'];
  }
  if (!empty($key) && isset($variables[$key])) {
    $element = $variables[$key];
  }

  if (isset($element) && is_array($element) && !empty($element['#contextual_links'])) {
    // Disable contextual links on the preview route.
    $variables['title_suffix']['#cache']['contexts'][] = 'route.name.is_mercury_editor_preview';
    if (\Drupal::routeMatch()->getRouteName() === 'mercury_editor.preview') {
      unset($variables['title_suffix']['contextual_links']);
    }
  }
}

/**
 * Implements hook_preprocess_layout_paragraphs_builder_controls().
 *
 * Set width on component delete modals.
 */
function mercury_editor_preprocess_layout_paragraphs_builder_controls(&$variables) {

  /** @var \Drupal\layout_paragraphs\LayoutParagraphsLayout $layout */
  $layout = $variables['layout_paragraphs_layout'];
  $is_mercury_editor_context = $layout->getSetting('mercury_editor_context') ?? FALSE;
  $component = $layout->getComponentByUuid($variables['uuid']);
  $paragraph = $component->getEntity();
  $paragraph_type = $paragraph->bundle();

  if ($is_mercury_editor_context) {
    $uuid = $variables['uuid'];
    $layout = $variables['layout_paragraphs_layout'];
    $component = $layout->getComponentByUuid($uuid);
    $type = $component->getEntity()->getParagraphType();

    $edit_dialog_options = json_decode($variables['controls']['edit_link']['#attributes']['data-dialog-options']);
    $edit_dialog_options->height = 'max-content';
    $edit_dialog_options->resizable = TRUE;
    $edit_dialog_options = Drupal::service('mercury_editor.dialog')->dialogSettings(['layout' => $layout, 'dialog' => $paragraph_type . '_form']);
    $variables['controls']['edit_link']['#attributes']['data-dialog-options'] = json_encode($edit_dialog_options);
    $variables['controls']['edit_link']['#attributes']['title'] = t('Edit :type', [':type' => $type->label()]);
    _mercury_editor_replace_ajax_class($variables['controls']['edit_link']['#attributes']['class']);
    _mercury_editor_replace_layout_paragraphs_routes($variables['controls']['edit_link']['#url']);

    $delete_dialog_options = Drupal::service('mercury_editor.dialog')->dialogSettings(['layout' => $layout, 'dialog' => 'delete_form']);
    $variables['controls']['delete_link']['#attributes']['data-dialog-options'] = json_encode($delete_dialog_options);
    $variables['controls']['delete_link']['#attributes']['title'] = t('Delete :type', [':type' => $type->label()]);
    _mercury_editor_replace_ajax_class($variables['controls']['delete_link']['#attributes']['class']);
    _mercury_editor_replace_layout_paragraphs_routes($variables['controls']['delete_link']['#url']);
  }
}

/**
 * Implements hook_preprocess_layout_paragraphs_insert_component_btn().
 *
 * Alters the "insert component" buttons to use window.postMessage instead
 * of directly invoking an Ajax action.
 */
function mercury_editor_preprocess_layout_paragraphs_insert_component_btn(&$variables) {
  /** @var \Drupal\Core\Url $variables['url'] */
  $parameters = $variables['url']->getRouteParameters();
  $layout_id = $parameters['layout_paragraphs_layout'];
  $layout = \Drupal::service('layout_paragraphs.tempstore_repository')->getWithStorageKey($layout_id);
  $is_mercury_editor_context = $layout->getSetting('mercury_editor_context') ?? FALSE;
  if (!$is_mercury_editor_context) {
    return;
  }
  _mercury_editor_replace_ajax_class($variables['attributes']['class']);
  _mercury_editor_replace_layout_paragraphs_routes($variables['url']);
  $old_options = json_decode($variables['attributes']['data-dialog-options'], TRUE);
  $dialog_options = ['target' => $old_options['target']] + Drupal::service('mercury_editor.dialog')->dialogSettings(['dialog' => 'component_menu']);
  $variables['attributes']['data-dialog-options'] = json_encode($dialog_options);
}

/**
 * Helper function to replace use-ajax classes with use-postmessage.
 *
 * @param array $classes
 *   An array of classes.
 */
function _mercury_editor_replace_ajax_class(&$classes) {
  if (($key = array_search('use-ajax', $classes)) !== FALSE) {
    unset($classes[$key]);
    $classes[] = 'use-postmessage';
  }
}

/**
 * Helper function to replace layout paragraph routes with mercury editor ones.
 *
 * @param \Drupal\Core\Url $url
 *   The url object.
 */
function _mercury_editor_replace_layout_paragraphs_routes(Url &$url) {
  $route_name = $url->getRouteName();
  $parameters = $url->getRouteParameters();
  $options = $url->getOptions();
  $route_name = str_replace('layout_paragraphs.', 'mercury_editor.', $route_name);
  $url = Url::fromRoute($route_name, $parameters, $options);
}

/**
 * Implements hook_theme_suggestions_page_alter().
 *
 * Adds page__mercury_editor as a suggestion for applicable content types.
 */
function mercury_editor_theme_suggestions_page_alter(array &$suggestions, array $variables) {
  $name = \Drupal::routeMatch()->getRouteName();
  if ($name === 'mercury_editor.preview') {
    $mercury_editor_entity = \Drupal::routeMatch()->getParameter('mercury_editor_entity');
    $suggestions[] = 'page__' . $mercury_editor_entity->bundle();
  }
  if ($name == 'mercury_editor.editor') {
    $suggestions[] = 'page__mercury_editor';
  }
}

/**
 * Implements hook_theme_suggestions_HOOK_alter().
 *
 * Use the mercury_editor theme suggestion for the component menu.
 */
function mercury_editor_theme_suggestions_layout_paragraphs_builder_component_menu_alter(array &$suggestions, array $variables) {
  $route_name = \Drupal::routeMatch()->getRouteName();
  if ($route_name === 'mercury_editor.builder.choose_component') {
    $suggestions[] = 'layout_paragraphs_builder_component_menu__mercury_editor';
  }
}

/**
 * Implements hook_theme_suggestions_HOOK_alter().
 *
 * Since node forms are set to use a specialized template in the node module,
 * we need to set the template suggestion here.
 */
function mercury_editor_theme_suggestions_node_edit_form_alter(array &$suggestions, array $variables) {
  if (strpos($variables['form']['#form_id'], '_mercury_editor_form') !== FALSE) {
    $suggestions[] = 'mercury_editor_entity_form';
  }
}

/**
 * Implements hook_entity_build_defaults_alter().
 *
 * Add cache context for mercury editor preview screen.
 */
function mercury_editor_entity_build_defaults_alter(array &$build, EntityInterface $entity, $view_mode) {
  $me_entity_types = \Drupal::config('mercury_editor.settings')->get('bundles');
  if (!empty($me_entity_types[$entity->getEntityTypeId()])) {
    $build['#cache']['contexts'][] = 'route.name.is_mercury_editor_preview';
  }
}

/**
 * Implements hook_build_alter().
 */
function mercury_editor_entity_display_build_alter(&$build, $context) {

  $route_name = \Drupal::routeMatch()->getRouteName();
  if ($route_name !== 'mercury_editor.preview' && $route_name !== 'mercury_editor.editor') {
    return;
  }
  /** @var \Drupal\Core\Entity\FieldableEntityInterface $entity */
  $entity = Drupal::service('mercury_editor.tempstore_repository')->get($context['entity']->uuid());
  if (!$entity) {
    return;
  }
  $build['#is_mercury_edit_mode'] = TRUE;

  // Turns on Layout Paragraphs builder for Mercury Editor LP fields.
  if (isset($entity->lp_storage_keys)) {
    foreach ($entity->lp_storage_keys as $field_name => $lp_key) {
      if (isset($build[$field_name])) {
        $layout = \Drupal::service('layout_paragraphs.tempstore_repository')->getWithStorageKey($lp_key);
        $build[$field_name] = [
          '#type' => 'layout_paragraphs_builder',
          '#layout_paragraphs_layout' => $layout,
        ];
      }
    }
  }
}

/**
 * Implements hook_theme().
 */
function mercury_editor_theme() {
  return [
    'page__mercury_editor' => [
      'base hook' => 'page',
    ],
    'mercury_editor_entity_form' => [
      'render element' => 'form',
    ],
    'layout_paragraphs_builder_component_menu__mercury_editor' => [
      'base hook' => 'layout_paragraphs_builder_component_menu',
    ],
  ];
}

/**
 * Implements hook_preprocess_html().
 *
 * Removes admin toolbar for Mercury Editor edit screens.
 */
function mercury_editor_preprocess_html(&$variables) {
  if (\Drupal::routeMatch()->getRouteObject()->getOption('_hide_admin_toolbar')) {
    // Check if toolbar is enabled.
    // Remove toolbar from the page.
    unset($variables['page_top']['toolbar']);

    // Remove toolbar classes from the body.
    $variables['attributes']['class'] = array_filter($variables['attributes']['class'] ?? [], function ($value) {
      return strpos($value, 'toolbar-') !== 0;
    });
    // Check if gin_toolbar is enabled.
    if (\Drupal::moduleHandler()->moduleExists('gin_toolbar')) {
      // Remove toolbar classes from the body.
      $variables['attributes']['class'] = array_filter($variables['attributes']['class'] ?? [], function ($value) {
        return strpos($value, 'gin--') !== 0;
      });
      // Remove toolbar attributes from the body.
      $variables['attributes'] = array_filter($variables['attributes'] ?? [], function ($value) {
        return strpos($value, 'data-gin') !== 0;
      }, ARRAY_FILTER_USE_KEY);
    }
    // Check if adminimal_admin_toolbar is enabled.
    if (\Drupal::moduleHandler()->moduleExists('adminimal_admin_toolbar')) {
      // Remove toolbar classes from the body.
      $variables['attributes']['class'] = array_filter($variables['attributes']['class'] ?? [], function ($value) {
        return strpos($value, 'adminimal-admin-toolbar') !== 0;
      });
    }
  }
}

/**
 * Implements hook_preprocess_page().
 *
 * Adds a select list to the page for choosing from a list of mobile presets.
 */
function mercury_editor_preprocess_page__mercury_editor(&$variables) {
  // Get mobile presets from the Mercury Editor settings.
  $mobile_presets = \Drupal::config('mercury_editor.settings')->get('mobile_presets') ?? [];
  if ($mobile_presets) {
    // Parse mobile presets into an array.
    $mobile_presets_names = array_map(function ($preset) {
      return $preset['width'] . 'x' . $preset['height'];
    }, $mobile_presets);
    $mobile_presets_values = array_map(function ($preset) {
      return $preset['name'] . ' (' . $preset['width'] . 'x' . $preset['height'] . ')';
    }, $mobile_presets);
    $mobile_presets_options = array_combine($mobile_presets_names, $mobile_presets_values);
    $variables['mobile_presets'] = [
      '#type' => 'select',
      '#options' => $mobile_presets_options,
      '#attributes' => [
        'class' => ['me-mobile-presets'],
        'style' => 'display: none',
      ],
    ];
  }
  // Get uuid from the route.
  $mercury_editor_entity = \Drupal::routeMatch()->getParameter('mercury_editor_entity');
  $variables['uuid'] = $mercury_editor_entity->uuid();
  $variables['entity_type'] = $mercury_editor_entity->getEntityTypeId();
}

/**
 * Implements hook_theme_registry_alter().
 *
 * Make sure this module's hook_preprocess_html implementation runs last.
 */
function mercury_editor_theme_registry_alter(&$theme_registry) {
  $index = array_search('mercury_editor_preprocess_html', $theme_registry['html']['preprocess functions']);;
  unset($theme_registry['html']['preprocess functions'][$index]);
  $theme_registry['html']['preprocess functions'][50] = 'mercury_editor_preprocess_html';
}

/**
 * Implements hook_page_attachments().
 */
function mercury_editor_page_attachments(&$attachments) {
  if (\Drupal::routeMatch()->getRouteName() == 'mercury_editor.preview') {
    $attachments['#attached']['library'][] = 'mercury_editor/preview_screen';
  }
}

/**
 * Implements hook_page_attachments_alter().
 */
function mercury_editor_page_attachments_alter(array &$attachments) {
  if (
    \Drupal::routeMatch()->getRouteName() == 'mercury_editor.preview' ||
    \Drupal::routeMatch()->getRouteObject()->getOption('_mercury_editor_route')
  ) {
    $theme = \Drupal::theme()->getActiveTheme()->getName();
    if ($theme !== 'gin') {
      // Remove all gin assets.
      $attachments['#attached']['library'] = array_filter($attachments['#attached']['library'], function ($library) {
        return strpos($library, 'gin/') !== 0;
      });
    }
    // Remove Gin Toolbar assets.
    if (in_array('gin/gin_toolbar', $attachments['#attached']['library'])) {
      $index = array_search('gin/gin_toolbar', $attachments['#attached']['library']);
      unset($attachments['#attached']['library'][$index]);
    }
  }
}

/**
 * Implements hook_element_info_alter().
 */
function mercury_editor_element_info_alter(array &$types) {
  // Attach layout_select overrides.
  if (isset($types['layout_select'])) {
    $types['layout_select']['#attached']['library'][] = 'mercury_editor/layout_select';
  }
}

/**
 * Implements hook_preprocess_layout_paragraphs_component_menu().
 */
function mercury_editor_preprocess_layout_paragraphs_builder_component_menu(&$variables) {

  $route_name = \Drupal::routeMatch()->getRouteName();
  if ($route_name !== 'mercury_editor.builder.choose_component') {
    return;
  }

  foreach (['layout', 'content'] as $category) {
    if (isset($variables['types'][$category])) {
      foreach ($variables['types'][$category] as $key => $type) {
        if (!empty($variables['types'][$category][$key]['url_object'])) {
          _mercury_editor_replace_layout_paragraphs_routes($variables['types'][$category][$key]['url_object']);
          $variables['types'][$category][$key]['url'] = $variables['types'][$category][$key]['url_object']->toString();
        }
      }
    }
  }

  $groups = Drupal::config('mercury_editor.menu.settings')->get('groups');
  $groups_array = !empty($groups)
    ? Yaml::decode($groups)
    : [
        'default' => [
          'label' => 'Default',
          'components' => [],
        ]
      ];
  $types = $variables['types']['content'];
  $variables['groups'] = [];
  foreach ($groups_array as $name => &$group) {
    $variables['groups'][$name] = [
      'items' => array_filter(array_map(function ($component) use ($types) {
          return $types[$component] ?? FALSE;
        }, array_combine($group['components'], $group['components']))),
      'label' => $group['label'],
    ];
  }
  $default_group = key(array_filter($groups_array, function($group) {
    return !empty($group['default']);
  })) ?? 'default';

  $variables['groups'] = array_filter($variables['groups'], function($group, $id) use ($default_group) {
    return count($group['items']) || $id == $default_group;
  }, ARRAY_FILTER_USE_BOTH);
  $orphaned_types = array_filter(array_keys($types), function($type) use ($variables) {
    foreach ($variables['groups'] as $group) {
      if (!empty($group['items'][$type])) {
        return FALSE;
      }
    }
    return TRUE;
  });
  foreach ($orphaned_types as $type) {
    $variables['groups'][$default_group]['items'][$type] = $types[$type];
  }
  $variables['#attached']['library'][] = 'mercury_editor/menu';
  $variables['#attached']['library'][] = 'mercury_editor/lpb_component_list';
}

/**
 * Implenents hook_preprocess_HOOK().
 * Implements hook_preprocess_layout_paragraphs_builder().
 */
function mercury_editor_preprocess_layout_paragraphs_builder(&$variables) {
  $entity = $variables['layout_paragraphs_layout']->getEntity();
  $field_name = $variables['layout_paragraphs_layout']->getFieldName();
  $field_ref = [
    $entity->getEntityTypeId(),
    $entity->id(),
    $field_name,
  ];
  $variables['attributes']['data-lp-field-ref'] = implode('/', $field_ref);
  $variables['#attached']['library'][] = 'mercury_editor/mercury_editor';
}

/**
 * Implements hook_toolbar_alter().
 */
function mercury_editor_toolbar_alter(&$items) {
  if (isset($items['primary_tasks'])) {
    $items['primary_tasks']['#weight'] = -1000;
    $classes =& $items['primary_tasks']['tray']['toolbar_actions']['#attributes']['class'];
    $classes[] = 'toolbar-menu';
    if (($key = array_search('toolbar-menu-administration', $classes)) !== false) {
      unset($classes[$key]);
    }
  }
}

/**
 * Implements hook_preproces_field().
 */
function mercury_editor_preprocess_field(&$variables) {
  // If in the mercury editor preview route.
  if (\Drupal::routeMatch()->getRouteName() !== 'mercury_editor.preview') {
    return;
  }
  if (isset($variables['entity_type']) && isset($variables['element']['#object'])) {
    $variables['attributes']['data-sync-changes'] = $variables['entity_type'] . '/' . $variables['element']['#object']->uuid() . '/' . $variables['field_name'];
  }
}

/**
 * Implements hook_entity_type_build().
 */
function mercury_editor_entity_type_build(array &$entity_types) {
  $entity_types['node']->setFormClass('mercury_editor', 'Drupal\mercury_editor\Entity\MercuryEditorNodeForm');
  $entity_types['taxonomy_term']->setFormClass('mercury_editor', 'Drupal\mercury_editor\Entity\MercuryEditorTermForm');
}

/**
 * Implements hook_entity_prepare_form().
 */
function mercury_editor_entity_prepare_form(EntityInterface $entity, $operation, FormStateInterface $form_state) {
  // If content moderation module is enabled.
  if (\Drupal::moduleHandler()->moduleExists('content_moderation')) {
    \Drupal::service('class_resolver')
      ->getInstanceFromDefinition(EntityTypeInfo::class)
      ->entityPrepareForm($entity, $operation, $form_state);
  }
}

/**
 * Implements hook_form_alter().
 */
function mercury_editor_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if (\Drupal::moduleHandler()->moduleExists('content_moderation')) {
    \Drupal::service('class_resolver')
      ->getInstanceFromDefinition(EntityTypeInfo::class)
      ->formAlter($form, $form_state, $form_id);
  }
}

/**
 * Implements hook_module_implements_alter().
 *
 * Removes hook_form_alter and hook_entity_prepare_form implementations
 * from content_moderation module.
 *
 * @see content_moderation_form_alter().
 * @see content_moderation_entity_prepare_form().
 */
function mercury_editor_module_implements_alter(&$implementations, $hook) {
  if ($hook == 'form_alter' || $hook == 'entity_prepare_form') {
    if (isset($implementations['content_moderation'])) {
      unset($implementations['content_moderation']);
    }
  }
}

/**
 * Implements hook_preprocess_toolbar().
 *
 * Replaces the edit link with the Mercury Editor edit link.
 */
function mercury_editor_preprocess_toolbar(&$variables) {
  if (!empty($variables['entity_edit_url'])) {
    $entity = $variables['entity_edit_url']->getOption('entity');
    $content_type = $entity->bundle();
    if (_mercury_editor_applies_to_content_type($content_type)) {
      $options = $variables['entity_edit_url']->getOptions();
      $parameters = $variables['entity_edit_url']->getRouteParameters();
      $parameters['mercury_editor_entity'] = $entity->uuid();
      $parameters['entity_type'] = $entity->getEntityTypeId();
      $variables['entity_edit_url'] = Url::fromRoute('mercury_editor.editor', $parameters, $options);
    }
  }
}

/**
 * Implements hook_entity_view().
 */
function mercury_editor_entity_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
  // @todo Need a better way to define when this gets added.
  $me_entity_types = \Drupal::config('mercury_editor.settings')->get('bundles');
  if ($view_mode == 'full' && isset($me_entity_types[$entity->getEntityTypeId()])) {
    $build['#attributes']['data-me-edit-screen-key'] = $entity->uuid();
    $id = $entity->id();
    if ($id) {
      $build['#attributes']['data-me-edit-screen-entity-id'] = $entity->id();
    }
  }
}

/**
 * Impelements hook_preprocess_layout_paragraphs_builder_formatter().
 */
function mercury_editor_preprocess_layout_paragraphs_builder_formatter(&$variables) {
  $variables['#attached']['library'][] = 'mercury_editor/mercury_editor';
  $variables['#attached']['library'][] = 'mercury_editor/field_formatter';
}

/**
 * Implements hook_entity_view_alter().
 */
function mercury_editor_entity_view_alter(&$build, $entity, $display) {
  $build['#attached']['library'][] = 'mercury_editor/me_dialog';
}

/**
 * Implements hook_form_FORM_ID_alter().
 * Implements hook_form_layout_paragraphs_builder_form_alter().
 */
function mercury_editor_form_layout_paragraphs_builder_form_alter(&$form, $form_state) {
  if (empty($form['#attached']['library'])) {
    $form['#attached']['library'] = [];
  }
  $form['#attached']['library'][] = 'mercury_editor/mercury_editor';
}

/**
 * Implements hook_form_FORM_ID_alter().
 * Implements hook_layout_paragraphs_component_form_alter().
 */
function mercury_editor_form_layout_paragraphs_component_form_alter(&$form, $form_state) {

  $form['uuid'] = [
    '#type' => 'hidden',
    '#value' => $form['#paragraph']->uuid(),
  ];

  $form['actions']['#attributes']['class'][] = 'me-form-actions';
  $form['#attached']['library'][] = 'mercury_editor/mercury_editor';
  $form['tabs'] = [
    '#type' => 'radios',
    '#options' => [],
    '#weight' => -1000,
    '#wrapper_attributes' => [
      'class' => ['me-horizontal-tab-radios', 'me-tabs'],
    ],
    '#after_build' => ['mercury_editor_after_build_radios'],
  ];
  if (isset($form['layout_paragraphs'])) {
    $form['layout_paragraphs']['#process'][] = 'mercury_editor_layout_paragraphs_form_process';
  }
  $form['#after_build'][] = 'mercury_editor_after_build_form';
  $field_keys = array_filter(
    Element::children($form),
    function ($key) use ($form) {
      return strpos($key, 'field_') === 0 && Element::isVisibleElement($form[$key]);
    });
  if (count($field_keys)) {
    $form['tabs']['#options']['content'] = t('Content');
    $form['tabs']['#default_value'] = 'content';
    foreach ($field_keys as $key) {
      $form[$key]['#attributes']['class'][] = 'me-tab-group';
      $form[$key]['#attributes']['class'][] = 'me-tab-group--content';
    }
  }
  // @todo Make this work for all behaviors, not just styles.
  if (isset($form['behavior_plugins'])) {
    $form['tabs']['#options']['styles'] = t('Styles');
    $form['behavior_plugins']['#type'] = 'container';
    $form['behavior_plugins']['#attributes']['class'][] = 'me-tab-group';
    $form['behavior_plugins']['#attributes']['class'][] = 'me-tab-group--styles';
  }
}

/**
 * Processes the layout paragraphs form element.
 *
 * @param array $element
 *   The form element.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   The form state.
 *
 * @return array
 *   The processed element.
 */
function mercury_editor_layout_paragraphs_form_process(array $element, FormStateInterface $form_state, &$form) {
  if (!empty($element['layout'])) {
    $form['tabs']['#options']['layout'] = t('Layout');
    $form['tabs']['#default_value'] = 'layout';
    $element['layout']['#prefix'] = '<div class="me-tab-group me-tab-group--layout">';
    $element['layout']['#suffix'] = '</div>';
  }
  if (!empty($element['config'])) {
    // @todo See if there is a better way to update the weight of this element
    // to come after the layout tab, rather than unsetting and re-adding it.
    unset($form['tabs']['#options']['styles']);
    $form['tabs']['#options']['styles'] = t('Styles');
    $element['config']['#type'] = 'container';
    $element['config']['#prefix'] = '<div class="me-tab-group me-tab-group--styles ">';
    $element['config']['#suffix'] = '</div>';
  }
  return $element;
}

/**
 * After build callback for adding classes to radio tabs.
 */
function mercury_editor_after_build_radios($element, $form_state) {
  foreach (Element::children($element) as $key) {
    $element[$key]['#wrapper_attributes']['class'][] = 'horizontal-tab--' . $key;
  }
  return $element;
}

function mercury_editor_after_build_form($form, $form_state) {
  if (isset($form['tabs']) && count($form['tabs']['#options']) < 2) {
    $form['tabs']['#access'] = FALSE;
  }
  else {
    $form['#attached']['library'][] = 'mercury_editor/horizontal_tabs';
  }
  return $form;
}


/**
 * Implements hook_form_FORM_ID_alter().
 * Implements hook_layout_paragraphs_delete_component_form_alter().
 */
function mercury_editor_form_layout_paragraphs_delete_component_form_alter(&$form, $form_state) {
  $form['actions']['#attributes']['class'][] = 'me-form-actions';
  $form['#attached']['library'][] = 'mercury_editor/lpb_component_delete_form';
}

/**
 * Returns TRUE if $type uses Mercury Editor.
 *
 * @param string $type
 *   The content type.
 *
 * @return bool
 *   TRUE if applies to content type.
 */
function _mercury_editor_applies_to_content_type($type) {
  $content_types = \Drupal::configFactory()->get('mercury_editor.settings')->get('content_types');
  if (empty($content_types[$type])) {
    return FALSE;
  }
  return TRUE;
}