localgov_publications-1.0.14/localgov_publications.module

localgov_publications.module
<?php

/**
 * @file
 * Module file for the LocalGov Publications module.
 */

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Config\Entity\ConfigDependencyManager;
use Drupal\Core\Config\FileStorage;
use Drupal\Core\Config\InstallStorage;
use Drupal\Core\Config\StorageInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Drupal\block\Entity\Block;
use Drupal\localgov_roles\RolesHelper;
use Drupal\node\Entity\Node;
use Drupal\node\NodeInterface;

/**
 * Implements hook_theme().
 */
function localgov_publications_theme($existing, $type, $theme, $path): array {
  return [
    'book_navigation__publication' => [
      'template' => 'book-navigation--publication',
      'base hook' => 'book_navigation__publication',
    ],
    'localgov_publication_page_header_block' => [
      'variables' => [
        'title' => '',
        'node_title' => '',
        'published_date' => NULL,
        'last_updated_date' => NULL,
      ],
    ],
    'paragraph__localgov_publications_banner' => [
      'template' => 'paragraph--localgov-publications-banner',
      'base hook' => 'paragraph',
    ],
    'media__document__publication' => [
      'template' => 'media--document--publication',
      'base hook' => 'media',
    ],
    'field__localgov_publication' => [
      'template' => 'publication-html-reference',
      'base hook' => 'field',
    ],
  ];
}

/**
 * Implements hook_localgov_role_default().
 */
function localgov_publications_localgov_roles_default(): array {
  return [
    RolesHelper::EDITOR_ROLE => [
      'access publication views',
      'add content to books',
      'administer book outlines',
      'create new books',
      'create localgov_publication_page content',
      'create localgov_publication_cover_page content',
      'delete any localgov_publication_page content',
      'delete any localgov_publication_cover_page content',
      'delete localgov_publication_page revisions',
      'delete localgov_publication_cover_page revisions',
      'delete own localgov_publication_page content',
      'delete own localgov_publication_cover_page content',
      'edit any localgov_publication_page content',
      'edit any localgov_publication_cover_page content',
      'edit own localgov_publication_page content',
      'edit own localgov_publication_cover_page content',
      'revert localgov_publication_page revisions',
      'revert localgov_publication_cover_page revisions',
      'view localgov_publication_page revisions',
      'view localgov_publication_cover_page revisions',
    ],
  ];
}

/**
 * Is the given type one of the publication node types?
 */
function localgov_publications_is_publication_type(string $type): bool {
  return $type === 'localgov_publication_page' || $type === 'localgov_publication_cover_page';
}

/**
 * Implements hook_theme_suggestions_HOOK().
 */
function localgov_publications_theme_suggestions_book_navigation(array $variables): array {
  $suggestions = [];

  // Only add suggestion on publication pages and publication cover pages.
  $node = \Drupal::routeMatch()->getParameter('node');
  if (localgov_publications_is_publication_type($node->getType())) {
    $suggestions[] = $variables['theme_hook_original'] . '__publication';
  }

  return $suggestions;
}

/**
 * Implements hook_block_access().
 */
function localgov_publications_block_access(Block $block, $operation, AccountInterface $account) {
  if ($block->getPluginId() == 'localgov_page_header_block' && $operation == 'view') {
    $node = \Drupal::routeMatch()->getParameter('node');
    if ($node instanceof NodeInterface && localgov_publications_is_publication_type($node->getType())) {
      return AccessResult::forbiddenIf(TRUE)->addCacheableDependency($block);
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter() for book_admin_edit.
 */
function localgov_publications_form_book_admin_edit_alter(&$form, FormStateInterface $form_state, $form_id): void {

  // If we're on the route this module uses for this form, change some wording.
  $route_name = \Drupal::routeMatch()->getRouteName();
  if ($route_name === 'publication.admin_edit') {
    $form['save']['#value'] = t('Save publication pages');
  }
}

/**
 * Change 'book' to 'publication' in text.
 *
 * This function can accept either a string or TranslatableMarkup.
 * If a TranslatableMarkup was passed, the arguments and options are preserved.
 */
function localgov_publications_book_to_publication(TranslatableMarkup|string $originalString): TranslatableMarkup|string {

  // Normalise TranslatableMarkup to a string.
  if ($originalString instanceof TranslatableMarkup) {
    $string = $originalString->getUntranslatedString();
    $arguments = $originalString->getArguments();
    $options = $originalString->getOptions();
  }
  else {
    $string = $originalString;
    $arguments = [];
    $options = [];
  }

  // We can't just search & replace 'book' with 'publication' in the string, as
  // that breaks the code style rule about not passing variables to t() or
  // TranslatableMarkup(). So we'll do it like this:
  $strings = [
    'Book outline' =>
    new TranslatableMarkup('Publication outline', $arguments, $options),
    'Book' =>
    new TranslatableMarkup('Publication', $arguments, $options),
    '- Create a new book -' =>
    new TranslatableMarkup('- Create a new publication -', $arguments, $options),
    'Your page will be part of the selected book' =>
    new TranslatableMarkup('Your page will be part of the selected publication', $arguments, $options),
    '<div id="edit-book-plid-wrapper"><em>No book selected.</em>' =>
    new TranslatableMarkup('<div id="edit-book-plid-wrapper"><em>No publication selected.</em>', $arguments, $options),
    '<div id="edit-book-plid-wrapper"><em>This will be the top-level page in this book.</em>' =>
    new TranslatableMarkup('<div id="edit-book-plid-wrapper"><em>This will be the top-level page in this publication.</em>', $arguments, $options),
    'The parent page in the book. The maximum depth for a book and all child pages is @maxdepth. Some pages in the selected book may not be available as parents if selecting them would exceed this limit.' =>
    new TranslatableMarkup('The parent page in the publication. The maximum depth for a publication and all child pages is @maxdepth. Some pages in the selected publication may not be available as parents if selecting them would exceed this limit.', $arguments, $options),
  ];

  if (isset($strings[$string])) {
    return $strings[$string];
  }

  return $originalString;
}

/**
 * Alter the node forms (add and edit)
 *
 * This function is called from both
 *   localgov_publications_form_node_form_alter (/node/add)
 * and
 *   localgov_publications_form_node_edit_form_alter (/node/x/edit)
 *
 * @param array $form
 *   Form elements.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   Form state object.
 * @param string $form_id
 *   Form ID string.
 */
function _localgov_publications_node_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void {

  // If this form element isn't present there's nothing for us to do here.
  if (!isset($form['book'])) {
    return;
  }

  $publication_node_forms = [
    'node_localgov_publication_page_form',
    'node_localgov_publication_page_edit_form',
  ];

  // Alter the publications node forms.
  if (in_array($form_id, $publication_node_forms, TRUE)) {

    // Attach JS.
    if ($form['book']['#attached']['library'][0] == 'book/drupal.book') {
      $form['book']['#attached']['library'][0] = 'localgov_publications/localgov-publications';
    }

    // Attach our validation.
    $form['#validate'][] = 'localgov_publications_validate_node_form';

    // All these places in the form contain the word 'book'.
    // We want them to say 'publication' instead.
    $form['book']['#title'] = localgov_publications_book_to_publication($form['book']['#title']);
    $form['book']['bid']['#title'] = localgov_publications_book_to_publication($form['book']['bid']['#title']);
    $form['book']['bid']['#description'] = localgov_publications_book_to_publication($form['book']['bid']['#description']);
    if (isset($form['book']['bid']['#options']['new'])) {
      $form['book']['bid']['#options']['new'] = localgov_publications_book_to_publication($form['book']['bid']['#options']['new']);
    }

    // New book will be the node ID on the edit page.
    $nid = $form_state->getFormObject()->getEntity()->id();
    if (isset($form['book']['bid']['#options'][$nid])) {
      $form['book']['bid']['#options'][$nid] = localgov_publications_book_to_publication($form['book']['bid']['#options'][$nid]);
    }

    if (isset($form['book']['pid']['#prefix'])) {
      $form['book']['pid']['#prefix'] = localgov_publications_book_to_publication($form['book']['pid']['#prefix']);
    }
    if (isset($form['book']['pid']['#description'])) {
      $form['book']['pid']['#description'] = localgov_publications_book_to_publication($form['book']['pid']['#description']);
    }

    $bids = array_filter(array_keys($form['book']['bid']['#options']), function ($item) {
      return (is_numeric($item) && $item != 0);
    });

    // Filter non publications from book selector.
    $valid_bids = _localgov_publications_valid_bids($bids);
    $form['book']['bid']['#options'] = array_filter($form['book']['bid']['#options'], function ($option) use ($valid_bids) {
      return (in_array($option, $valid_bids, TRUE) || $option == 0 || !is_numeric($option));
    }, ARRAY_FILTER_USE_KEY);
  }

  // Else, strip out publications from any books.
  else {
    $bids = array_filter(array_keys($form['book']['bid']['#options']), function ($item) {
      return (is_numeric($item) && $item != 0);
    });

    $valid_bids = _localgov_publications_valid_bids($bids);
    $form['book']['bid']['#options'] = array_filter($form['book']['bid']['#options'], function ($option) use ($valid_bids) {
      return (!in_array($option, $valid_bids, TRUE) || $option == 0 || !is_numeric($option));
    }, ARRAY_FILTER_USE_KEY);
  }
}

/**
 * Get valid publication book bids.
 *
 * @param array $bids
 *   Book ids on the node edit form.
 *
 * @return array
 *   Array of top level book page node ids, converted to integers.
 */
function _localgov_publications_valid_bids(array $bids) :array {
  $valid_bids = [];

  // Only search for valid books if $bids has values.
  if (count($bids) !== 0) {
    $node_storage = \Drupal::entityTypeManager()->getStorage('node');
    $valid_bids = $node_storage->getQuery()
      ->condition('type', 'localgov_publication_page')
      ->condition('nid', $bids, 'IN')
      ->accessCheck(TRUE)
      ->execute();

    // Convert arrray to integers for comparision with option values.
    $valid_bids = array_map(fn($valid_bid) => (int) $valid_bid, $valid_bids);
  }
  return $valid_bids;
}

/**
 * Implements hook_form_BASE_ID_alter().
 */
function localgov_publications_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id): void {
  _localgov_publications_node_form_alter($form, $form_state, $form_id);
}

/**
 * Implements hook_form_BASE_ID_alter().
 */
function localgov_publications_form_node_edit_form_alter(&$form, FormStateInterface $form_state, $form_id): void {
  _localgov_publications_node_form_alter($form, $form_state, $form_id);
}

/**
 * Form validation function.
 *
 * Ensures that either 'Create a new publication', or an existing publication
 * has been chosen from the book field.
 */
function localgov_publications_validate_node_form(&$form, FormStateInterface $form_state): void {

  if ($form_state->hasValue('book')) {
    $book = $form_state->getValue('book');
    if ($book['bid'] === '0') {
      $form_state->setErrorByName('book', t("Please choose either 'Create a new publication', or one of your existing publications for this page to be part of."));
    }
  }
}

/**
 * Implements hook_node_links_alter().
 *
 * If book module has added the "Add child page" link, and we're on a
 * publication type page, alter the link, so it creates a
 * localgov_publication_page, instead of the default book type.
 */
function localgov_publications_node_links_alter(array &$links, NodeInterface $node, array &$context): void {

  if (localgov_publications_is_publication_type($node->getType()) && isset($links['book']['#links']['book_add_child'])) {
    $links['book']['#links']['book_add_child']['url'] = Url::fromRoute('node.add', ['node_type' => 'localgov_publication_page'], ['query' => ['parent' => $node->id()]]);
  }
}

/**
 * Implements hook_preprocess_node().
 */
function localgov_publications_preprocess_node(&$variables): void {
  $view_mode = $variables['elements']['#view_mode'];
  /** @var \Drupal\node\NodeInterface $node */
  $node = $variables['elements']['#node'];

  if ($view_mode === 'full' && localgov_publications_is_publication_type($node->getType())) {
    $variables['content']['#attached']['library'][] = 'localgov_publications/localgov-publications';
  }
}

/**
 * Implements hook_modules_installed().
 */
function localgov_publications_modules_installed($modules, $is_syncing) {
  if (!$is_syncing && in_array('book', $modules, TRUE)) {
    // If book module is being installed, prevent the 'book' node type and its
    // dependencies from being installed from its config (or rather, delete it
    // -- there's no way to intercept it within the config API).
    $extension_path = \Drupal::service('extension.path.resolver')->getPath('module', 'book');
    $optional_install_path = $extension_path . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY;

    // Get all of book module's optional config.
    $storage = new FileStorage($optional_install_path, StorageInterface::DEFAULT_COLLECTION);
    $list = $storage->listAll();
    $config_to_create = $storage->readMultiple($list);

    // Filter this to those config entities that depend on the 'book' node type.
    $dependency_manager = new ConfigDependencyManager();
    $dependency_manager->setData($config_to_create);
    $dependencies = $dependency_manager->getDependentEntities('config', 'node.type.book');

    foreach (array_keys($dependencies) as $config_name) {
      \Drupal::configFactory()->getEditable($config_name)->delete();
    }
    \Drupal::configFactory()->getEditable('node.type.book')->delete();

    // The mapping of fields to bundles is stored in the key-value store. As we
    // removed the content type and its fields on a config level, we also need
    // to clear out the book data from here, as it won't be done for us like it
    // is when you delete a content type via the UI.
    $kvStore = \Drupal::keyValue('entity.definitions.bundle_field_map');
    $fieldMap = $kvStore->get('node');
    unset($fieldMap['body']['bundles']['book']);
    $kvStore->set('node', $fieldMap);

    // Clear all caches to ensure there are no references to the Book node left.
    drupal_flush_all_caches();
  }
}

/**
 * Implements hook_module_implements_alter().
 *
 * Moves our implementations of hook_entity_insert and hook_entity_update to the
 * end of the list, so they run after pathauto. If they run before pathauto, we
 * don't pick up changes to the URL of cover pages when generating URL aliases
 * for the rest of the publication.
 */
function localgov_publications_module_implements_alter(&$implementations, $hook): void {
  switch ($hook) {
    case 'entity_insert':
    case 'entity_update':
      $group = $implementations['localgov_publications'];
      unset($implementations['localgov_publications']);
      $implementations['localgov_publications'] = $group;
      break;
  }
}

/**
 * Implements hook_entity_insert().
 *
 * NB that we don't implement hook_node_insert to ensure we run after pathauto.
 */
function localgov_publications_entity_insert(EntityInterface $entity): void {
  if ($entity instanceof NodeInterface) {
    localgov_publications_update_path_aliases($entity);
  }
}

/**
 * Implements hook_entity_update().
 *
 * NB that we don't implement hook_node_update to ensure we run after pathauto.
 */
function localgov_publications_entity_update(EntityInterface $entity): void {
  if ($entity instanceof NodeInterface) {
    localgov_publications_update_path_aliases($entity);
  }
}

/**
 * Updates the path alias of every page in a publication.
 *
 * @param \Drupal\node\NodeInterface $node
 *   Cover page node.
 */
function localgov_publications_update_path_aliases(NodeInterface $node): void {
  // Only do anything if we're saving a cover page.
  if ($node->getType() !== 'localgov_publication_cover_page') {
    return;
  }

  /** @var \Drupal\book\BookManager $bookManager */
  $bookManager = \Drupal::service('book.manager');

  /** @var \Drupal\node\NodeInterface[] $publications */
  $publications = $node->get('localgov_publication')->referencedEntities();

  $publicationPages = [];

  foreach ($publications as $publication) {
    if (isset($publication->book)) {
      // Find the ID of every node in the publication.
      $bookPages = $bookManager->bookTreeGetFlat($publication->book);
      $publicationPages = array_merge($publicationPages, array_keys($bookPages));
    }
  }

  if (count($publicationPages) === 0) {
    return;
  }

  $pageNodes = Node::loadMultiple($publicationPages);
  $pathAutoGenerator = \Drupal::service('pathauto.generator');
  foreach ($pageNodes as $pageNode) {
    $pathAutoGenerator->updateEntityAlias($pageNode, 'update');
  }
}

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

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