Skip to content

Commit

Permalink
8.1.21
Browse files Browse the repository at this point in the history
- Fixed the orphan action to compare string and interger values
- D8CORE-5193 Add "Forget" orphan action (#47)
- Migrate process plugin to adjust date values to 11:59pm (#46)
- Deny field access to imported fields on nodes (#44)
  • Loading branch information
pookmish committed Jan 28, 2022
2 parents 39d9c59 + 7db6ee8 commit acd6ea6
Show file tree
Hide file tree
Showing 8 changed files with 344 additions and 27 deletions.
18 changes: 0 additions & 18 deletions .circleci/config.yml
Expand Up @@ -47,21 +47,6 @@ back_to_dev: &back_to_dev
composer global require SU-SWS/stanford-caravan:dev-8.x-2.x
~/.composer/vendor/bin/sws-caravan back-to-dev ${CIRCLE_TAG} ${CIRCLE_WORKING_DIRECTORY}
d8_codeception: &d8_codeception
<<: *defaults
steps:
- checkout:
path: /var/www/test
- run:
name: Run Codeception Tests
command: |
composer global require SU-SWS/stanford-caravan:dev-8.x-1.x
~/.composer/vendor/bin/sws-caravan codeception /var/www/html --extension-dir=/var/www/test
- store_test_results:
path: /var/www/html/artifacts/behat
- store_artifacts:
path: /var/www/html/artifacts

d9_codeception: &d9_codeception
<<: *defaults
steps:
Expand All @@ -83,8 +68,6 @@ jobs:
<<: *code_coverage
run-back-to-dev:
<<: *back_to_dev
run-d8-codeception:
<<: *d8_codeception
run-d9-codeception:
<<: *d9_codeception

Expand All @@ -104,7 +87,6 @@ workflows:
tests:
jobs:
- run-coverage
- run-d8-codeception
- run-d9-codeception
# Re-test every sunday in case this code becomes stale.
sundays:
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
@@ -1,6 +1,16 @@
# Stanford Migrate


8.x-1.21
--------------------------------------------------------------------------------
_Release Date: 2022-01-27_

- Fixed the orphan action to compare string and interger values
- D8CORE-5193 Add "Forget" orphan action (#47)
- Migrate process plugin to adjust date values to 11:59pm (#46)
- Deny field access to imported fields on nodes (#44)


8.x-1.20
--------------------------------------------------------------------------------
_Release Date: 2022-01-04_
Expand Down
34 changes: 32 additions & 2 deletions src/EventSubscriber/EventsSubscriber.php
Expand Up @@ -7,7 +7,9 @@
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\migrate\Event\MigrateEvents;
use Drupal\migrate\Event\MigrateImportEvent;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Row;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;

Expand All @@ -28,6 +30,11 @@ class EventsSubscriber implements EventSubscriberInterface {
*/
const ORPHAN_UNPUBLISH = 'unpublish';

/**
* If the migration is configured to unpublish orphans.
*/
const ORPHAN_FORGET = 'forget';

/**
* Drupal\Core\Entity\EntityTypeManagerInterface definition.
*
Expand Down Expand Up @@ -118,11 +125,23 @@ public function postImport(MigrateImportEvent $event) {
$id_exists_in_source = FALSE;
// Source key array of the already imported item.
$source_id = $id_map->currentSource();
$row = $id_map->getRowBySource($source_id);

// The current item is already ignored, lets move on to the next one. This
// is skipped if the migration is ran with `--update` or via the UI with
// the "Update" checkbox checked.
if ($row['source_row_status'] == MigrateIdMapInterface::STATUS_IGNORED) {
$id_map->next();
continue;
}

// Look through the current source to see if we can find a match to the
// existing item.
foreach ($current_source_ids as $key => $ids) {
if ($ids == $source_id) {
if (
(is_array($ids) && is_array($source_id) && empty(array_diff($ids, $source_id))) ||
$ids == $source_id
) {
// The existing item is in the source, flag it as found and we can
// reduce the current source ids to make subsequent lookups faster.
unset($current_source_ids[$key]);
Expand Down Expand Up @@ -169,6 +188,17 @@ public function postImport(MigrateImportEvent $event) {
$id_map->delete($id_map->currentSource());
break;

// Tell the migration to ignore the given source ids.
case self::ORPHAN_FORGET:
$this->logger->notice($this->t('Entity since it no longer exists in the source data, it will be now be ignored. Migration: @migration, Entity Type: @entity_type, Label: @label'), [
'@migration' => $event->getMigration()->label(),
'@entity_type' => $type,
'@label' => $entity->label(),
]);
$old_row = new Row($id_map->currentSource(), $id_map->currentSource(), TRUE);
$id_map->saveIdMapping($old_row, [], MigrateIdMapInterface::STATUS_IGNORED);
break;

case self::ORPHAN_UNPUBLISH:
// Unpublish the orphan only if it is currently published.
if (
Expand Down Expand Up @@ -212,7 +242,7 @@ protected function getOrphanAction(MigrationInterface $migration) {
// temporary flag that the orphan action has recently occurred. This
// will prevent the unnecessary double execution.
if ($this->cache->get($cid)) {
return FALSE;
// return FALSE;
}

$source_config = $migration->getSourceConfiguration();
Expand Down
66 changes: 66 additions & 0 deletions src/Plugin/migrate/process/SmartDateAllDayAdjust.php
@@ -0,0 +1,66 @@
<?php

namespace Drupal\stanford_migrate\Plugin\migrate\process;

use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;

/**
* Adjust an ending timestamp to work with the "All Day" for smart date module.
*
* @code
* process:
* field_date/end_value:
* plugin: datetime_adjust
* source: end_value
* start_time: start_value
* @endcode
*
* @MigrateProcessPlugin(
* id = "smartdate_adjust"
* )
*/
class SmartDateAllDayAdjust extends ProcessPluginBase {

/**
* {@inheritdoc}
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
if (empty($this->configuration['start_time'])) {
return $value;
}
$start = $row->get($this->configuration['start_time']);
if (empty($start)) {
return $value;
}

if (!is_numeric($start)) {
$start = strtotime($start);
}

if (!is_numeric($value)) {
$value = strtotime($value);
}

// If the start and end values are the same & the start is at 12:00 midnight
// then increase the end value to be at the end of the day for the smart
// date module to work correctly.
if ((int) date('Gi', $start) == 0 && $start == $value) {
return $value + (60 * 60 * 24) - 60;
}

// If either the start or the end date value are not at midnight, don't
// adjust the end value at all since it won't be considered an "All Day"
// date time.
if ((int) date('Gi', $start) || (int) date('Gi', $value)) {
return $value;
}

// Now that the start and the end must both be at midnight and different
// days, reduce the end value by 1 minute to work correctly with the smart
// date module.
return $value - 60;
}

}
2 changes: 1 addition & 1 deletion stanford_migrate.info.yml
Expand Up @@ -3,7 +3,7 @@ description: 'Adds more functionality to migrate and migrate plus modules'
type: module
core_version_requirement: ^8.8 || ^9
package: 'Stanford'
version: 8.x-1.20
version: 8.x-1.21
dependencies:
- drupal:migrate
- migrate_plus:migrate_plus
Expand Down
113 changes: 113 additions & 0 deletions stanford_migrate.module
Expand Up @@ -5,16 +5,22 @@
* Contains stanford_migrate.module.
*/

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Installer\InstallerKernel;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\migrate\Exception\RequirementsException;
use Drupal\migrate\MigrateMessage;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Plugin\RequirementsInterface;
use Drupal\migrate_plus\Entity\Migration;
use Drupal\migrate_tools\MigrateExecutable;
use Drupal\node\NodeInterface;
use Drupal\ultimate_cron\CronJobInterface;

/**
Expand All @@ -33,6 +39,113 @@ function stanford_migrate_help($route_name, RouteMatchInterface $route_match) {
}
}

/**
* 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.
*/
function stanford_migrate_get_migration(NodeInterface $node) {
// Use a static variable so that it doesn't look up the migrations multiple
// times.
$node_migration = &drupal_static(__FUNCTION__ . $node->id());
if (!is_null($node_migration)) {
return $node_migration;
}
// Set the static to false and check for null above. If the first attempt to
// find a migration doesn't show anything, we don't want to continue checking.
$node_migration = FALSE;

$plugin_manager = \Drupal::service('plugin.manager.migration');

// Loop through the migration entities, build their migration plugins so that
// we can dig into their source mapping data.
/** @var \Drupal\migrate_plus\Entity\MigrationInterface $migration */
foreach (Migration::loadMultiple() as $migration) {

// This migrate entity has methods that allow easy queries on the
// migrate_map tables.
/** @var \Drupal\migrate\Plugin\MigrationInterface $migrate */
$migrate = $plugin_manager->createInstance($migration->id());

// CSV Imported content can be ignored since it's normally a one time thing.
if ($migrate->getSourcePlugin()->getPluginId() == 'csv') {
continue;
}

$destination_ids = $migrate->getDestinationPlugin()->getIds();

// Ignore any migrate plugin that doesn't map to nodes.
if (isset($destination_ids['nid'])) {

// If the migrate id map returns something, that means this node is tied
// to this migration. Set the static variable for later references and
// get out of here.
$row_data = $migrate->getIdMap()->getRowByDestination(['nid' => $node->id()]);
if (!empty($row_data) && $row_data['source_row_status'] != MigrateIdMapInterface::STATUS_IGNORED) {
$node_migration = $migration;
return $migration;
}
}
}

}

/**
* Implements hook_entity_field_access().
*/
function stanford_migrate_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
$route_match = \Drupal::routeMatch();
// When edit an existing node that was imported via migrate module, mark the
// fields that are mapped from migration as forbidden.
if (
$operation == 'edit' &&
$route_match->getRouteName() == 'entity.node.edit_form' &&
$migration = stanford_migrate_get_migration($route_match->getParameter('node'))
) {
$field_name = $field_definition->getName();
$columns = $field_definition->getFieldStorageDefinition()->getColumns();
$processing = !empty($migration->process[$field_name]);

// 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"]);
}

// 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. They are not visible here.'));

return AccessResult::forbidden((string) t('Field is mapped by the importer'));
}
}

return AccessResult::neutral();
}

/**
* Implements hook_config_readonly_whitelist_patterns().
*/
Expand Down

0 comments on commit acd6ea6

Please sign in to comment.