geysir-8.x-1.2/geysir.module

geysir.module
<?php

/**
 * @file
 * Geysir module file.
 */

use Drupal\Component\Utility\Html;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Url;
use Drupal\entity\Routing\AdminHtmlRouteProvider;
use Drupal\node\Entity\Node;
use Drupal\node\NodeInterface;

/**
 * Implements hook_theme().
 */
function geysir_theme() {
  return [
    'geysir_field_paragraph_wrapper' => [
      'render element' => 'element',
      'file' => 'geysir.theme.inc',
    ],
  ];
}

/**
 * Implements hook_toolbar().
 */
function geysir_toolbar() {
  $items = [];

  $items['geysir'] = [
    '#cache' => [
      'contexts' => [
        'user.permissions',
      ],
    ],
  ];

  if (!Drupal::currentUser()
    ->hasPermission('geysir manage paragraphs from front-end')) {
    return $items;
  }

  $items['geysir'] += [
    '#type' => 'toolbar_item',
    'tab' => [
      '#type' => 'html_tag',
      '#tag' => 'button',
      '#value' => t('Geysir'),
      '#attributes' => [
        'class' => ['toolbar-icon', 'toolbar-icon-geysir'],
        'aria-pressed' => 'false',
        'type' => 'button',
      ],
    ],
    '#wrapper_attributes' => [
      'class' => ['geysir-toolbar-tab'],
    ],
    '#attached' => [
      'library' => [
        'geysir/geysir-toolbar',
      ],
    ],
  ];

  return $items;
}

/**
 * Implements hook_help().
 */
function geysir_help($route_name, $route_match) {
  $output = '';

  switch ($route_name) {
    // Main module help for the Geysir module.
    case 'help.page.geysir':
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('Geysir introduces several user interface optimisations which support content authors in their daily workflow. Focus lies on the page building process.') . '</p>';
      $output .= '<h3>' . t('Uses') . '</h3>';
      $output .= '<dt>' . t('Inserting new Paragraphs from the front-end') . '</dt>';
      $output .= '<dd>' . t('Geysir allows authors to insert new Paragraphs without having to go to the Drupal backend. A button is available to <em>add</em> a new Paragraph between existing Paragraphs, this button opens a modal dialog which allows inserting a new Paragraph. The modal first requires you to select a Paragraph Type to add. Then it contains all the fields that are part of the related Paragraph Bundle.') . '</dd>';
      $output .= '<dt>' . t('Editing existing Paragraphs from the front-end') . '</dt>';
      $output .= '<dd>' . t('Geysir allows authors to edit Paragraphs without having to go to the Drupal backend. When hovering over an existing Paragraph, it gets highlighted and an <em>edit</em> button appears. This button opens a modal dialog which allows editing that particular Paragraph. The modal contains all the fields that are part of the related Paragraph Bundle.') . '</dd>';
      $output .= '<dt>' . t('Deleting existing Paragraphs from the front-end') . '</dt>';
      $output .= '<dd>' . t('Next to editing Paragraphs, Geysir also allows deletion of existing paragraphs. A <em>delete</em> button is available which allows fast deletion of a Paragraph in a page.') . '</dd>';
      $output .= '<h3>' . t('Future additions') . '</h3>';
      $output .= '<p>' . t('We plan to continuously add support for new features in the near future, like the introduction of draft versions to be able to work with a page without publishing every action, reordering of paragraphs from the front-end and the reduction of clutter for authors to provide high-fidelity page previews.') . '</p>';
      break;
  }

  return $output;
}

/**
 * Implements hook_entity_type_build().
 */
function geysir_entity_type_build(array &$entity_types) {
  /* @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
  // Add forms for Paragraphs without overriding the default forms.
  $entity_types['paragraph']->setFormClass('geysir_delete', '\Drupal\geysir\Form\GeysirParagraphDeleteForm');
  $entity_types['paragraph']->setFormClass('geysir_edit', '\Drupal\geysir\Form\GeysirParagraphForm');
  $entity_types['paragraph']->setFormClass('geysir_modal_add', '\Drupal\geysir\Form\GeysirModalParagraphAddForm');
  $entity_types['paragraph']->setFormClass('geysir_modal_delete', '\Drupal\geysir\Form\GeysirModalParagraphDeleteForm');
  $entity_types['paragraph']->setFormClass('geysir_modal_edit', '\Drupal\geysir\Form\GeysirModalParagraphForm');

  if (isset($entity_types['paragraph'])) {
    $entity_types['paragraph']->setLinkTemplate('delete-form', '/paragraphs/{paragraph}/delete');

    $route_provider = $entity_types['paragraph']->getHandlerClass('route_provider');
    // During first install, the AdminHtmlRouteProvider class is not available, because entity module is not installed yet.
    // After running all updates, clearing the cache will load the AdminHtmlRouteProvider class.
    if (class_exists(AdminHtmlRouteProvider::class)) {
      $route_provider['html'] = AdminHtmlRouteProvider::class;
    }
    $entity_types['paragraph']->setHandlerClass('route_provider', $route_provider);

  }
}

/**
 * Implements hook_preprocess_HOOK().
 *
 * Using hook_preprocess_paragraph().
 */
function geysir_preprocess_paragraph(&$vars) {
  $vars['attributes']['class'][] = 'clearfix';
}

/**
 * Gets the first parent that is not a paragraph.
 *
 * @param Drupal\Core\Entity\EntityInterface $entity
 *
 * @return Drupal\Core\Entity\EntityInterface $entity
 */
function geysir_get_non_paragraph_parent(EntityInterface $entity) {
  if ($entity->getEntityTypeId() != 'paragraph') {
    return $entity;
  }
  else {
    /** @var Drupal\paragraphs\Entity\Paragraph $entity */
    return geysir_get_non_paragraph_parent($entity->getParentEntity());
  }
}

/**
 * Implements hook_preprocess_HOOK().
 *
 * Using hook_preprocess_field().
 */
function geysir_preprocess_field(&$vars) {
  if (empty($vars['field_type']) || $vars['field_type'] !== 'entity_reference_revisions') {
    return;
  }

  // Do not apply Geysir on Admin routes. This avoids rendering the buttons
  // when a paragraph is set to preview in form mode.
  // At the same time check if the admin route is not a Geysir route.
  $route = \Drupal::routeMatch()->getRouteName();
  if (\Drupal::service('router.admin_context')->isAdminRoute() && strpos($route, 'geysir.') !== 0) {
    return;
  }

  $element = &$vars['element'];
  /** @var Drupal\Core\Entity\FieldableEntityInterface $parent */
  $parent = $element['#object'];

  // Skip nested paragraphs.
  if (!($parent instanceof NodeInterface)) {
    return;
  }

  // Check update access for current node + permission to use Geysir.
  if (
    !$parent->isLatestRevision() ||
    !$parent->access('update') ||
    !\Drupal::currentUser()->hasPermission('geysir manage paragraphs from front-end')
  ) {
    return;
  }

  /** @var Drupal\entity_reference_revisions\EntityReferenceRevisionsFieldItemList $field */
  $field = $element['#items'];
  $field_definition = $field->getFieldDefinition();
  $field_storage_definition = $field_definition->getFieldStorageDefinition();
  if ($field_storage_definition->getSetting('target_type') !== 'paragraph') {
    return;
  }

  $can_add_more_fields = ($field_storage_definition->getCardinality() == -1 ||
    count($vars['items']) < $field_storage_definition->getCardinality());
  $field_wrapper_id = Html::getUniqueId('geysir--' . $vars['field_name']);

  $langcode = Drupal::languageManager()->getCurrentLanguage()->getId();
  $translated_parent = FALSE;
  $non_paragraph_parent = geysir_get_non_paragraph_parent($parent);
  if ($non_paragraph_parent->isTranslatable()) {
    $parent_translation = Drupal::service('entity.repository')
      ->getTranslationFromContext($non_paragraph_parent);
    $translated_parent = !$parent_translation->isDefaultTranslation();
  }

  $delta = 0;
  while (!empty($element[$delta])) {
    $links = [];
    /** @var \Drupal\paragraphs\Entity\Paragraph $paragraph */
    $paragraph = $element[$delta]['#paragraph'];
    $paragraph_to_cut = $paragraph;
    // Use the parent revision id if available, otherwise the parent id.
    $parent_revision_id = ($parent->getRevisionId()) ? $parent->getRevisionId() : $parent->id();

    if (!$translated_parent && $can_add_more_fields) {
      // Add link - before.
      $links['add_before'] = [
        'title' => t('Add before'),
        'url' => Url::fromRoute('geysir.modal.add_form', [
          'parent_entity_type' => $parent->getEntityType()->id(),
          'parent_entity_bundle' => $parent->bundle(),
          'parent_entity_revision' => $parent_revision_id,
          'field' => $vars['field_name'],
          'field_wrapper_id' => $field_wrapper_id,
          'delta' => $delta,
          'paragraph' => $paragraph->id(),
          'paragraph_revision' => $paragraph->getRevisionId(),
          'position' => 'before',
          'js' => 'nojs',
        ]),
        'attributes' => [
          'class' => ['use-ajax', 'geysir-button'],
          'data-dialog-type' => 'modal',
          'title' => t('Add before'),
        ],
      ];

      // Add link - after.
      $links['add_after'] = [
        'title' => t('Add after'),
        'url' => Url::fromRoute('geysir.modal.add_form', [
          'parent_entity_type' => $parent->getEntityType()->id(),
          'parent_entity_bundle' => $parent->bundle(),
          'parent_entity_revision' => $parent_revision_id,
          'field' => $vars['field_name'],
          'field_wrapper_id' => $field_wrapper_id,
          'delta' => $delta,
          'paragraph' => $paragraph->id(),
          'paragraph_revision' => $paragraph->getRevisionId(),
          'position' => 'after',
          'js' => 'nojs',
        ]),
        'attributes' => [
          'class' => ['use-ajax', 'geysir-button'],
          'data-dialog-type' => 'modal',
          'title' => t('Add after'),
        ],
      ];
    }

    // Edit link.
    $links['edit'] = [
      'title' => t('Edit'),
      'url' => Url::fromRoute('geysir.modal.edit_form', [
        'parent_entity_type' => $parent->getEntityType()->id(),
        'parent_entity_bundle' => $parent->bundle(),
        'parent_entity_revision' => $parent_revision_id,
        'field' => $vars['field_name'],
        'field_wrapper_id' => $field_wrapper_id,
        'delta' => $delta,
        'paragraph' => $paragraph->id(),
        'paragraph_revision' => $paragraph->getRevisionId(),
        'js' => 'nojs',
      ]),
      'attributes' => [
        'class' => ['use-ajax', 'geysir-button'],
        'data-dialog-type' => 'modal',
        'title' => t('Edit'),
      ],
    ];
    if ($translated_parent) {
      if (
        $paragraph->isDefaultTranslation()
      ) {
        $links['edit'] = [
          'title' => t('Translate'),
          'url' => Url::fromRoute('geysir.modal.translate_form', [
            'parent_entity_type' => $parent->getEntityType()->id(),
            'parent_entity_bundle' => $parent->bundle(),
            'parent_entity_revision' => $parent_revision_id,
            'field' => $vars['field_name'],
            'field_wrapper_id' => $field_wrapper_id,
            'delta' => $delta,
            'paragraph' => $paragraph->id(),
            'paragraph_revision' => $paragraph->getRevisionId(),
            'js' => 'nojs',
          ]),
          'attributes' => [
            'class' => ['use-ajax', 'geysir-button'],
            'data-dialog-type' => 'modal',
            'title' => t('Translate'),
          ],
        ];
      }
      if (!$paragraph->isTranslatable()) {
        unset($links['edit']);
      }
    }

    if (!$translated_parent) {
      if (count($element['#items']) > 1) {
        // Cut link.
        $links['cut'] = [
          'title' => t('Cut'),
          'url' => Url::fromRoute('geysir.cut', [
            'parent_entity_type' => $parent->getEntityType()->id(),
            'parent_entity_bundle' => $parent->bundle(),
            'parent_entity_revision' => $parent_revision_id,
            'field' => $vars['field_name'],
            'field_wrapper_id' => $field_wrapper_id,
            'delta' => $delta,
            'paragraph_to_cut' => $paragraph->id(),
            'paragraph_revision' => $paragraph->getRevisionId(),
            'js' => 'nojs',
          ]),
          'attributes' => [
            'data-geysir-field-paragraph-field-wrapper' => $field_wrapper_id,
            'class' => ['geysir-button'],
            'data-dialog-type' => 'modal',
            'title' => t('Cut'),
          ],
        ];
      }

      // Delete link.
      $links['delete'] = [
        'title' => t('Delete'),
        'url' => Url::fromRoute('geysir.modal.delete_form', [
          'parent_entity_type' => $parent->getEntityType()->id(),
          'parent_entity_bundle' => $parent->bundle(),
          'parent_entity_revision' => $parent_revision_id,
          'field' => $vars['field_name'],
          'field_wrapper_id' => $field_wrapper_id,
          'delta' => $delta,
          'paragraph' => $paragraph->id(),
          'paragraph_revision' => $paragraph->getRevisionId(),
          'js' => 'nojs',
        ]),
        'attributes' => [
          'class' => ['use-ajax', 'geysir-button'],
          'data-dialog-type' => 'modal',
          'title' => t('Delete'),
        ],
      ];
      if ($field_definition->isRequired() && count($element['#items']) == 1) {
        $links['delete']['title'] = t('Cannot remove the last item of a required field');
        $links['delete']['url'] = Url::fromUserInput('#');
        $links['delete']['attributes']['class'][] = 'disabled';
        $links['delete']['attributes']['title'] = t('Cannot remove the last item of a required field');
      }

      // Paste link - before.
      $links['paste_before'] = [
        'title' => t('Paste'),
        'url' => Url::fromRoute('geysir.paste', [
          'parent_entity_type' => $parent->getEntityType()->id(),
          'parent_entity_bundle' => $parent->bundle(),
          'parent_entity_revision' => $parent_revision_id,
          'field' => $vars['field_name'],
          'field_wrapper_id' => $field_wrapper_id,
          'delta' => $delta,
          'position' => 'before',
          'paragraph_revision' => $paragraph->getRevisionId(),
          'paragraph_to_paste' => $paragraph_to_cut->id(),
          'js' => 'nojs',
        ]),
        'attributes' => [
          'data-geysir-field-paragraph-field-wrapper' => $field_wrapper_id,
          'class' => ['use-ajax', 'geysir-button', 'geysir-paste'],
          'data-dialog-type' => 'modal',
          'title' => t('Paste'),
        ],
      ];

      // Paste link - after.
      $links['paste_after'] = [
        'title' => t('Paste'),
        'url' => Url::fromRoute('geysir.paste', [
          'parent_entity_type' => $parent->getEntityType()->id(),
          'parent_entity_bundle' => $parent->bundle(),
          'parent_entity_revision' => $parent_revision_id,
          'field' => $vars['field_name'],
          'field_wrapper_id' => $field_wrapper_id,
          'delta' => $delta,
          'position' => 'after',
          'paragraph_revision' => $paragraph->getRevisionId(),
          'paragraph_to_paste' => $paragraph_to_cut->id(),
          'js' => 'nojs',
        ]),
        'attributes' => [
          'data-geysir-field-paragraph-field-wrapper' => $field_wrapper_id,
          'class' => ['use-ajax', 'geysir-button', 'geysir-paste'],
          'data-dialog-type' => 'modal',
          'title' => t('Paste'),
        ],
      ];
    }

    $context = [
      'paragraph' => $paragraph,
      'parent' => $parent,
      'delta' => $delta,
      'field_definition' => $field_definition,
    ];

    \Drupal::moduleHandler()->alter('geysir_paragraph_links', $links, $context);

    $links_array = [
      '#theme' => 'links',
      '#links' => $links,
      '#attributes' => [
        'class' => [
          'geysir-field-paragraph-links',
          'links',
        ],
      ],
      '#attached' => [
        'library' => [
          'core/drupal.dialog.ajax',
          'geysir/geysir',
        ],
      ],
    ];

    $vars['items'][$delta]['content']['#theme_wrappers'][] = 'geysir_field_paragraph_wrapper';
    $vars['items'][$delta]['content']['#geysir_field_paragraph_links'] = Drupal::service('renderer')
      ->render($links_array);

    /** @var \Drupal\paragraphs\Entity\Paragraph $paragraph */
    $paragraph = $vars['items'][$delta]['content']['#paragraph'];
    if (
      $paragraph->isTranslatable() &&
      $paragraph->hasTranslation($langcode)
    ) {
      $vars['items'][$delta]['content']['#paragraph'] = $paragraph->getTranslation($langcode);
    }

    $delta++;
  }

  // Attach the field wrapper ID in a data-attribute.
  $vars['attributes']['data-geysir-field-paragraph-field-wrapper'] = $field_wrapper_id;
}

/**
 * Implements hook_preprocess_HOOK().
 *
 * Using hook_preprocess_node().
 */
function geysir_preprocess_node(&$vars) {
  /** @var \Drupal\node\Entity\Node $node */
  $node = $vars["node"];

  if (
    empty($node) ||
    !$node->isLatestRevision() ||
    !$node->access('update') ||
    !Drupal::currentUser()->hasPermission('geysir manage paragraphs from front-end') ||
    !$node->isDefaultTranslation()
  ) {
    return;
  }

  $field_definitions = $node->getFieldDefinitions();

  // Check if multiple paragraph fields.
  $paragraph_fields = [];
  foreach ($field_definitions as $field_definition) {
    /** @var \Drupal\Core\Field\BaseFieldDefinition $field_Definition */
    if ($field_definition->getType() == 'entity_reference_revisions') {
      /** @var \Drupal\field\Entity\FieldStorageConfig $field_storage_definition */
      $field_storage_definition = $field_definition->getFieldStorageDefinition();
      if ($field_storage_definition->getSetting('target_type') == 'paragraph') {
        $paragraph_fields[$field_storage_definition->get('field_name')] = $field_definition->getLabel();
      }
    }
  }

  if (empty($paragraph_fields)) {
    return;
  }

  foreach ($paragraph_fields as $field_name => $field_label) {
    // Check if the paragraph field already has paragraphs added.
    if (isset($vars['content'][$field_name]) && empty($vars['content'][$field_name]['#items'])) {
      $field_wrapper_id = Html::getUniqueId('geysir--' . $field_name);

      $markup = geysir_get_add_first_paragraph_markup($node, $field_name, $field_wrapper_id, $field_label);
      $markup = [
        '#type' => 'html_tag',
        '#tag' => 'div',
        '#attributes' => ['data-geysir-field-paragraph-field-wrapper' => $field_wrapper_id],
        '#value' => $markup,
      ];
      $vars['content'][$field_name]['#suffix'] = Drupal::service('renderer')
        ->render($markup);
    }
  }
}

/**
 * Get the 'Add first paragrah' link.
 *
 * @param Node $node
 *  The parent node.
 * @param $field_name
 *  The field name.
 * @param $field_wrapper_id
 *  The wrapper in place for the field.
 * @param $field_label
 *  The field label
 *
 * @return mixed
 *  The markup of the link.
 */
function geysir_get_add_first_paragraph_markup(Node $node, $field_name, $field_wrapper_id, $field_label) {
  $links['add_before'] = [
    'title' => t('Start adding content to %field_label', ['%field_label' => $field_label]),
    'url' => Url::fromRoute('geysir.modal.add_form_first', [
      'parent_entity_type' => $node->getEntityType()->id(),
      'parent_entity_bundle' => $node->bundle(),
      'parent_entity_revision' => $node->getRevisionId() ? $node->getRevisionId() : $node->getLoadedRevisionId(),
      'field' => $field_name,
      'field_wrapper_id' => $field_wrapper_id,
      'delta' => 0,
      'position' => 'before',
      'js' => 'nojs',
    ]),
    'attributes' => [
      'class' => ['use-ajax', 'geysir-button'],
      'data-dialog-type' => 'modal',
      'title' => t('Start adding content to %field_label', ['%field_label' => $field_label]),
    ],
  ];

  $links_array = [
    '#theme' => 'links',
    '#links' => $links,
    '#attributes' => [
      'class' => [
        'geysir-field-paragraph-links',
        'geysir-field-paragraph-add-first',
        'links',
      ],
    ],
    '#attached' => ['library' => ['core/drupal.dialog.ajax', 'geysir/geysir']],
  ];

  return Drupal::service('renderer')->render($links_array);
}

/**
 * Implements hook_preprocess_HOOK().
 *
 * Adds classes to link elements.
 */
function geysir_preprocess_links(&$variables) {
  if (empty($variables['links']) || !is_array($variables['links'])) {
    return;
  }

  foreach ($variables['links'] as $key => $link) {
    if (!$key || !is_string($key)) {
      continue;
    }

    $variables['links'][$key]['attributes']['class'][] = Html::cleanCssIdentifier($key);
  }
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc