Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
namespace Concrete\Package\MigrationTool;

use Concrete\Core\Asset\AssetList;
use Concrete\Core\Database\Connection\Connection;
use Concrete\Core\Messenger\MessageBusManager;
use Concrete\Core\Package\Package;
use Concrete\Core\Page\Type\Type;
Expand All @@ -28,12 +29,13 @@
use PortlandLabs\Concrete5\MigrationTool\Publisher\ContentImporter\ValueInspector\InspectionRoutine\BatchPageRoutine;
use PortlandLabs\Concrete5\MigrationTool\Publisher\Routine\Manager as PublisherManager;
use SinglePage;
use Throwable;

class Controller extends Package
{
protected $pkgHandle = 'migration_tool';
protected $appVersionRequired = '9.0.0';
protected $pkgVersion = '9.1.0';
protected $pkgVersion = '9.2.0';
protected $pkgAutoloaderMapCoreExtensions = true;
protected $pkgAutoloaderRegistries = array(
'src/PortlandLabs/Concrete5/MigrationTool' => '\PortlandLabs\Concrete5\MigrationTool',
Expand Down Expand Up @@ -282,5 +284,13 @@ public function upgrade()
$pkg = \Package::getByHandle('migration_tool');
$this->installSinglePages($pkg);
$this->installPageTypes($pkg);
try {
$cn = $this->app->make(Connection::class);
// Before version 9.2.0 we didn't have the MigrationImportImportedBlockValues.originalValue field.
// The code rewrites "value" and its original content is already lost: so this query won't fix that,
// but at least we won't break existing installations.
$cn->executeStatement('UPDATE MigrationImportImportedBlockValues SET originalValue = value WHERE originalValue IS NULL');
} catch (Throwable $_) {
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,81 +2,138 @@

namespace PortlandLabs\Concrete5\MigrationTool\Batch\Command;

use PortlandLabs\Concrete5\MigrationTool\Batch\ContentMapper\Item\Item;
use PortlandLabs\Concrete5\MigrationTool\Batch\ContentMapper\PresetManager;
use Doctrine\ORM\EntityManagerInterface;
use Generator;
use PortlandLabs\Concrete5\MigrationTool\Entity\Import\Batch;
use PortlandLabs\Concrete5\MigrationTool\Entity\Import\BlockValue\ImportedBlockValue;

class NormalizePagePathsCommandHandler
{

public function __invoke(NormalizePagePathsCommand $command)
{
// Since batch is serialized we do this:
$em = \Database::connection()->getEntityManager();
$batch = $em->getRepository('PortlandLabs\Concrete5\MigrationTool\Entity\Import\Batch')->findOneById($command->getBatchId());
$em = app(EntityManagerInterface::class);
$batch = $em->find(Batch::class, $command->getBatchId());
$pages = $batch->getPages();
$paths = [];
foreach($pages as $page) {
$paths[] = trim($page->getOriginalPath(), '/');
}
$n = count($paths);
$common = '';
$offset = 1;
if (isset($paths[0]) && $paths[0]) {
while (strpos($paths[0], '/', $offset) !== false) {
$offset = strpos($paths[0], '/', $offset) + 1;
$c = substr($paths[0], 0, $offset);
for ($i = 1; $i < $n; ++$i) {
if (substr($paths[$i], 0, $offset) !== $c) {
break 2;
}
}
$common = $c;
$pagesToNormalize = [];
foreach ($pages as $page) {
if ($batch->isPublishToSitemap() || !$page->canNormalizePath()) {
$page->setBatchPath($page->getOriginalPath());
} else {
$pagesToNormalize[] = $page;
}
}
$map = $this->normalizePagePaths($pagesToNormalize);
$this->applyMapToPageLinks($pages, $batch, $map);
$em->flush();
}

if (count($pages) == 1) {
// Then $common equals the part of the path up to the last slug.
// So if our only page is /path/to/my/page, then $common = '/path/to/my';
$common = substr($pages[0]->getOriginalPath(), 0, strrpos($pages[0]->getOriginalPath(), '/'));
/**
* Set the "Batch Path" of the pages, and returns a map from the "original" page paths and the "batch" paths.
*
* @param \PortlandLabs\Concrete5\MigrationTool\Entity\Import\Page[] $pages
*/
private function normalizePagePaths(array $pagesToNormalize): array
{
$map = [];
$commonPrefix = $this->calculateCommonPathPrefix($pagesToNormalize);
foreach ($pagesToNormalize as $page) {
$originalPath = '/' . ltrim($page->getOriginalPath() ?? '', '/');
$newPath = substr($originalPath, strlen($commonPrefix) - 1);
$page->setBatchPath($newPath);
$map[$originalPath] = $newPath;
}
if ($common) {
$common = '/' . trim($common, '/');
$contentSearchURL = "/\{ccm:export:page:" . preg_quote($common, '/') . "(.*?)\}/i";
$contentReplaceURL = "{ccm:export:page:$1}";
foreach ($pages as $page) {
$originalPath = $page->getOriginalPath();
$newPath = substr($originalPath, strlen($common));

if ($page->canNormalizePath()) {
$page->setBatchPath($newPath);
}
return $map;
}

$areas = $page->getAreas();
foreach ($areas as $area) {
$blocks = $area->getBlocks();
foreach ($blocks as $block) {
$value = $block->getBlockValue();
if ($value instanceof ImportedBlockValue) {
$content = preg_replace($contentSearchURL, $contentReplaceURL, $value->getValue());
// doctrine is doing some dumb shit here so let's do a direct query
$db = $em->getConnection();
$db->executeQuery('update MigrationImportImportedBlockValues set value = ? where id = ?',
array($content, $value->getID())
);
}
/**
* Calculate the common prefix of page paths.
*
* @param \PortlandLabs\Concrete5\MigrationTool\Entity\Import\Page[] $pages
*
* @example for 0 pages: you'll get '/'
* @example for 1 page at '/path/to/my/page': you'll get '/path/to/my/'
* @example for 2 pages at '/path/to/my/page' and '/path/to/another/page': you'll get '/path/to/'
* @example for 2 pages at '/first/page' and '/second/page': you'll get '/'
*/
private function calculateCommonPathPrefix(array $pages): string
{
$commonSlugs = null;
foreach ($pages as $page) {
$pageSlugs = preg_split('{/}', $page->getOriginalPath() ?? '', -1, PREG_SPLIT_NO_EMPTY);
array_pop($pageSlugs);
if ($commonSlugs === null) {
$commonSlugs = $pageSlugs;
} else {
$newCommonSlugs = [];
foreach ($commonSlugs as $index => $slug) {
if (!isset($pageSlugs[$index]) || $pageSlugs[$index] !== $slug) {
break;
}
$newCommonSlugs[] = $slug;
}
$commonSlugs = $newCommonSlugs;
}
} else {
foreach ($pages as $page) {
if ($page->canNormalizePath()) {
$page->setBatchPath($page->getOriginalPath());
}
if ($commonSlugs === []) {
break;
}
}
$em->flush();
if ($commonSlugs === null || $commonSlugs === []) {
return '/';
}

return '/' . implode('/', $commonSlugs) . '/';
}

/**
* Update the '{ccm:export:page:...}` placeholders of the blocks.
*
* @param \PortlandLabs\Concrete5\MigrationTool\Entity\Import\Page[] $pages
* @param array $map the map from the "original" page paths and the "batch" paths.
*/
private function applyMapToPageLinks(array $pages, Batch $batch, array $map): void
{
$pathPrefix = $batch->isPublishToSitemap() ? '' : "/!import_batches/{$batch->getID()}";
foreach ($this->listImportedBlockValues($pages) as $importedBlockValue) {
$value = $importedBlockValue->getOriginalValue();
if ($value) {
$value = preg_replace_callback(
'/\{ccm:export:page:(?<path>.*?)\}/',
static function (array $matches) use (&$map, $pathPrefix): string {
$path = '/' . ltrim($matches['path'], '/');
if (isset($map[$path])) {
$path = $pathPrefix . $map[$path];
}
if ($path === '/') {
$path = '';
}
return "{ccm:export:page:{$path}}";
},
$value
);
}
$importedBlockValue->setValue($value);
}
}

}
/**
* List all the ImportedBlockValue instances owned by pages.
*
* @param \PortlandLabs\Concrete5\MigrationTool\Entity\Import\Page[] $pages
*
* @return \PortlandLabs\Concrete5\MigrationTool\Entity\Import\BlockValue\ImportedBlockValue[]
*/
private function listImportedBlockValues(array $pages): Generator
{
foreach ($pages as $page) {
foreach ($page->getAreas() as $area) {
foreach ($area->getBlocks() as $block) {
$value = $block->getBlockValue();
if ($value instanceof ImportedBlockValue) {
yield $value;
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public function jsonSerialize()
}

$node->id = $page->getId();
$node->pagePath = '/' . $page->getBatchPath();
$node->pagePath = '/' . ltrim($page->getBatchPath() ?? '', '/');
$node->pageType = $page->getType();
$node->pageTemplate = $page->getTemplate();
if (!$skipItem) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ public function itemExists(ItemInterface $item, Batch $batch)
if (is_object($item->getContentObject())) {
return true;
}

$itemPath = $this->normalizePath($item->getReference());
foreach ($batch->getPages() as $page) {
if ($page->getBatchPath() == $item->getReference()) {
if ($this->normalizePath($page->getBatchPath()) === $itemPath) {
return true;
}
if ($page->getOriginalPath() == $item->getReference()) {
if ($this->normalizePath($page->getOriginalPath()) === $itemPath) {
return true;
}
}
Expand All @@ -32,4 +32,9 @@ public function addMissingItemMessage(ItemInterface $item, MessageCollection $me
new Message(t('Referenced page at path %s cannot be found in the site or in the current content batch.', $item->getReference()), Message::E_WARNING)
);
}

private function normalizePath(?string $path): string
{
return '/' . ltrim($path ?? '', '/');
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php
namespace PortlandLabs\Concrete5\MigrationTool\Entity\Import\BlockValue;
use Doctrine\ORM\Mapping as ORM;

use Doctrine\ORM\Mapping as ORM;
use PortlandLabs\Concrete5\MigrationTool\Batch\Formatter\Block\ImportedFormatter;
use PortlandLabs\Concrete5\MigrationTool\Inspector\Block\CIFInspector;
use PortlandLabs\Concrete5\MigrationTool\Publisher\Block\CIFPublisher;
Expand All @@ -12,22 +12,44 @@
*/
class ImportedBlockValue extends BlockValue
{
/**
* @ORM\Column(type="text", nullable=true)
*/
protected $originalValue;

/**
* @ORM\Column(type="text", nullable=true)
*/
protected $value;

public function getValue()
public function getOriginalValue(): ?string
{
return $this->originalValue;
}

/**
* @return $this
*/
public function setOriginalValue(?string $value): self
{
$this->originalValue = $value;

return $this;
}

public function getValue(): ?string
{
return $this->value;
}

/**
* @param mixed $value
* @return $this
*/
public function setValue($value)
public function setValue(?string $value): self
{
$this->value = $value;

return $this;
}

public function getFormatter()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ class Importer extends AbstractImporter
public function parse(\SimpleXMLElement $node)
{
$value = new ImportedBlockValue();
$value->setValue((string) $node->asXML());
$xml = (string) $node->asXML();

return $value;
return $value
->setOriginalValue($xml)
->setValue($xml)
;
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
<?php
namespace PortlandLabs\Concrete5\MigrationTool\Publisher\Block;

use Concrete\Core\Error\UserMessageException;
use Concrete\Core\Page\Page;
use PortlandLabs\Concrete5\MigrationTool\Entity\Import\Batch;
use PortlandLabs\Concrete5\MigrationTool\Entity\Import\BlockValue\BlockValue;
use Concrete\Core\Page\Page;
use SimpleXMLElement;

defined('C5_EXECUTE') or die("Access Denied.");

Expand All @@ -12,7 +14,19 @@ class CIFPublisher implements PublisherInterface
public function publish(Batch $batch, $bt, Page $page, $area, BlockValue $value)
{
$btc = $bt->getController();
$bx = simplexml_load_string($value->getValue());
$xml = $value->getValue();
$bx = simplexml_load_string($xml);
if (!$bx instanceof SimpleXMLElement) {
$message = t('Invalid XML found:') . "\n";
if (!is_string($xml)) {
$message .= t('Expected a string, got %s', gettype($xml));
} elseif ($xml === '') {
$message .= t('Empty string');
} else {
$message .= $xml;
}
throw new UserMessageException($message);
}

return $btc->import($page, $area, $bx);
}
Expand Down
Loading
Loading