You are here

acquia_contenthub.module in Acquia Content Hub 8

Same filename and directory in other branches
  1. 8.2 acquia_contenthub.module

File

acquia_contenthub.module
View source
<?php

/**
 * @file
 * Contains acquia_contenthub.module.
 */
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drush\Drush;

/**
 * Implements hook_help().
 */
function acquia_contenthub_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    case 'help.page.acquia_contenthub':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('Some info about Acquia Content Hub') . '</p>';
      return $output;
  }
}

/**
 * Implements hook_entity_insert().
 */
function acquia_contenthub_entity_insert(EntityInterface $entity) {

  /** @var \Drupal\acquia_contenthub\EntityManager $entity_manager */
  $entity_manager = \Drupal::service('acquia_contenthub.entity_manager');
  $entity_manager
    ->enqueueCandidateEntity($entity);
}

/**
 * Implements hook_entity_update().
 */
function acquia_contenthub_entity_update(EntityInterface $entity) {

  /** @var \Drupal\acquia_contenthub\EntityManager $entity_manager */
  $entity_manager = \Drupal::service('acquia_contenthub.entity_manager');
  $entity_manager
    ->enqueueCandidateEntity($entity);

  /** @var \Drupal\acquia_contenthub\ImportEntityManager $import_entity_manager */
  $import_entity_manager = \Drupal::service('acquia_contenthub.import_entity_manager');
  $import_entity_manager
    ->entityUpdate($entity);
}

/**
 * Implements hook_entity_delete().
 */
function acquia_contenthub_entity_delete(EntityInterface $entity) {

  /** @var \Drupal\acquia_contenthub\EntityManager $entity_manager */
  $entity_manager = \Drupal::service('acquia_contenthub.entity_manager');
  $entity_manager
    ->enqueueCandidateEntity($entity, FALSE);

  /** @var \Drupal\acquia_contenthub\ImportEntityManager $import_entity_manager */
  $import_entity_manager = \Drupal::service('acquia_contenthub.import_entity_manager');
  $import_entity_manager
    ->entityDelete($entity);
}

/**
 * Implements hook_entity_presave().
 */
function acquia_contenthub_entity_presave(EntityInterface $entity) {

  /** @var \Drupal\acquia_contenthub\ImportEntityManager $import_entity_manager */
  $import_entity_manager = \Drupal::service('acquia_contenthub.import_entity_manager');
  $import_entity_manager
    ->entityPresave($entity);
}

/**
 * Process all candidate entities and insert/update/delete on Content Hub.
 */
function acquia_contenthub_bulk_export() {

  /** @var \Drupal\acquia_contenthub\EntityManager $entity_manager */
  $entity_manager = \Drupal::service('acquia_contenthub.entity_manager');
  $entity_manager
    ->bulkExport();
  $candidate_entities = $entity_manager
    ->getExportCandidateEntities();

  /** @var \Drupal\acquia_contenthub\Controller\ContentHubEntityExportController $export_controller */
  $export_controller = \Drupal::service('acquia_contenthub.acquia_contenthub_export_entities');

  // Export Entities.
  $use_export_queue = $export_controller
    ->exportEntities($candidate_entities);
}

/**
 * Generic alter handler for the entity form. Not natively hooked by naming.
 *
 * Implements hook_form_BASE_FORM_ID_alter().
 */
function acquia_contenthub_form_entity_form_alter(&$form, $form_state, $entity_type_id) {

  /* @var Drupal\Core\Entity\FieldableEntityInterface $entity */
  $entity = $form_state
    ->getFormObject()
    ->getEntity();
  $entity_form_service = \Drupal::service('acquia_contenthub.form.entity_form');
  $entity_form = $entity_form_service
    ->getForm($entity);
  if (!$entity_form) {
    return;
  }

  // Attach node form.
  $form['acquia_contenthub'] = $entity_form;

  // Attach submit handler.
  $submit_handler_name = 'acquia_contenthub_form_' . $entity_type_id . '_form_submit';
  $entity_form_service
    ->attachSubmitHandler($form['actions'], $submit_handler_name);
}

/**
 * Generic submit handler for the entity form. Not natively hooked by naming.
 *
 * @see acquia_contenthub_form_entity_form_alter()
 */
function acquia_contenthub_form_entity_form_submit(FormStateInterface $form_state) {
  $entity_form_service = \Drupal::service('acquia_contenthub.form.entity_form');
  $entity_form_service
    ->saveSettings($form_state);
}

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 */
function acquia_contenthub_form_node_form_alter(&$form, $form_state) {
  acquia_contenthub_form_entity_form_alter($form, $form_state, 'node');
}

/**
 * Submit handler for the node form with acquia contenthub options.
 *
 * @see acquia_contenthub_form_node_form_alter()
 */
function acquia_contenthub_form_node_form_submit($form, FormStateInterface $form_state) {
  acquia_contenthub_form_entity_form_submit($form_state);
}

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 */
function acquia_contenthub_form_taxonomy_term_form_alter(&$form, $form_state) {
  acquia_contenthub_form_entity_form_alter($form, $form_state, 'taxonomy_term');
}

/**
 * Submit handler for the taxonomy term form with acquia contenthub options.
 *
 * @see acquia_contenthub_form_taxonomy_term_form_alter()
 */
function acquia_contenthub_form_taxonomy_term_form_submit($form, FormStateInterface $form_state) {
  acquia_contenthub_form_entity_form_submit($form_state);
}

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 */
function acquia_contenthub_form_block_content_form_alter(&$form, $form_state) {
  acquia_contenthub_form_entity_form_alter($form, $form_state, 'block_content');
}

/**
 * Submit handler for the block content form with acquia contenthub options.
 *
 * @see acquia_contenthub_form_block_content_form_alter()
 */
function acquia_contenthub_form_block_content_form_submit($form, FormStateInterface $form_state) {
  acquia_contenthub_form_entity_form_submit($form_state);
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function acquia_contenthub_form_node_type_form_alter(array &$form, FormStateInterface &$form_state) {
  $node_type = $form_state
    ->getFormObject()
    ->getEntity()
    ->id();

  /** @var \Drupal\acquia_contenthub\EntityManager $entity_manager */
  $entity_manager = \Drupal::service('acquia_contenthub.entity_manager');

  /** @var \Drupal\acquia_contenthub\Entity\ContentHubEntityTypeConfig $entity_type_config */
  $entity_type_config = $entity_manager
    ->getContentHubEntityTypeConfigurationEntity('node');

  // Only enable selection of preview images for entities that have been
  // previously selected to work with Acquia Content Hub.
  if (!empty($entity_type_config) && $entity_type_config
    ->isEnableIndex($node_type)) {
    $form['acquia_contenthub'] = \Drupal::service('acquia_contenthub.form.node_type_preview_image_form')
      ->getForm($node_type);
    $form['actions']['submit']['#submit'][] = 'acquia_contenthub_form_node_type_form_submit';
  }
}

/**
 * Submit handler for the node type form with acquia contenthub options.
 *
 * @see acquia_contenthub_form_node_type_form_alter()
 */
function acquia_contenthub_form_node_type_form_submit(array $form, FormStateInterface &$form_state) {
  $node_type = $form_state
    ->getFormObject()
    ->getEntity()
    ->id();
  $settings = $form_state
    ->getValue('acquia_contenthub');
  \Drupal::service('acquia_contenthub.form.node_type_preview_image_form')
    ->saveSettings($node_type, $settings);
}

/**
 * Implements hook_theme().
 */
function acquia_contenthub_theme($existing, $type, $theme, $path) {

  // Normally theme suggestion templates are only picked up when they are in
  // themes. We explicitly define theme suggestions here so that the templates
  // in the templates folder are picked up.
  return [
    'html__acquia_contenthub' => [
      'template' => 'html--acquia-contenthub',
      'render element' => 'html',
      'preprocess functions' => [
        'template_preprocess_html',
      ],
    ],
    'page__acquia_contenthub' => [
      'template' => 'page--acquia-contenthub',
      'render element' => 'page',
      'preprocess functions' => [
        'template_preprocess_page',
      ],
    ],
    'region__content__acquia_contenthub' => [
      'template' => 'region--content--acquia-contenthub',
      'render element' => 'elements',
      'preprocess functions' => [
        'template_preprocess_region',
      ],
    ],
    'block__block_content__acquia_contenthub' => [
      'render element' => 'elements',
      'base hook' => 'block',
    ],
  ];
}

/**
 * Implements hook_theme_suggestions_HOOK().
 */
function acquia_contenthub_theme_suggestions_region(array $variables) {
  if ($variables['elements']['#region'] === 'content' && \Drupal::routeMatch()
    ->getRouteName() === 'acquia_contenthub.content_entity_display.entity') {
    return [
      'region__content__acquia_contenthub',
    ];
  }
}

/**
 * Implements hook_theme_suggestions_HOOK_alter().
 *
 * Provides block_content templates:
 *  - block__block_content__acquia_contenthub
 *  - block__block_content__BLOCK_UUID__acquia_contenthub.
 *
 * @see block_theme_suggestions_block()
 */
function acquia_contenthub_theme_suggestions_block_alter(array &$suggestions, array $variables) {
  if ($variables['elements']['#configuration']['provider'] === 'block_content' && \Drupal::routeMatch()
    ->getRouteName() === 'acquia_contenthub.content_entity_display.entity') {

    // We can safely explode on : because we know the Block plugin type manager
    // enforces that delimiter for all derivatives.
    // Example value: block_content:12345678-9abc-0123-4567-000000000000.
    $parts = explode(':', $variables['elements']['#plugin_id']);
    $suggestion = 'block';
    foreach ($parts as $part) {
      $suggestion .= '__' . strtr($part, '-', '_');
      $suggestions[] = $suggestion . '__acquia_contenthub';
    }
  }
}

/**
 * Batch finished function for re-export entities.
 *
 * @param bool $success
 *   TRUE if succeeded, FALSE otherwise.
 * @param mixed $results
 *   The results array.
 * @param mixed $operations
 *   The operations array.
 */
function acquia_contenthub_reexport_finished($success, $results, $operations) {
  if ($success) {
    $message = \Drupal::translation()
      ->formatPlural(count($results), 'One entity processed.', '@count entities processed.');

    /** @var \Drupal\acquia_contenthub\Controller\ContentHubReindex $reindex */
    $reindex = \Drupal::service('acquia_contenthub.acquia_contenthub_reindex');
    $reindex
      ->setReindexStateNone();
  }
  else {
    $message = t('Finished with an error.');
  }
  \Drupal::messenger()
    ->addStatus($message);
}

/**
 * Implements hook_element_info_alter().
 */
function acquia_contenthub_element_info_alter(&$type) {

  // Remove the off-canvas page wrapper that was added to core in 8.5.0.
  // This wrapper is only needed for the off-canvas dialog.
  if (\Drupal::routeMatch()
    ->getRouteName() === 'acquia_contenthub.content_entity_display.entity') {
    unset($type['page']['#theme_wrappers']['off_canvas_page_wrapper']);
  }
}

/**
 * Implements hook_module_implements_alter().
 */
function acquia_contenthub_module_implements_alter(&$implementations, $hook) {

  // Ensure that our implementation of hook_element_info_alter runs after the
  // system modules implementation.
  if ($hook === 'element_info_alter') {
    $implementation = $implementations['acquia_contenthub'];
    unset($implementations['acquia_contenthub']);
    $implementations['acquia_contenthub'] = $implementation;
  }
}

/**
 * Checks published entities and compares them with Content Hub.
 *
 * This method also republishes entities if they are not in sync with what
 * exists currently in Content Hub.
 *
 * @param array $entities
 *   An array of records from the tracking table.
 * @param bool $republish
 *   1 to republish entities, FALSE to just print.
 * @param mixed $context
 *   The context array.
 *
 * @return mixed|false
 *   Drush Output.
 */
function acquia_contenthub_audit_publisher(array $entities, $republish, &$context) {
  if (empty($context['sandbox'])) {
    $context['results']['not_published'] = !empty($context['results']['not_published']) ? $context['results']['not_published'] : 0;
    $context['results']['outdated'] = !empty($context['results']['outdated']) ? $context['results']['outdated'] : 0;
  }

  /** @var \Drupal\acquia_contenthub\Client\ClientManager $client_manager */
  $client_manager = \Drupal::service('acquia_contenthub.client_manager');
  if (!$client_manager
    ->isConnected()) {
    throw new \Exception(dt('The Content Hub client is not connected so no operations could be performed.'));
  }

  // Collect UUIDs.
  $uuids = [];
  foreach ($entities as $entity) {
    $uuids[] = $entity->entity_uuid;
  }

  /** @var \Drupal\acquia_contenthub\EntityManager $entity_manager */
  $entity_manager = \Drupal::service('acquia_contenthub.entity_manager');

  /** @var \Acquia\ContentHubClient\Entity[] $ch_entities */
  $ch_entities = $client_manager
    ->createRequest('readEntities', [
    $uuids,
  ]);
  foreach ($entities as $entity) {
    $out_of_sync = FALSE;
    $uuid = $entity->entity_uuid;
    $ch_entity = isset($ch_entities[$uuid]) ? $ch_entities[$uuid] : FALSE;
    if (!$ch_entity) {

      // Entity does not exist in Content Hub.
      Drush::output()
        ->writeln(dt('Entity not published: Entity Type = @type, UUID = @uuid, ID = @id, Modified = @modified', [
        '@type' => $entity->entity_type,
        '@uuid' => $entity->entity_uuid,
        '@id' => $entity->entity_id,
        '@modified' => $entity->modified,
      ]));
      $out_of_sync = TRUE;
      $context['results']['not_published']++;
    }
    elseif ($ch_entity && $entity->modified !== $ch_entity
      ->getModified()) {

      // Entity exists in Content Hub but the modified flag does not match.
      Drush::output()
        ->writeln(dt('Outdated entity: Entity Type = @type, UUID = @uuid, ID = @id, Modified (local) = @lmodified, Modified (remote) = @rmodified', [
        '@type' => $entity->entity_type,
        '@uuid' => $entity->entity_uuid,
        '@id' => $entity->entity_id,
        '@lmodified' => $entity->modified,
        '@rmodified' => $ch_entity
          ->getModified(),
      ]));
      $out_of_sync = TRUE;
      $context['results']['outdated']++;
    }
    if ($out_of_sync) {
      $entity_id = FALSE;
      if ($republish) {
        $drupal_entity = \Drupal::entityTypeManager()
          ->getStorage($entity->entity_type)
          ->load($entity->entity_id);
        if ($drupal_entity) {
          $entity_id = $drupal_entity
            ->id();
          if ($entity_manager
            ->isEligibleEntity($drupal_entity)) {

            /** @var \Drupal\acquia_contenthub\Controller\ContentHubEntityExportController $export_controller */
            $export_controller = \Drupal::service('acquia_contenthub.acquia_contenthub_export_entities');

            // Export Entities.
            $export_controller
              ->exportEntities([
              $drupal_entity,
            ]);
          }
          else {

            // Entity is not eligible for exporting anymore.
            Drush::output()
              ->writeln(dt('Entity exists in the tracking table but is no longer eligible for exporting: Entity Type = @type, UUID = @uuid, ID = @id, Modified (local) = @lmodified, Modified (remote) = @rmodified', [
              '@type' => $entity->entity_type,
              '@uuid' => $entity->entity_uuid,
              '@id' => $entity->entity_id,
              '@lmodified' => $entity->modified,
              '@rmodified' => $ch_entity
                ->getModified(),
            ]));
          }
        }
      }
      else {
        $entity_type = \Drupal::entityTypeManager()
          ->getStorage($entity->entity_type)
          ->getEntityType();
        $table = $entity_type
          ->getBaseTable();
        $id_col = $entity_type
          ->getKey("id");
        $query = \Drupal::database()
          ->select($table)
          ->fields($table, [
          $id_col,
        ]);
        $query
          ->condition("{$table}.{$id_col}", $entity->entity_id);
        $entity_id = $query
          ->execute()
          ->fetchField();
      }
      if (empty($entity_id)) {

        // The drupal entity could not be loaded.
        throw new \Exception(dt('This entity exists in the tracking table but could not be loaded in Drupal: Entity Type = @type, UUID = @uuid, ID = @id, Modified = @modified', [
          '@type' => $entity->entity_type,
          '@uuid' => $entity->entity_uuid,
          '@id' => $entity->entity_id,
          '@modified' => $entity->modified,
        ]));
      }
    }
  }
}

/**
 * Prints results from the comparison of the tracking table with Content Hub.
 *
 * @param bool $success
 *   TRUE if there were not PHP fatal errors, FALSE otherwise.
 * @param mixed $results
 *   An array of results.
 * @param mixed $operations
 *   The operations array.
 */
function acquia_contenthub_audit_publisher_finished($success, $results, $operations) {

  // The 'success' parameter means no fatal PHP errors were detected. All
  // other error management should be handled using 'results'.
  if ($success) {
    Drush::output()
      ->writeln(dt('Total number of audited entities not found in Content Hub: @total', [
      '@total' => $results['not_published'],
    ]));
    Drush::output()
      ->writeln(dt('Total number of audited entities found outdated in Content Hub: @total', [
      '@total' => $results['outdated'],
    ]));
  }
  else {
    Drush::output()
      ->writeln(dt('Finished with a PHP fatal error.'));
  }
}

Functions

Namesort descending Description
acquia_contenthub_audit_publisher Checks published entities and compares them with Content Hub.
acquia_contenthub_audit_publisher_finished Prints results from the comparison of the tracking table with Content Hub.
acquia_contenthub_bulk_export Process all candidate entities and insert/update/delete on Content Hub.
acquia_contenthub_element_info_alter Implements hook_element_info_alter().
acquia_contenthub_entity_delete Implements hook_entity_delete().
acquia_contenthub_entity_insert Implements hook_entity_insert().
acquia_contenthub_entity_presave Implements hook_entity_presave().
acquia_contenthub_entity_update Implements hook_entity_update().
acquia_contenthub_form_block_content_form_alter Implements hook_form_BASE_FORM_ID_alter().
acquia_contenthub_form_block_content_form_submit Submit handler for the block content form with acquia contenthub options.
acquia_contenthub_form_entity_form_alter Generic alter handler for the entity form. Not natively hooked by naming.
acquia_contenthub_form_entity_form_submit Generic submit handler for the entity form. Not natively hooked by naming.
acquia_contenthub_form_node_form_alter Implements hook_form_BASE_FORM_ID_alter().
acquia_contenthub_form_node_form_submit Submit handler for the node form with acquia contenthub options.
acquia_contenthub_form_node_type_form_alter Implements hook_form_FORM_ID_alter().
acquia_contenthub_form_node_type_form_submit Submit handler for the node type form with acquia contenthub options.
acquia_contenthub_form_taxonomy_term_form_alter Implements hook_form_BASE_FORM_ID_alter().
acquia_contenthub_form_taxonomy_term_form_submit Submit handler for the taxonomy term form with acquia contenthub options.
acquia_contenthub_help Implements hook_help().
acquia_contenthub_module_implements_alter Implements hook_module_implements_alter().
acquia_contenthub_reexport_finished Batch finished function for re-export entities.
acquia_contenthub_theme Implements hook_theme().
acquia_contenthub_theme_suggestions_block_alter Implements hook_theme_suggestions_HOOK_alter().
acquia_contenthub_theme_suggestions_region Implements hook_theme_suggestions_HOOK().