SU-SWS/stanford_profile_helper

View on GitHub
modules/stanford_decoupled/stanford_decoupled.module

Summary

Maintainability
Test Coverage
<?php

/**
 * @file
 * stanford_decoupled.module
 */

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\graphql\GraphQL\Execution\FieldContext;
use Drupal\graphql_compose\Plugin\GraphQLCompose\GraphQLComposeFieldTypeInterface;
use Drupal\paragraphs\ParagraphInterface;
use Drupal\stanford_decoupled\Config\DecoupledConfigOverrides;
use Drupal\views\ViewExecutable;

/**
 * Implements hook_next_revalidator_info_alter().
 */
function stanford_decoupled_next_revalidator_info_alter(&$plugins) {
  $plugins['path']['class'] = 'Drupal\stanford_decoupled\Plugin\Next\Revalidator\Path';
}

/**
 * Implements hook_graphql_compose_field_type_alter().
 */
function stanford_decoupled_graphql_compose_field_type_alter(array &$field_types) {
  $field_types['image']['class'] = 'Drupal\stanford_decoupled\Plugin\GraphQLCompose\FieldType\ImageItem';
}

/**
 * Implements hook_graphql_compose_graphql_type_alter().
 */
function stanford_decoupled_graphql_compose_graphql_type_alter(array &$entity_types) {
  $entity_types['Image']['class'] = 'Drupal\stanford_decoupled\Plugin\GraphQLCompose\SchemaType\ImageType';
}

/**
 * Implements hook_library_info_alter().
 */
function stanford_decoupled_library_info_alter(&$libraries, $extension) {
  if ($extension == 'editoria11y' && DecoupledConfigOverrides::isDecoupled()) {
    // Disable Editoria11y library if the site is decoupled to avoid confusion.
    $libraries = [];
  }
}

/**
 * Implements hook_ENTITY_TYPE_access().
 */
function stanford_decoupled_layout_access(EntityInterface $entity, $operation, AccountInterface $account) {
  // Only allow access if the site is decoupled. Otherwise, don't change access.
  return AccessResult::allowedIf($operation == 'view' && DecoupledConfigOverrides::isDecoupled());
}

/**
 * Implements hook_token_info().
 */
function stanford_decoupled_token_info() {
  // Drupal 11.1.0 already has the UUID token. This can be removed at that time.
  if (version_compare(\Drupal::VERSION, '11.1', '>=')) {
    return [];
  }
  $entity_types = \Drupal::entityTypeManager()->getDefinitions();
  $info = [];

  foreach ($entity_types as $entity_id => $entity_type) {
    if ($entity_type->getGroup() == 'content') {
      $info['tokens'][$entity_id]['uuid'] = [
        'name' => t('@entity_id UUID', ['@entity_id' => $entity_type->getLabel()]),
        'description' => t('The Universal Unique Identifier of @entity_id', ['@entity_id' => $entity_type->getLabel()]),
      ];
    }
  }
  return $info;
}

/**
 * Implements hook_tokens().
 */
function stanford_decoupled_tokens($type, $tokens, array $data = [], array $options = []) {
  $replacements = [];
  if (!empty($data[$type]) && $data[$type] instanceof ContentEntityInterface) {
    $entity = $data[$type];

    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'uuid':
          $replacements[$original] = $entity->uuid();
          break;
      }
    }
  }
  return $replacements;
}

/**
 * Implements hook_cron().
 */
function stanford_decoupled_cron() {
  if (!DecoupledConfigOverrides::isDecoupled()) {
    return;
  }
  $last_run = \Drupal::state()->get('stanford-decoupled-last-ran', 0);
  $now = time();
  $node_storage = \Drupal::entityTypeManager()->getStorage('node');
  $query = $node_storage->getQuery()
    ->accessCheck(FALSE)
    ->condition('status', TRUE);

  $conditions = $query->orConditionGroup();

  $start_conditions = $query->andConditionGroup();
  $end_conditions = $query->andConditionGroup();

  $start_conditions->condition('su_event_date_time.value', $last_run, '>=');
  $start_conditions->condition('su_event_date_time.value', $now, '<=');

  $end_conditions->condition('su_event_date_time.end_value', $last_run, '>=');
  $end_conditions->condition('su_event_date_time.end_value', $now, '<=');

  $conditions->condition($start_conditions);
  $conditions->condition($end_conditions);

  $query->condition($conditions);
  $results = $query->execute();

  foreach ($node_storage->loadMultiple($results) as $node) {
    next_entity_update($node);
  }
  \Drupal::state()->set('stanford-decoupled-last-ran', $now);
}

/**
 * Implements hook_entity_insert().
 */
function stanford_decoupled_entity_insert(EntityInterface $entity) {
  _stanford_decoupled_entity_changes($entity);
}

/**
 * Implements hook_entity_update().
 */
function stanford_decoupled_entity_update(EntityInterface $entity) {
  _stanford_decoupled_entity_changes($entity);
}

/**
 * Implement hook_entity_delete().
 */
function stanford_decoupled_entity_delete(EntityInterface $entity) {
  _stanford_decoupled_entity_changes($entity);
}

/**
 * Trigger Next module for on-demand invalidation for referencing entities.
 *
 * @param \Drupal\Core\Entity\EntityInterface $entity
 *   Inserted, updated, or deleted entity.
 */
function _stanford_decoupled_entity_changes(EntityInterface $entity) {
  $tracked_types = \Drupal::config('stanford_decoupled.settings')
    ->get('referenced_invalidated_types') ?: ['taxonomy_term', 'media'];

  if (
    !in_array($entity->getEntityTypeId(), $tracked_types) ||
    !$entity instanceof ContentEntityInterface ||
    !DecoupledConfigOverrides::isDecoupled()
  ) {
    return;
  }
  $entity_type_manager = \Drupal::entityTypeManager();
  $field_storage = $entity_type_manager->getStorage('field_storage_config');

  /** @var \Drupal\Core\Queue\QueueFactoryInterface $queue_factory */
  $queue_factory = \Drupal::service('queue');
  $queue = $queue_factory->get('decoupled_referenced_invalidator');

  // Loop through the fields that use the changed entity as a possible target.
  // We'll then query to find entities that do reference the entity, and call
  // the Next functions to trigger any invalidations that are configured.
  /** @var \Drupal\field\FieldStorageConfigInterface[] $reference_fields */
  $reference_fields = $field_storage->loadByProperties(['settings' => ['target_type' => $entity->getEntityTypeId()]]);
  foreach ($reference_fields as $field) {
    $entity_storage = $entity_type_manager->getStorage($field->getTargetEntityTypeId());
    $referencing_ids = $entity_storage->getQuery()
      ->accessCheck(FALSE)
      ->condition($field->getName(), $entity->id())
      ->execute();

    foreach ($referencing_ids as $referencing_id) {
      $queue->createItem([
        'entity_type' => $field->getTargetEntityTypeId(),
        'id' => $referencing_id,
      ]);
    }
  }
}

/**
 * Implements hook_graphql_compose_field_results_alter().
 */
function stanford_decoupled_graphql_compose_field_results_alter(array &$results, $entity, GraphQLComposeFieldTypeInterface $plugin, FieldContext $context) {
  $field_definition = $plugin->getFieldDefinition();
  if ($field_definition->getName() == 'layout_selection') {
    foreach ($results as &$result) {
      $result = ['id' => $result->id(), 'label' => $result->label()];
    }
  }

  foreach ($results as $result) {
    if ($result instanceof ParagraphInterface) {
      $behaviors = $result->getAllBehaviorSettings();
      $result->set('behavior_settings', $behaviors ? json_encode($behaviors) : NULL);
    }
  }
}

/**
 * Implements hook_graphql_compose_entity_base_fields_alter().
 */
function stanford_decoupled_graphql_compose_entity_base_fields_alter(array &$fields, string $entity_type_id) {
  if ($entity_type_id == 'paragraph') {
    $fields['behavior_settings'] = [
      'field_type' => 'string',
      'name_sdl' => 'behaviors',
      'required' => FALSE,
      'description' => t('Paragraph Behavior Settings.'),
    ];
  }
}

/**
 * Implements hook_ENTITY_TYPE_insert().
 *
 * When a new Next site is created, create all Next entity type configs.
 */
function stanford_decoupled_next_site_insert(EntityInterface $entity) {
  Cache::invalidateTags(['library_info']);
  $next_storage = \Drupal::entityTypeManager()
    ->getStorage('next_entity_type_config');
  $node_types = \Drupal::entityTypeManager()
    ->getStorage('node_type')
    ->loadMultiple();

  // Create each of the node type bundle configs.
  foreach (array_keys($node_types) as $node_bundle) {
    // Make sure one doesn't already exist.
    if (!$next_storage->load("node.$node_bundle")) {
      $next_storage->create([
        'id' => "node.$node_bundle",
        'site_resolver' => 'site_selector',
        'revalidator' => 'path',
        'configuration' => [
          'sites' => [$entity->id() => $entity->id()],
        ],
        'revalidator_configuration' => [
          'revalidate_page' => TRUE,
          'additional_paths' => "/tags/views:all/views:$node_bundle",
        ],
      ])->save();
    }
  }

  if (!$next_storage->load('redirect.redirect')) {
    $next_storage->create([
      'id' => 'redirect.redirect',
      'site_resolver' => 'site_selector',
      'revalidator' => 'redirect_path',
      'configuration' => ['sites' => [$entity->id() => $entity->id()]],
      'revalidator_configuration' => [],
    ])->save();
  }

  if (!$next_storage->load('menu_link_content.menu_link_content')) {
    $next_storage->create([
      'id' => 'menu_link_content.menu_link_content',
      'site_resolver' => 'site_selector',
      'revalidator' => 'path',
      'configuration' => ['sites' => [$entity->id() => $entity->id()]],
      'revalidator_configuration' => [
        'revalidate_page' => FALSE,
        'additional_paths' => '/tags/menu:main',
      ],
    ])->save();
  }

  $config_page_types = [
    'lockup_settings',
    'stanford_global_message',
    'stanford_local_footer',
    'stanford_basic_site_settings',
    'stanford_super_footer',
  ];
  // Create each of the node type bundle configs.
  foreach ($config_page_types as $config_page_type) {
    // Make sure one doesn't already exist.
    if (!$next_storage->load("config_pages.$config_page_type")) {
      $next_storage->create([
        'id' => "config_pages.$config_page_type",
        'site_resolver' => 'site_selector',
        'revalidator' => 'path',
        'configuration' => [
          'sites' => [$entity->id() => $entity->id()],
        ],
        'revalidator_configuration' => [
          'revalidate_page' => FALSE,
          'additional_paths' => "/tags/config-pages",
        ],
      ])->save();
    }
  }
}

/**
 * Implements hook_ENTITY_TYPE_access().
 */
function stanford_decoupled_redirect_access(EntityInterface $entity, $operation, AccountInterface $account) {
  if ($operation == 'view' && DecoupledConfigOverrides::isDecoupled()) {
    // Allowing viewing redirecting from JSON API.
    return AccessResult::allowed();
  }
  return AccessResult::neutral();
}

/**
 * Implements hook_next_site_preview_alter().
 */
function stanford_decoupled_next_site_preview_alter(array &$preview, array $context) {
  // Only use the preview for nodes. Prevent the preview from any other entity
  // type that might have a revalidation configured, like redirects.
  if ($context['entity']->getEntityTypeid() != 'node') {
    $preview = $context['original_build'][0]['content'];
  }

  if (isset($preview['toolbar']['links']['#links']['live_link']['url'])) {
    /** @var \Drupal\Core\Url $url */
    $url = $preview['toolbar']['links']['#links']['live_link']['url'];
    $options = $url->getOptions();
    // No need for all the other parameters.
    if (isset($options['query']['slug'])) {
      $options['query'] = [
        'slug' => $options['query']['slug'],
        'secret' => $options['query']['secret'],
      ];
      $url->setOptions($options);
    }
  }
}

/**
 * Implements hook_preprocess_HOOK().
 */
function stanford_decoupled_preprocess_image(&$variables) {
  $decoupled = DecoupledConfigOverrides::isDecoupled();

  if ($decoupled && (empty($variables['width']) || empty($variables['height']))) {
    $path = str_starts_with($variables['uri'], '/') ? DRUPAL_ROOT . $variables['uri'] : $variables['uri'];
    $path = preg_replace('/\?.*$/', '', $path);
    if ($size = @getimagesize($path)) {
      $variables['attributes']['data-width'] = $size[0];
      $variables['attributes']['data-height'] = $size[1];
    }
  }
}

/**
 * Implements hook_views_pre_execute().
 */
function stanford_decoupled_views_pre_execute(ViewExecutable $view) {
  $route = \Drupal::routeMatch()->getRouteName();
  if (!($route == 'entity.node.canonical' && DecoupledConfigOverrides::isDecoupled())) {
    return;
  }
  $current_limit = $view->query->getLimit();
  if ($current_limit <= 0 || $current_limit > 5) {
    $view->query->setLimit(30);
  }
}

/**
 * Implements hook_preprocess_HOOK().
 */
function stanford_decoupled_preprocess_file_link(&$variables) {
  if (DecoupledConfigOverrides::isDecoupled()) {
    $variables['link']['#url']->setAbsolute();
  }
}