Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save derhansen/4524495ccfef9335c96d6d535bad7324 to your computer and use it in GitHub Desktop.
Save derhansen/4524495ccfef9335c96d6d535bad7324 to your computer and use it in GitHub Desktop.
SwitchableControllerActionsPluginUpdater for TYPO3 extension "Plain FAQ"
<?php
declare(strict_types=1);
/*
* This file is part of the Extension "plain_faq" for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
namespace Derhansen\PlainFaq\Updates;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\Service\FlexFormService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Install\Updates\DatabaseUpdatedPrerequisite;
use TYPO3\CMS\Install\Updates\UpgradeWizardInterface;
class SwitchableControllerActionsPluginUpdater implements UpgradeWizardInterface
{
private const MIGRATION_SETTINGS = [
[
'sourceListType' => 'plainfaq_pi1',
'switchableControllerActions' => 'Faq->list;Faq->detail',
'targetListType' => 'plainfaq_pilistdetail'
],
[
'sourceListType' => 'plainfaq_pi1',
'switchableControllerActions' => 'Faq->list',
'targetListType' => 'plainfaq_pilist'
],
[
'sourceListType' => 'plainfaq_pi1',
'switchableControllerActions' => 'Faq->detail',
'targetListType' => 'plainfaq_pidetail'
],
];
protected FlexFormService $flexFormService;
public function __construct()
{
$this->flexFormService = GeneralUtility::makeInstance(FlexFormService::class);
}
public function getIdentifier(): string
{
return 'switchableControllerActionsPluginUpdater';
}
public function getTitle(): string
{
return 'Migrates plugin and settings of existing FAQ plugins using switchableControllerActions';
}
public function getDescription(): string
{
$description = 'The old FAQ plugin using switchableControllerActions has been split into 3 separate plugins. ';
$description .= 'This update wizard migrates all existing plugin settings and changes the Plugin (list_type) ';
$description .= 'to use the new plugins available.';
return $description;
}
public function getPrerequisites(): array
{
return [
DatabaseUpdatedPrerequisite::class
];
}
public function updateNecessary(): bool
{
return $this->checkIfWizardIsRequired();
}
public function executeUpdate(): bool
{
return $this->performMigration();
}
public function checkIfWizardIsRequired(): bool
{
return count($this->getMigrationRecords()) > 0;
}
public function performMigration(): bool
{
$records = $this->getMigrationRecords();
foreach ($records as $record) {
$flexFormData = GeneralUtility::xml2array($record['pi_flexform']);
$flexForm = $this->flexFormService->convertFlexFormContentToArray($record['pi_flexform']);
$targetListType = $this->getTargetListType(
$record['list_type'],
$flexForm['switchableControllerActions']
);
$allowedSettings = $this->getAllowedSettingsFromFlexForm($targetListType);
// Remove flexform data which do not exist in flexform of new plugin
foreach ($flexFormData['data'] as $sheetKey => $sheetData) {
foreach ($sheetData['lDEF'] as $settingName => $setting) {
if (!in_array($settingName, $allowedSettings, true)) {
unset($flexFormData['data'][$sheetKey]['lDEF'][$settingName]);
}
}
// Remove empty sheets
if (!count($flexFormData['data'][$sheetKey]['lDEF']) > 0) {
unset($flexFormData['data'][$sheetKey]);
}
}
if (count($flexFormData['data']) > 0) {
$newFlexform = $this->array2xml($flexFormData);
} else {
$newFlexform = '';
}
$this->updateContentElement($record['uid'], $targetListType, $newFlexform);
}
return true;
}
protected function getMigrationRecords(): array
{
$checkListTypes = array_unique(array_column(self::MIGRATION_SETTINGS, 'sourceListType'));
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
$queryBuilder = $connectionPool->getQueryBuilderForTable('tt_content');
$queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
return $queryBuilder
->select('uid', 'list_type', 'pi_flexform')
->from('tt_content')
->where(
$queryBuilder->expr()->in(
'list_type',
$queryBuilder->createNamedParameter($checkListTypes, Connection::PARAM_STR_ARRAY)
)
)
->execute()
->fetchAll();
}
protected function getTargetListType(string $sourceListType, string $switchableControllerActions): string
{
foreach (self::MIGRATION_SETTINGS as $setting) {
if ($setting['sourceListType'] === $sourceListType &&
$setting['switchableControllerActions'] === $switchableControllerActions
) {
return $setting['targetListType'];
}
}
return '';
}
protected function getAllowedSettingsFromFlexForm(string $listType): array
{
$flexFormFile = $GLOBALS['TCA']['tt_content']['columns']['pi_flexform']['config']['ds'][$listType . ',list'];
$flexFormContent = file_get_contents(GeneralUtility::getFileAbsFileName(substr(trim($flexFormFile), 5)));
$flexFormData = GeneralUtility::xml2array($flexFormContent);
// Iterate each sheet and extract all settings
$settings = [];
foreach ($flexFormData['sheets'] as $sheet) {
foreach ($sheet['ROOT']['el'] as $setting => $tceForms) {
$settings[] = $setting;
}
}
return $settings;
}
/**
* Updates list_type and pi_flexform of the given content element UID
*
* @param int $uid
* @param string $newListType
* @param string $flexform
*/
protected function updateContentElement(int $uid, string $newListType, string $flexform): void
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
$queryBuilder->update('tt_content')
->set('list_type', $newListType)
->set('pi_flexform', $flexform)
->where(
$queryBuilder->expr()->in(
'uid',
$queryBuilder->createNamedParameter($uid, Connection::PARAM_INT)
)
)
->execute();
}
/**
* Transforms the given array to FlexForm XML
*
* @param array $input
* @return string
*/
protected function array2xml(array $input = []): string
{
$options = [
'parentTagMap' => [
'data' => 'sheet',
'sheet' => 'language',
'language' => 'field',
'el' => 'field',
'field' => 'value',
'field:el' => 'el',
'el:_IS_NUM' => 'section',
'section' => 'itemType'
],
'disableTypeAttrib' => 2
];
$spaceInd = 4;
$output = GeneralUtility::array2xml($input, '', 0, 'T3FlexForms', $spaceInd, $options);
$output = '<?xml version="1.0" encoding="utf-8" standalone="yes" ?>' . LF . $output;
return $output;
}
}
@Mbigha
Copy link

Mbigha commented Jan 26, 2024

Thanks very much for this and also the explanation on your blog.
It was very helpful to me

@wazum
Copy link

wazum commented Feb 22, 2024

if (!count($flexFormData['data'][$sheetKey]['lDEF']) > 0) { looks a bit strange and confusing …
if (count($flexFormData['data'][$sheetKey]['lDEF']) === 0) { does the same.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment