Skip to content
Snippets Groups Projects
MigrateNewsCommandController.php 14.3 KiB
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 = [];

	/**
	 * @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))
			)
			->execute()->fetchAll();
		$localDataHandler = GeneralUtility::makeInstance(DataHandler::class);
		Bootstrap::initializeBackendAuthentication();
		$localCommandMap = [
			'pages' => [
				$copyPageId => [
					'copy' => (int) $categoryPid
		foreach ($rows as $row) {
			// 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;

				$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);
					/** @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)
						->execute();
				}
			} else { // this row is a translation and should simply update the content accordingly
				$this->updateTranslation($row);
			}
	 * Get the image file matching the news
	 *
	 * @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))
				)
			)
			->execute()->fetch();
		if (!$fileReferenceResult) {
		$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))
			)
			->execute()->fetch();
		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
	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) {
				'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))
			)
			->execute()->fetch();
		$queryBuilder->update('tt_content');
		// if its the new default, there is no l18n_parent
		if ((int) $this->languageMap[(int) $row['sys_language_uid'] === 0]) {
			$queryBuilder->where(
				$queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($originalContentElement[0], Connection::PARAM_INT))
			$queryBuilder->where(
				$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->andWhere(
				$queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter($this->languageMap[(int) $row['sys_language_uid']], Connection::PARAM_INT))
			$queryBuilder->andWhere(
				$queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter($row['sys_language_uid'], Connection::PARAM_INT))
		$queryBuilder->set('bodytext', $row['bodytext'], TRUE)
			->execute();

		// 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))
				)
				->execute()->fetch();
				$queryBuilder->where(
					$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)
					->execute();
			$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)
				->execute();
			$queryBuilder->update('pages')
				->where(
					$queryBuilder->expr()->eq('l10n_parent', $queryBuilder->createNamedParameter($parentId, Connection::PARAM_INT))
				);
			// 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->andWhere(
					$queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter($this->languageMap[(int) $row['sys_language_uid']], Connection::PARAM_INT))
				$queryBuilder->andWhere(
					$queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter($row['sys_language_uid'], Connection::PARAM_INT))
			$queryBuilder->set('title', date('Y-m-d', $row['datetime']) . ' - ' . $row['title'], TRUE)
				->execute();