Newer
Older
<?php
namespace SGalinski\SgNews\Command;
/***************************************************************
* Copyright notice
*
* (c) sgalinski Internet Services (https://www.sgalinski.de)
*
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
use SGalinski\SgNews\Domain\Model\News;
use SGalinski\SgNews\Domain\Repository\FileReferenceRepository;
use SGalinski\SgNews\Domain\Repository\NewsRepository;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use TYPO3\CMS\Core\Core\Bootstrap;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Core\Resource\FileInterface;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use Symfony\Component\Console\Command\Command;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException;
use TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException;
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
/**
* Command controller, that migrates data from tx_news to sg_news
*/
class MigrateNewsCommandController extends Command {
* this array maps new pages to their original entry in the tx_news table
*
* @var array $newsPagesMap
*/
protected $newsPagesMap = [];
/**
* @var array $languageMap
*/
protected $languageMap = [];

Torsten Oppermann
committed
/**
* @var array $languageMap
*/
protected $categoryMap = [];
* Configure the command
*/
public function configure() {
$this->setDescription('Migrate data from tx_news to sg_news')
->addArgument('copyPageId', InputArgument::REQUIRED, 'The page id of the template that should be copied')
->addArgument('categoryPid', InputArgument::REQUIRED, 'The page id of the category page')
->addArgument('year', InputArgument::OPTIONAL, 'Only news from that year will be migrated', 2015)
->addArgument('languageMapAsJson', InputArgument::OPTIONAL, 'A json, mapping language ids (old => new). this is needed if the sys_language_uids have changed', '{"3":1,"1":0,"2":2,"0":3}')
->addArgument('categoryMapAsJson', InputArgument::OPTIONAL, 'A json, mapping sys_category ids (old => new).', '{"2":17,"3":16,"4":15,"5":14,"6":14,"7":15,"8":16,"9":17}')
->addArgument('pId', InputArgument::OPTIONAL, 'Only news from that pid will be migrated', 52);
}
/**
* Execute the command
* @param InputInterface $input
* @param OutputInterface $output
* @return int|void
* @throws IllegalObjectTypeException
* @throws UnknownObjectException
* @throws \JsonException
* @throws \TYPO3\CMS\Extbase\Object\Exception
public function execute(InputInterface $input, OutputInterface $output) {
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
$persistenceManager = $objectManager->get(PersistenceManager::class);
$fileReferenceRepository = $objectManager->get(FileReferenceRepository::class);
$newsRepository = $objectManager->get(NewsRepository::class);
$copyPageId = $input->getArgument('copyPageId');
$categoryPid = $input->getArgument('categoryPid');
$year = $input->getArgument('year');
$languageMapAsJson = $input->getArgument('languageMapAsJson');
$categoryMapAsJson = $input->getArgument('categoryMapAsJson');
$pId = $input->getArgument('pId');
$this->languageMap = json_decode($languageMapAsJson, TRUE, 512, JSON_THROW_ON_ERROR);
$this->categoryMap = json_decode($categoryMapAsJson, TRUE, 512, JSON_THROW_ON_ERROR);
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_news_domain_model_news');
$queryBuilder->getRestrictions()->removeByType(StartTimeRestriction::class);
$rows = $queryBuilder->select('*')
->from('tx_news_domain_model_news')
->where(
$queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pId, Connection::PARAM_INT))
$localDataHandler = GeneralUtility::makeInstance(DataHandler::class);
Bootstrap::initializeBackendAuthentication();
$localCommandMap = [
'pages' => [
$copyPageId => [
'copy' => (int) $categoryPid
// ignore the entry if its not within the given year
if ((int) date('Y', $row['datetime']) !== $year) {
// if no l10n_parent exists, create a copy of the page
if ((int) $row['l10n_parent'] === 0) {
$localDataHandler->start([], $localCommandMap);
$localDataHandler->bypassAccessCheckForRecords = TRUE;
$localDataHandler->checkModifyAccessList('pages');
$localDataHandler->process_cmdmap();
// get the id of the new object
$newPageId = $localDataHandler->copyMappingArray['pages'][$copyPageId];
// make entry in news map, so we can fetch it later
$this->newsPagesMap[$row['uid']] = $newPageId;
/** @var News $newsPage */
$newsPage = $newsRepository->findByUidIgnoreEnableFields($newPageId);
if ($newsPage !== NULL) {
$title = date('Y-m-d', $row['datetime']) . ' - ' . $row['title'];
$newsPage->setTitle($title);
$newsPage->setSubtitle($row['title']);
$date = new \DateTime('@' . $row['datetime']);
$newsPage->setLastUpdated($date);
$this->setMatchingTag($row);
/** @var File $image */
$file = $this->getMatchingFile($row);
if ($file instanceof File) {
$teaserImage1 = $fileReferenceRepository->addFileReferenceFromFile(
$file, $this->newsPagesMap[$row['uid']],
$this->newsPagesMap[$row['uid']], 'pages', 'tx_sgnews_teaser1_image'
);
if ($teaserImage1) {
$newsPage->addTeaser1Image($teaserImage1);
$teaserImage2 = $fileReferenceRepository->addFileReferenceFromFile(
$file, $this->newsPagesMap[$row['uid']],
$this->newsPagesMap[$row['uid']], 'pages', 'tx_sgnews_teaser2_image'
);
$newsPage->addTeaser2Image($teaserImage2);
}
}
$newsRepository->update($newsPage);
$persistenceManager->persistAll();
// update content element from the new page
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
$queryBuilder->getRestrictions()->removeAll();
$queryBuilder->update('tt_content')
->where(
$queryBuilder->expr()->andX(
$queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($newPageId, Connection::PARAM_INT)),
$queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter($this->languageMap[(int) $row['sys_language_uid']], \PDO::PARAM_INT))
)
)
->set('bodytext', $row['bodytext'], TRUE)
}
} else { // this row is a translation and should simply update the content accordingly
$this->updateTranslation($row);
}
* Get the image file matching the news
*
* @param array $row
* @return null|FileInterface
* @throws \Exception
private function getMatchingFile(array $row) {
// match old page id with old file reference id
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file_reference_news_migration');
$queryBuilder->getRestrictions()->removeAll();
$fileReferenceResult = $queryBuilder->select('uid', 'uid_local')
->from('sys_file_reference_news_migration')
->where(
$queryBuilder->expr()->andX(
$queryBuilder->expr()->eq('tablenames', $queryBuilder->createNamedParameter('tx_news_domain_model_news')),
$queryBuilder->expr()->eq('fieldname', $queryBuilder->createNamedParameter('fal_media')),
$queryBuilder->expr()->eq('uid_foreign', $queryBuilder->createNamedParameter($row['uid'], Connection::PARAM_INT))
return NULL;
}
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file_news_migration');
$queryBuilder->getRestrictions()->removeAll();
$fileResult = $queryBuilder->select('identifier')
->from('sys_file_news_migration')
->where(
$queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($fileReferenceResult['uid_local'], Connection::PARAM_INT))
if (!$fileResult) {
return NULL;
}
$oldIdentifier = $fileResult['identifier'];
$resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
$storage = $resourceFactory->getStorageObject(1);
if (!$storage->hasFile($oldIdentifier)) {
return NULL;
}
return $storage->getFile($oldIdentifier);
}
/**
* Get the tag / category, matching the news
* @param array $row
private function setMatchingTag(array $row) {
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_category_record_mm_news_migration');
$queryBuilder->getRestrictions()->removeAll();
$mmRows = $queryBuilder->select('uid_local', 'sorting_foreign')
->from('sys_category_record_mm_news_migration')
->where(
$queryBuilder->expr()->eq('uid_foreign', $queryBuilder->createNamedParameter($row['uid'], Connection::PARAM_INT))
)
->execute()->fetchAll();
foreach ($mmRows as $mmRow) {
$values = [
'uid_local' => $this->categoryMap[(int) $mmRow['uid_local']],
'uid_foreign' => $this->newsPagesMap[(int) $row['uid']],
'tablenames' => 'pages',
'fieldname' => 'tx_sgnews_tags',
'sorting_foreign' => (int) $mmRow['sorting_foreign']
$queryBuilder->insert('sys_category_record_mm')
->values($values);
/**
* Updates content element of a news translationuid
*
* @param array $row
*/
private function updateTranslation(array $row) {
// get entry in news map
$parentId = $this->newsPagesMap[$row['l10n_parent']];
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
$queryBuilder->getRestrictions()->removeAll();
$originalContentElement = $queryBuilder->select('l18n_parent')
->from('tt_content')
->where(
$queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($parentId, Connection::PARAM_INT))

Torsten Oppermann
committed
// if its the new default, there is no l18n_parent
if ((int) $this->languageMap[(int) $row['sys_language_uid'] === 0]) {
$queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($originalContentElement[0], Connection::PARAM_INT))
$queryBuilder->expr()->eq('l18n_parent', $queryBuilder->createNamedParameter($originalContentElement[0], Connection::PARAM_INT))
// look up the correct language id, if they have changed
if (isset($this->languageMap[(int) $row['sys_language_uid']])) {
$queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter($this->languageMap[(int) $row['sys_language_uid']], Connection::PARAM_INT))
$queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter($row['sys_language_uid'], Connection::PARAM_INT))
$queryBuilder->set('bodytext', $row['bodytext'], TRUE)

Torsten Oppermann
committed
// possibly the default language needs to be overwritten and the old default translation needs to be preserved
if (isset($this->languageMap[(int) $row['sys_language_uid']]) && $this->languageMap[(int) $row['sys_language_uid']] === 0) {
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
$queryBuilder->getRestrictions()->removeAll();
$result = $queryBuilder->select('title', 'subtitle')
->from('pages')
->where(
$queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($parentId, Connection::PARAM_INT))
if ($result) {
$queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter($this->languageMap[0], Connection::PARAM_INT))
$queryBuilder->update('pages')
->andWhere(
$queryBuilder->expr()->eq('l10n_parent', $queryBuilder->createNamedParameter($this->languageMap[0], Connection::PARAM_INT))
);
$queryBuilder->set('title', $result['title'], TRUE)
->set('subtitle', $result['subtitle'], TRUE)
->set('navtitle', '', TRUE)

Torsten Oppermann
committed
$newTitle = date('Y-m-d', $row['datetime']) . ' - ' . $row['title'];
$queryBuilder->update('pages')
->where(
$queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($parentId, Connection::PARAM_INT))
->set('title', $newTitle, TRUE)
->set('subtitle', $row['title'], TRUE)
->set('lastUpdated', $row['datetime'], TRUE)
->set('navtitle', '', TRUE)

Torsten Oppermann
committed
} else {
$queryBuilder->update('pages')
->where(
$queryBuilder->expr()->eq('l10n_parent', $queryBuilder->createNamedParameter($parentId, Connection::PARAM_INT))
);

Torsten Oppermann
committed
// finally translate the page title if necessary
if (isset($this->languageMap[(int) $row['sys_language_uid']]) && $this->languageMap[(int) $row['sys_language_uid']] > 0) {
$queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter($this->languageMap[(int) $row['sys_language_uid']], Connection::PARAM_INT))

Torsten Oppermann
committed
} else {
$queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter($row['sys_language_uid'], Connection::PARAM_INT))

Torsten Oppermann
committed
}
$queryBuilder->set('title', date('Y-m-d', $row['datetime']) . ' - ' . $row['title'], TRUE)

Torsten Oppermann
committed
}