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\node\NodeInterface;
use Drupal\paragraphs\ParagraphInterface;

/**
 * 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' && _stanford_decoupled_is_decoupled()) {
    // 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' && _stanford_decoupled_is_decoupled());
}

/**
 * Implements hook_cron().
 */
function stanford_decoupled_cron() {
  $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) {
  if (
    !$entity instanceof ContentEntityInterface ||
    $entity->getEntityType()->isRevisionable() ||
    !_stanford_decoupled_is_decoupled()
  ) {
    return;
  }
  $entity_type_manager = \Drupal::entityTypeManager();
  $field_storage = $entity_type_manager->getStorage('field_storage_config');

  // 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) {
      $referencing_entity = $entity_storage->load($referencing_id);;

      next_entity_update($referencing_entity);

      // Now if it's a node, look to see if there was a migration that imported
      // the node. If there was, mark the record as needing update. This will
      // update the node on the next cron job.
      if ($referencing_entity instanceof NodeInterface) {
        /** @var \Drupal\migrate_plus\Entity\Migration $migration */
        $migration_entity = \Drupal::service('stanford_migrate')
          ->getNodesMigration($referencing_entity);

        if ($migration_entity) {
          /** @var \Drupal\migrate\Plugin\Migration $migration */
          $migration = \Drupal::service('plugin.manager.migration')->createInstance($migration_entity->id());
          $source_ids = $migration->getIdMap()->lookupSourceId(['nid' => $referencing_entity->id()]);
          $migration->getIdMap()->setUpdate($source_ids);
        }
      }
    }
  }
}

/**
 * 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();
  }

  $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_type.$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' && _stanford_decoupled_is_decoupled()) {
    // 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'];
  }
}

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

  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_preprocess_HOOK().
 */
function stanford_decoupled_preprocess_file_link(&$variables) {
  if (_stanford_decoupled_is_decoupled()) {
    $variables['link']['#url']->setAbsolute();
  }
}

/**
 * Check if the site is configured to be decoupled.
 *
 * @return bool
 *   True if it's intended to be decoupled.
 */
function _stanford_decoupled_is_decoupled(): bool {
  if (!\Drupal::entityTypeManager()->hasDefinition('next_site')) {
    return FALSE;
  }
  $sites_count = \Drupal::entityTypeManager()
    ->getStorage('next_site')
    ->getQuery()
    ->accessCheck(FALSE)
    ->count()
    ->execute();
  return !!$sites_count;
}