View source
<?php
namespace Drupal\cms_content_sync\Controller;
use Drupal\cms_content_sync\Entity\Flow;
use Drupal\cms_content_sync\Entity\Pool;
use Drupal\cms_content_sync\PullIntent;
use Drupal\cms_content_sync\PushIntent;
use Drupal\cms_content_sync\SyncCoreFlowExport;
use Drupal\cms_content_sync\SyncCoreInterface\SyncCoreFactory;
use Drupal\cms_content_sync\SyncCorePoolExport;
use Drupal\Core\Controller\ControllerBase;
use EdgeBox\SyncCore\V2\Raw\Model\FlowSyndicationMode;
class Migration extends ControllerBase {
public const STEP_EXPORT_POOLS = 'export-pools';
public const STEP_EXPORT_FLOWS = 'export-flows';
public const STEP_TEST_MANUAL_PUSH = 'push-content-manually';
public const STEP_TEST_AUTOMATED_PULL = 'pull-content-automatically';
public const STEP_TEST_MANUAL_PULL = 'pull-content-manually';
public const STEP_SWITCH = 'switch';
public const STEP_DONE = 'done';
public static $pool_statuses = null;
protected static $flow_statuses = [];
public static function useV2($set_runtime = null) {
static $value = null;
if (null !== $set_runtime) {
$value = $set_runtime;
SyncCoreFactory::clearCache();
foreach (Pool::getAll() as $pool) {
$pool
->getClient(true);
}
}
if (null === $value) {
$value = self::alwaysUseV2();
}
return $value;
}
public static function alwaysUseV2() {
static $value = null;
if (null === $value) {
$active_step = self::getActiveStep(true);
$value = self::STEP_DONE === $active_step;
}
return $value;
}
public static function didMigrate() {
return self::alwaysUseV2() && !empty(\Drupal::service('config.factory')
->get('cms_content_sync.migration')
->get('cms_content_sync_v2_pool_statuses'));
}
public static function getPoolStatus($pool) {
if (!self::$pool_statuses) {
self::$pool_statuses = \Drupal::service('config.factory')
->get('cms_content_sync.migration')
->get('cms_content_sync_v2_pool_statuses');
if (null === self::$pool_statuses) {
self::$pool_statuses = [];
}
}
if (!isset(self::$pool_statuses[$pool->id])) {
if (!$pool->backend_url) {
return Pool::V2_STATUS_ACTIVE;
}
return Pool::V2_STATUS_NONE;
}
return self::$pool_statuses[$pool->id];
}
public static function entityUsedV2(string $flow, string $type, string $bundle, ?string $uuid, ?string $shared_id, bool $pushed) {
$statuses = self::getStoredFlowStatus($flow);
foreach ($statuses['types'] as &$status) {
if ($status['namespaceMachineName'] === $type && $status['machineName'] === $bundle) {
$status[$pushed ? 'pushedEntity' : 'pulledEntity'] = [
'remoteUuid' => $uuid,
'remoteUniqueId' => $shared_id,
'verified' => false,
];
break;
}
}
self::setFlowStatus($flow, $statuses);
}
public static function getFullFlowStatus($flow) {
$status = self::getFlowStatus($flow);
if (Flow::V2_STATUS_EXPORTED === $status) {
return self::getStoredFlowStatus($flow->id);
}
return [
'exported' => Flow::V2_STATUS_ACTIVE === $status,
'active' => Flow::V2_STATUS_ACTIVE === $status,
'skipTest' => false,
'skipPush' => false,
'skipPull' => false,
'types' => [],
];
}
public static function getFlowStatus($flow) {
$status = self::getStoredFlowStatus($flow->id);
if (empty($status) || !isset($status['active'])) {
foreach ($flow
->getController()
->getUsedPools() as $pool) {
if ((bool) $pool->backend_url) {
return Flow::V2_STATUS_NONE;
}
}
return Flow::V2_STATUS_ACTIVE;
}
if ($status['active']) {
return Flow::V2_STATUS_ACTIVE;
}
return Flow::V2_STATUS_EXPORTED;
}
public static function getActiveStep($avoid_recursion = false) {
$pools = Pool::getAll();
foreach ($pools as $pool) {
if (!$pool
->v2Ready()) {
return self::STEP_EXPORT_POOLS;
}
}
$flows = Flow::getAll();
foreach ($flows as $flow) {
if (!$flow
->v2Ready()) {
return self::STEP_EXPORT_FLOWS;
}
}
if ($avoid_recursion) {
foreach ($pools as $pool) {
if ($pool->backend_url) {
return self::STEP_SWITCH;
}
}
return self::STEP_DONE;
}
foreach ($pools as $pool) {
if (!$pool
->useV2()) {
return self::STEP_SWITCH;
}
}
return self::STEP_DONE;
}
public static function runPoolExport($machine_names = null) {
self::useV2(true);
foreach (Pool::getAll() as $pool) {
if ($machine_names ? in_array($pool
->id(), $machine_names) : !$pool
->v2Ready()) {
$exporter = new SyncCorePoolExport($pool);
$batch = $exporter
->prepareBatch(true);
$batch
->executeAll();
self::setPoolV2Ready($pool);
}
}
\Drupal::messenger()
->addMessage(t('Successfully exported your Pools to the new Sync Core v2.'));
}
public static function runFlowExport($machine_names = null) {
self::useV2(true);
foreach (Flow::getAll() as $flow) {
if ($machine_names ? in_array($flow
->id(), $machine_names) : !$flow
->v2Ready()) {
$flow
->getController()
->updateEntityTypeVersions();
$exporter = new SyncCoreFlowExport($flow, true);
$batch = $exporter
->prepareBatch();
$batch
->executeAll();
self::setFlowV2Ready($flow);
}
}
\Drupal::messenger()
->addMessage(t('Successfully exported your Flows to the new Sync Core v2.'));
}
public static function skipFlowsTest($machine_names) {
self::useV2(true);
foreach (Flow::getAll() as $flow) {
if (in_array($flow
->id(), $machine_names)) {
$status = self::getStoredFlowStatus($flow
->id());
$status['skipTest'] = true;
self::setFlowStatus($flow
->id(), $status);
}
}
\Drupal::messenger()
->addMessage(t('Successfully skipped testing your Flow(s).'));
}
public static function skipFlowsPush($machine_names) {
self::useV2(true);
foreach (Flow::getAll() as $flow) {
if (in_array($flow
->id(), $machine_names)) {
$status = self::getStoredFlowStatus($flow
->id());
$status['skipPush'] = true;
self::setFlowStatus($flow
->id(), $status);
}
}
\Drupal::messenger()
->addMessage(t('Successfully skipped pushing your Flow(s).'));
}
public static function skipFlowsPull($machine_names) {
self::useV2(true);
foreach (Flow::getAll() as $flow) {
if (in_array($flow
->id(), $machine_names)) {
$status = self::getStoredFlowStatus($flow
->id());
$status['skipPull'] = true;
self::setFlowStatus($flow
->id(), $status);
}
}
\Drupal::messenger()
->addMessage(t('Successfully skipped mapping your Flow(s).'));
}
public static function runSwitch() {
self::useV2(true);
foreach (Pool::getAll() as $pool) {
self::setPoolV2Ready($pool, true);
}
\Drupal::messenger()
->addMessage(t('Successfully switched to always use the new Sync Core v2. Congratulations!'));
}
public static function runActiveStep() {
$step = self::getActiveStep();
if (self::STEP_EXPORT_POOLS === $step) {
self::runPoolExport();
}
elseif (self::STEP_EXPORT_FLOWS === $step) {
self::runFlowExport();
}
elseif (self::STEP_SWITCH === $step) {
self::runSwitch();
}
else {
throw new \Exception("This migration step can't be automated.");
}
}
public static function getSteps() {
return [
self::STEP_EXPORT_POOLS => t('Export pools'),
self::STEP_EXPORT_FLOWS => t('Export flows'),
self::STEP_SWITCH => t('Switch over'),
self::STEP_DONE => t('Done'),
];
}
public static function getStepDescriptions() {
return [
self::STEP_EXPORT_POOLS => [
'status' => t('All pool configuration will be exported to the new Sync Core. You can still syndicate content using the old Sync Core.'),
],
self::STEP_EXPORT_FLOWS => [
'status' => t('All flow configuration will be exported to the new Sync Core. You can still syndicate content using the old Sync Core.'),
],
self::STEP_TEST_MANUAL_PUSH => [
'status' => t("Please install the 'Content Sync Health' submodule, then go to Content > Sync health > Entity status. Select one entity per Flow and use the 'Migrate to V2' action. You can still syndicate content using the old Sync Core."),
'warning' => t('This will push the content and trigger updates on remote sites as usual.'),
],
self::STEP_TEST_AUTOMATED_PULL => [
'status' => t("Please migrate your other sites first; make sure to use the 'Migrate to V2' action on an entity that is configured with 'Pull: All' on this site. You can still syndicate content using the old Sync Core."),
'warning' => t('This will make changes on the content you pull.'),
],
self::STEP_TEST_MANUAL_PULL => [
'status' => t("Please migrate your other sites first; make sure to use the 'Migrate to V2' action on an entity that is configured with 'Pull: Manually' on this site. You can still syndicate content using the old Sync Core."),
'warning' => t('This will make changes on the content you pull.'),
],
self::STEP_SWITCH => [
'status' => t('Switch over'),
'warning' => t("This action can't be undone. The old Sync Core will no longer have access to this site and all future push and pull operations to/from this site will be done by the new Sync Core."),
],
self::STEP_DONE => [
'status' => t('Congratulations! You have finished migrating your site to v2. Enjoy our new features and let us know if anything is missing!'),
],
];
}
public static function getAutomatedSteps() {
return [
self::STEP_EXPORT_POOLS,
self::STEP_EXPORT_FLOWS,
self::STEP_SWITCH,
];
}
protected static function setPoolV2Ready($pool, $switch = false) {
$settings = \Drupal::service('config.factory')
->getEditable('cms_content_sync.migration');
self::$pool_statuses = $settings
->get('cms_content_sync_v2_pool_statuses');
if (!self::$pool_statuses) {
self::$pool_statuses = [];
}
if ($switch) {
self::$pool_statuses[$pool->id] = Pool::V2_STATUS_ACTIVE;
unset($pool->backend_url);
$pool
->save();
}
else {
self::$pool_statuses[$pool->id] = Pool::V2_STATUS_EXPORTED;
}
$settings
->set('cms_content_sync_v2_pool_statuses', self::$pool_statuses)
->save();
}
protected static function getStoredFlowStatus($id) {
if (!empty(self::$flow_statuses[$id])) {
return self::$flow_statuses[$id];
}
$status = \Drupal::service('config.factory')
->get('cms_content_sync.migration')
->get('flow.' . $id);
if ($status) {
self::$flow_statuses[$id] = $status;
}
return $status;
}
protected static function setFlowStatus($id, $status) {
\Drupal::service('config.factory')
->getEditable('cms_content_sync.migration')
->set('flow.' . $id, $status)
->save();
self::$flow_statuses[$id] = $status;
}
protected static function setFlowV2Ready($flow) {
$previous = self::getStoredFlowStatus($flow
->id()) ?? [];
$all_types = $flow
->getController()
->getEntityTypeConfig(null, null, true);
$types = [];
foreach ($all_types as $type => $bundles) {
foreach ($bundles as $bundle => $config) {
$version = $config['version'];
$existing = [];
if (isset($previous['types'])) {
foreach ($previous['types'] as $existing_type) {
if ($existing_type['namespaceMachineName'] === $type && $existing_type['machineName'] === $bundle) {
$existing = $existing_type;
break;
}
}
}
$types[] = [
'namespaceMachineName' => $type,
'machineName' => $bundle,
'versionId' => $version,
'pushMode' => PushIntent::PUSH_AUTOMATICALLY === $config['export'] ? FlowSyndicationMode::ALL : (PushIntent::PUSH_MANUALLY === $config['export'] ? FlowSyndicationMode::MANUALLY : (PushIntent::PUSH_AS_DEPENDENCY === $config['export'] ? FlowSyndicationMode::DEPENDENT : null)),
'pullMode' => PullIntent::PULL_AUTOMATICALLY === $config['import'] ? FlowSyndicationMode::ALL : (PullIntent::PULL_MANUALLY === $config['import'] ? FlowSyndicationMode::MANUALLY : (PullIntent::PULL_AS_DEPENDENCY === $config['import'] ? FlowSyndicationMode::DEPENDENT : null)),
'pushedEntity' => isset($existing['pushedEntity']) ? $existing['pushedEntity'] : null,
'pulledEntity' => isset($existing['pulledEntity']) ? $existing['pulledEntity'] : null,
'skipTest' => !empty($existing['skipTest']) ? $existing['skipTest'] : false,
'skipPush' => !empty($existing['skipPush']) ? $existing['skipPush'] : false,
'skipPull' => !empty($existing['skipPull']) ? $existing['skipPull'] : false,
];
}
}
self::setFlowStatus($flow->id, [
'exported' => true,
'active' => false,
'skipTest' => !empty($previous['skipTest']) ? $previous['skipTest'] : false,
'skipPush' => !empty($previous['skipPush']) ? $previous['skipPush'] : false,
'skipPull' => !empty($previous['skipPull']) ? $previous['skipPull'] : false,
'types' => $types,
]);
}
}