stanford_migrate.module
<?php
/**
* @file
* Contains stanford_migrate.module.
*/
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\migrate\Plugin\MigrateSourceInterface;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Row;
use Drupal\migrate_plus\Entity\Migration;
use Drupal\node\NodeInterface;
use Drupal\ultimate_cron\CronJobInterface;
/**
* Implements hook_help().
*/
function stanford_migrate_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
// Main module help for the stanford_migrate module.
case 'help.page.stanford_migrate':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('Adds more functionality to migrate and migrate plus modules') . '</p>';
return $output;
default:
}
}
/**
* Implements hook_migrate_prepare_row().
*/
function stanford_migrate_migrate_prepare_row(Row $row, MigrateSourceInterface $source, MigrationInterface $migration) {
// Oauth2 authentication adds a token query parameter into the data urls that changes frequently. Since it
// changes, the hash of the row changes without any data actually changing. Fix the row data so that it doesn't
// contain those tokens.
$authentication = $row->getSourceProperty('authentication/plugin');
$current_url = $row->getSourceProperty('current_feed_url');
if ($current_url && $authentication == 'oauth2') {
$row->setSourceProperty('current_feed_url', preg_replace('/access_token=.*?&/', '', $current_url));
}
// In case the list of urls changes dynamically, lets just remove it from the source data to avoid unnecessary hash
// changes.
$row->setSourceProperty('urls', []);
}
/**
* Get the migration that imported the given node.
*
* @param \Drupal\node\NodeInterface $node
* Node entity.
*
* @return array|\Drupal\migrate_plus\Entity\MigrationInterface|mixed
* Migration entity or null/false if none found.
*
* @deprecated in 8.2.3 and is removed in 9.0.0. Use \Drupal::service('stanford_migrate')->getNodesMigration() instead.
*/
function stanford_migrate_get_migration(NodeInterface $node) {
return \Drupal::service('stanford_migrate')->getNodesMigration($node);
}
/**
* Implements hook_entity_form_display_alter().
*/
function stanford_migrate_entity_form_display_alter(EntityFormDisplayInterface $form_display, array $context) {
// We only care about nodes, but this could be expanded later if more entities
// are imported.
$node = \Drupal::routeMatch()->getParameter('node');
if ($context['entity_type'] != 'node' || !$node) {
return;
}
$migration = \Drupal::service('stanford_migrate')->getNodesMigration($node);
// Check if the current node was imported.
if (!$migration) {
return;
}
// Grab the default display settings for use later.
$default_display = EntityViewDisplay::load("node.{$node->bundle()}.default");
$field_definitions = $form_display->get('fieldDefinitions');
foreach ($form_display->getComponents() as $field_name => $component) {
// Make sure the field component is one of the field definitions.
if (empty($field_definitions[$field_name])) {
continue;
}
// When edit an existing node that was imported via migrate module, mark the
// fields that are mapped from migration as readonly.
$field_definition = $field_definitions[$field_name];
$columns = $field_definition->getFieldStorageDefinition()->getColumns();
$processing = !empty($migration->process[$field_name]) || !empty($migration->process["$field_name/0"]);
// This will check if a migrate process is mapped to a specific column on
// the field.
foreach (array_keys($columns) as $column) {
$processing = $processing ?: !empty($migration->process["$field_name/$column"]) || !empty($migration->process["$field_name/0/$column"]);
}
// If the migration destination has the `overwrite_properties` configured,
// those fields specifically should be locked, not the other fields that
// are not designated in the original process configuration.
if ($processing && !empty($migration->get('destination')['overwrite_properties'])) {
// If the current field doesn't exist in the overwrite_properties, it
// should not be considered to be processing since it's a one time only
// import.
$processing = FALSE;
foreach ($migration->get('destination')['overwrite_properties'] as $overwrite_property) {
// If any part of the field is set to overwrite, lock the whole field
// down.
$overwrite_property = strstr($overwrite_property, '/', TRUE) ?: $overwrite_property;
if ($field_name == $overwrite_property) {
$processing = TRUE;
}
}
}
if ($processing) {
\Drupal::messenger()
->addWarning(t('Some fields can not be edited since they contain imported & synced data.'));
// If the default display is configured with some settings, let's use that
// for the best display on the entity form. If it's not configured, the
// readonly_field_widget module will use some default display settings.
if ($display_component = $default_display->getComponent($field_name)) {
$component['settings']['formatter_type'] = $display_component['type'];
$component['settings']['formatter_settings'][$display_component['type']] = $display_component['settings'];
$component['settings']['formatter_third_party_settings'] = $display_component['third_party_settings'] ?? [];
// Add the empty fields module settings to display a message.
$component['settings']['formatter_third_party_settings']['empty_fields']['handler'] = 'text';
$component['settings']['formatter_third_party_settings']['empty_fields']['settings']['empty_text'] = '<em>' . t('No Data') . '</em>';
$component['settings']['formatter_third_party_settings']['stanford_migrate']['readonly'] = TRUE;
}
$component['type'] = 'readonly_field_widget';
$form_display->setComponent($field_name, $component);
}
}
}
/**
* Implements hook_preprocess_HOOK().
*/
function stanford_migrate_preprocess_field(&$variables) {
if ($variables['element']['#third_party_settings']['stanford_migrate']['readonly'] ?? false) {
// Wrap the readonly form fields with classes so that they can be identified
// more easily to the user.
$variables['attributes']['class'][] = 'messages';
$variables['attributes']['class'][] = 'messages--warning';
$variables['attributes']['class'][] = 'messages--readonly';
$variables['#attached']['library'][] = 'stanford_migrate/readonly';
}
}
/**
* Implements hook_config_readonly_whitelist_patterns().
*/
function stanford_migrate_config_readonly_whitelist_patterns() {
$configs = [];
/** @var \Drupal\migrate_plus\Entity\MigrationInterface $migration */
foreach (Migration::loadMultiple() as $migration) {
if ($migration->get('source')['plugin'] == 'csv') {
$configs[] = $migration->getConfigDependencyName();
}
}
return $configs;
}
/**
* Migration callback to just get the current timestamp.
*
* We use this function in migration callback processes because using `time` as
* the callback produces messages about "function accepts 0 arguments, 1
* argument passed". So we just have our own callback that takes the argument
* from the migration process and does nothing with it.
*
* @param mixed $arg
* Passed parameter from migration plugin `callback`.
*
* @return int
* Current timestamp.
*
* @see \Drupal\migrate\Plugin\migrate\process\Callback::transform()
*/
function _stanford_migrate_get_time($arg = NULL) {
return time();
}
/**
* Implements hook_entity_type_alter().
*/
function stanford_migrate_entity_type_alter(array &$entity_types) {
if (\Drupal::moduleHandler()->moduleExists('migrate_source_csv')) {
$entity_types['migration']->setFormClass('csv-upload', 'Drupal\stanford_migrate\Form\StanfordMigrateCsvImportForm');
$entity_types['migration']->setLinkTemplate('csv-upload', '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/csv-upload');
$entity_types['migration']->setLinkTemplate('csv-template', '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/csv-template');
}
}
/**
* Implements hook_ENTITY_TYPE_delete().
*/
function stanford_migrate_migration_delete(Migration $entity) {
// Clean up the state if the migration is deleted.
\Drupal::state()->delete("stanford_migrate.csv.{$entity->id()}");
}
/**
* Implements hook_migrate_id_map_info_alter().
*/
function stanford_migrate_migrate_id_map_info_alter(&$definitions) {
$definitions['sql']['class'] = '\Drupal\stanford_migrate\Plugin\migrate\id_map\StanfordSql';
}
/**
* Implements hook_migrate_source_info_alter().
*/
function stanford_migrate_migrate_source_info_alter(array &$definitions) {
$definitions['url']['class'] = '\Drupal\stanford_migrate\Plugin\migrate\source\StanfordUrl';
}
/**
* Implements hook_data_parser_info_alter().
*/
function stanford_migrate_data_parser_info_alter(array &$definitions) {
$definitions['json']['class'] = '\Drupal\stanford_migrate\Plugin\migrate_plus\data_parser\StanfordJson';
$definitions['simple_xml']['class'] = '\Drupal\stanford_migrate\Plugin\migrate_plus\data_parser\StanfordSimpleXml';
}
/**
* Implements hook_migrate_process_info_alter().
*/
function stanford_migrate_migrate_process_info_alter(array &$definitions) {
if (!empty($definitions['file_import'])) {
$definitions['file_import']['class'] = '\Drupal\stanford_migrate\Plugin\migrate\process\StanfordFileImport';
}
}
/**
* Implements hook_entity_delete().
*
* When an entity is manually deleted from the database, we want to remove it
* from the migration mapping.
*/
function stanford_migrate_entity_delete(EntityInterface $entity) {
\Drupal::service('stanford_migrate')->deleteEntityFromMigration($entity);
}
/**
* Ultimate cron callback function to execute a migration group.
*
* The entity id should be in the form `stanford_migrate_{migrate_group_id}` to
* execute that group.
*
* @param \Drupal\ultimate_cron\CronJobInterface $cron_entity
* Ultimate cron entity.
*/
function stanford_migrate_ultimate_cron_task(CronJobInterface $cron_entity) {
// Invalidate migration plugins to gather any changes to config entities
// before running import. This allows for any changes to the source urls.
\Drupal::service('plugin.manager.migration')->clearCachedDefinitions();
Cache::invalidateTags(['migration_plugins']);
/** @var \Drupal\stanford_migrate\StanfordMigrateInterface $migrate_service */
$migrate_service = \Drupal::service('stanford_migrate');
$migrations = $migrate_service->getMigrationList();
$migration_group = str_replace('stanford_migrate_', '', $cron_entity->id());
// Execute the migration entities in the provided migration group.
if ($migration_group && !empty($migrations[$migration_group])) {
return array_walk($migrations[$migration_group], [
$migrate_service,
'executeMigration',
]);
}
\Drupal::logger('stanford_migrate')
->info('No migration group @group exists. No migration executed.', ['@group' => $migration_group]);
}
/**
* Executes a single migration, taken from drush command in migrate_tools.
*
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
* The migration to execute.
* @param string $migration_id
* The migration ID (not used, just an artifact of array_walk()).
* @param array $options
* Array of options to pass into the migration import.
* @param bool $batch
* Execute the migration using a batch process.
*
* @deprecated in 8.2.3 and is removed in 9.0.0. Use \Drupal::service('stanford_migrate')->executeMigration() instead.
*
* @see \Drupal\migrate_tools\Drush\MigrateToolsCommands::executeMigration()
*/
function stanford_migrate_execute_migration(MigrationInterface $migration, string $migration_id, array $options = [], bool $batch = FALSE): void {
\Drupal::service('stanford_migrate')
->executeMigration($migration, $migration_id, $options, $batch);
}
/**
* Retrieve a list of active migrations, partially taken from migrate_tools.
*
* @return \Drupal\migrate\Plugin\MigrationInterface[][]
* An array keyed by migration group, each value containing an array of
* migrations or an empty array if no migrations match the input criteria.
*
* @deprecated in 8.2.3 and is removed in 9.0.0. Use \Drupal::service('stanford_migrate')->getMigrationList() instead.
*
* @see \Drupal\migrate_tools\Drush\MigrateToolsCommands::migrationsList()
*/
function stanford_migrate_migration_list() {
return \Drupal::service('stanford_migrate')->getMigrationList();
}