Passed
Push — master ( af9933...6d2375 )
by Torben
04:41 queued 01:21
created

PiEventPluginUpdater::performMigration()   B

Complexity

Conditions 7
Paths 15

Size

Total Lines 37
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 20
c 1
b 0
f 0
nc 15
nop 0
dl 0
loc 37
rs 8.6666
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Extension "sf_event_mgt" for TYPO3 CMS.
7
 *
8
 * For the full copyright and license information, please read the
9
 * LICENSE.txt file that was distributed with this source code.
10
 */
11
12
namespace DERHANSEN\SfEventMgt\Updates;
13
14
use TYPO3\CMS\Core\Database\Connection;
15
use TYPO3\CMS\Core\Database\ConnectionPool;
16
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
17
use TYPO3\CMS\Core\Service\FlexFormService;
18
use TYPO3\CMS\Core\Utility\GeneralUtility;
19
use TYPO3\CMS\Install\Updates\DatabaseUpdatedPrerequisite;
20
use TYPO3\CMS\Install\Updates\UpgradeWizardInterface;
21
22
class PiEventPluginUpdater implements UpgradeWizardInterface
23
{
24
    private const MIGRATION_SETTINGS = [
25
        [
26
            'sourceListType' => 'sfeventmgt_pievent',
27
            'switchableControllerActions' => 'Event->list',
28
            'targetListType' => 'sfeventmgt_pieventlist'
29
        ],
30
        [
31
            'sourceListType' => 'sfeventmgt_pievent',
32
            'switchableControllerActions' => 'Event->detail;Event->icalDownload',
33
            'targetListType' => 'sfeventmgt_pieventdetail'
34
        ],
35
        [
36
            'sourceListType' => 'sfeventmgt_pievent',
37
            'switchableControllerActions' => 'Event->registration;Event->saveRegistration;Event->saveRegistrationResult;Event->confirmRegistration;Event->cancelRegistration',
38
            'targetListType' => 'sfeventmgt_pieventregistration'
39
        ],
40
        [
41
            'sourceListType' => 'sfeventmgt_pievent',
42
            'switchableControllerActions' => 'Event->search',
43
            'targetListType' => 'sfeventmgt_pieventsearch'
44
        ],
45
        [
46
            'sourceListType' => 'sfeventmgt_pievent',
47
            'switchableControllerActions' => 'Event->calendar',
48
            'targetListType' => 'sfeventmgt_pieventcalendar'
49
        ],
50
    ];
51
52
    protected FlexFormService $flexFormService;
53
54
    public function __construct()
55
    {
56
        $this->flexFormService = GeneralUtility::makeInstance(FlexFormService::class);
57
    }
58
59
    public function getIdentifier(): string
60
    {
61
        return 'piEventPluginUpdater';
62
    }
63
64
    public function getTitle(): string
65
    {
66
        return 'Event management and registration: Migrate plugins and settings';
67
    }
68
69
    public function getDescription(): string
70
    {
71
        $description = 'The old event plugin using switchableControllerActions has been split into 5 separate plugins. ';
72
        $description .= 'This update wizard migrates all existing plugin settings and changes the plugin (list_type) ';
73
        $description .= 'to use the new plugins available.';
74
        return $description;
75
    }
76
77
    public function getPrerequisites(): array
78
    {
79
        return [
80
            DatabaseUpdatedPrerequisite::class
81
        ];
82
    }
83
84
    public function updateNecessary(): bool
85
    {
86
        return $this->checkIfWizardIsRequired();
87
    }
88
89
    public function executeUpdate(): bool
90
    {
91
        return $this->performMigration();
92
    }
93
94
    public function checkIfWizardIsRequired(): bool
95
    {
96
        return count($this->getMigrationRecords()) > 0;
97
    }
98
99
    public function performMigration(): bool
100
    {
101
        $records = $this->getMigrationRecords();
102
103
        foreach ($records as $record) {
104
            $flexFormData = GeneralUtility::xml2array($record['pi_flexform']);
105
            $flexForm = $this->flexFormService->convertFlexFormContentToArray($record['pi_flexform']);
106
            $targetListType = $this->getTargetListType(
107
                $record['list_type'],
108
                $flexForm['switchableControllerActions']
109
            );
110
            $allowedSettings = $this->getAllowedSettingsFromFlexForm($targetListType);
111
112
            // Remove flexform data which do not exist in flexform of new plugin
113
            foreach ($flexFormData['data'] as $sheetKey => $sheetData) {
114
                foreach ($sheetData['lDEF'] as $settingName => $setting) {
115
                    if (!in_array($settingName, $allowedSettings, true)) {
116
                        unset($flexFormData['data'][$sheetKey]['lDEF'][$settingName]);
117
                    }
118
                }
119
120
                // Remove empty sheets
121
                if (!count($flexFormData['data'][$sheetKey]['lDEF']) > 0) {
122
                    unset($flexFormData['data'][$sheetKey]);
123
                }
124
            }
125
126
            if (count($flexFormData['data']) > 0) {
127
                $newFlexform = $this->array2xml($flexFormData);
128
            } else {
129
                $newFlexform = '';
130
            }
131
132
            $this->updateContentElement($record['uid'], $targetListType, $newFlexform);
133
        }
134
135
        return true;
136
    }
137
138
    protected function getMigrationRecords(): array
139
    {
140
        $checkListTypes = array_unique(array_column(self::MIGRATION_SETTINGS, 'sourceListType'));
141
142
        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
143
        $queryBuilder = $connectionPool->getQueryBuilderForTable('tt_content');
144
        $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
145
146
        return $queryBuilder
147
            ->select('uid', 'list_type', 'pi_flexform')
148
            ->from('tt_content')
149
            ->where(
150
                $queryBuilder->expr()->in(
151
                    'list_type',
152
                    $queryBuilder->createNamedParameter($checkListTypes, Connection::PARAM_STR_ARRAY)
153
                )
154
            )
155
            ->execute()
156
            ->fetchAll();
157
    }
158
159
    protected function getTargetListType(string $sourceListType, string $switchableControllerActions): string
160
    {
161
        foreach (self::MIGRATION_SETTINGS as $setting) {
162
            if ($setting['sourceListType'] === $sourceListType &&
163
                $setting['switchableControllerActions'] === $switchableControllerActions
164
            ) {
165
                return $setting['targetListType'];
166
            }
167
        }
168
169
        return '';
170
    }
171
172
    protected function getAllowedSettingsFromFlexForm(string $listType): array
173
    {
174
        $flexFormFile = $GLOBALS['TCA']['tt_content']['columns']['pi_flexform']['config']['ds'][$listType . ',list'];
175
        $flexFormContent = file_get_contents(GeneralUtility::getFileAbsFileName(substr(trim($flexFormFile), 5)));
176
        $flexFormData = GeneralUtility::xml2array($flexFormContent);
177
178
        // Iterate each sheet and extract all settings
179
        $settings = [];
180
        foreach ($flexFormData['sheets'] as $sheet) {
181
            foreach ($sheet['ROOT']['el'] as $setting => $tceForms) {
182
                $settings[] = $setting;
183
            }
184
        }
185
186
        return $settings;
187
    }
188
189
    /**
190
     * Updates list_type and pi_flexform of the given content element UID
191
     *
192
     * @param int $uid
193
     * @param string $newListType
194
     * @param string $flexform
195
     */
196
    protected function updateContentElement(int $uid, string $newListType, string $flexform): void
197
    {
198
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
199
        $queryBuilder->update('tt_content')
200
            ->set('list_type', $newListType)
201
            ->set('pi_flexform', $flexform)
202
            ->where(
203
                $queryBuilder->expr()->in(
204
                    'uid',
205
                    $queryBuilder->createNamedParameter($uid, Connection::PARAM_INT)
206
                )
207
            )
208
            ->execute();
209
    }
210
211
    /**
212
     * Transforms the given array to FlexForm XML
213
     *
214
     * @param array $input
215
     * @return string
216
     */
217
    protected function array2xml(array $input = []): string
218
    {
219
        $options = [
220
            'parentTagMap' => [
221
                'data' => 'sheet',
222
                'sheet' => 'language',
223
                'language' => 'field',
224
                'el' => 'field',
225
                'field' => 'value',
226
                'field:el' => 'el',
227
                'el:_IS_NUM' => 'section',
228
                'section' => 'itemType'
229
            ],
230
            'disableTypeAttrib' => 2
231
        ];
232
        $spaceInd = 4;
233
        $output = GeneralUtility::array2xml($input, '', 0, 'T3FlexForms', $spaceInd, $options);
234
        $output = '<?xml version="1.0" encoding="utf-8" standalone="yes" ?>' . LF . $output;
235
        return $output;
236
    }
237
}
238