/
Richtext.php
220 lines (198 loc) · 9.23 KB
/
Richtext.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
<?php
declare(strict_types=1);
namespace TYPO3\CMS\Core\Configuration;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader;
use TYPO3\CMS\Core\TypoScript\TypoScriptService;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Prepare richtext configuration. Used in DataHandler and FormEngine
*
* @internal Internal class for the time being - may change / vanish any time
* @todo When I grow up, I want to become a data provider
*/
class Richtext
{
/**
* This is an intermediate class / method to retrieve RTE
* configuration until all core places use data providers to do that.
*
* @param string $table The table the field is in
* @param string $field Field name
* @param int $pid Real page id
* @param string $recordType Record type value
* @param array $tcaFieldConf ['config'] section of TCA field
* @return array
*/
public function getConfiguration(string $table, string $field, int $pid, string $recordType, array $tcaFieldConf): array
{
// create instance of NodeFactory, ask for "text" element
//
// As soon an the Data handler starts using FormDataProviders, this class can vanish again, and the hack to
// test for specific rich text instances can be dropped: Split the "TcaText" data provider into multiple parts, each
// RTE should register and own data provider that does the transformation / configuration providing. This way,
// the explicit check for different RTE classes is removed from core and "hooked in" by the RTE's.
// The main problem here is that all parameters that the processing needs is handed over to as TSconfig
// "dotted array" syntax. We convert at least the processing information available under "processing"
// together with pageTS, this way it can be overridden and understood in RteHtmlParser.
// However, all other parts of the core will depend on the non-dotted syntax (coming from Yaml directly)
$pageTs = $this->getPageTsConfiguration($table, $field, $pid, $recordType);
// determine which preset to use
$usePreset = $pageTs['preset'] ?? $tcaFieldConf['richtextConfiguration'] ?? 'default';
// load configuration from preset
$configuration = $this->loadConfigurationFromPreset($usePreset);
// overlay preset configuration with pageTs
ArrayUtility::mergeRecursiveWithOverrule(
$configuration,
$this->addFlattenedPageTsConfig($pageTs)
);
// Handle "mode" / "transformation" config when overridden
if (isset($configuration['proc.']['overruleMode']) && $configuration['proc.']['overruleMode'] === 'ts_css') {
// Change legacy 'ts_css' to 'default'
$configuration['proc.']['overruleMode'] = 'default';
} elseif (!isset($configuration['proc.']['mode']) && !isset($configuration['proc.']['overruleMode'])) {
$configuration['proc.']['overruleMode'] = 'default';
}
return $configuration;
}
/**
* Load a configuration preset from an external resource (currently only YAML is supported).
* This is the default behaviour and can be overridden by pageTSconfig.
*
* @param string $presetName
* @return array the parsed configuration
*/
protected function loadConfigurationFromPreset(string $presetName = ''): array
{
$configuration = [];
if (!empty($presetName) && isset($GLOBALS['TYPO3_CONF_VARS']['RTE']['Presets'][$presetName])) {
$fileLoader = GeneralUtility::makeInstance(YamlFileLoader::class);
$configuration = $fileLoader->load($GLOBALS['TYPO3_CONF_VARS']['RTE']['Presets'][$presetName]);
// For future versions, you should however rely on the "processing" key and not the "proc" key.
if (is_array($configuration['processing'])) {
$configuration['proc.'] = $this->convertPlainArrayToTypoScriptArray($configuration['processing']);
}
}
return $configuration;
}
/**
* Return RTE section of page TS
*
* @param int $pid Page ts of given pid
* @return array RTE section of pageTs of given pid
*/
protected function getRtePageTsConfigOfPid(int $pid): array
{
// Override with pageTs if needed
$backendUser = $this->getBackendUser();
return $backendUser->getTSConfig('RTE', BackendUtility::getPagesTSconfig($pid));
}
/**
* Returns an array with Typoscript the old way (with dot)
* Since the functionality in Yaml is without the dots, but the new configuration is used without the dots
* this functionality adds also an explicit = 1 to the arrays
*
* @param array $plainArray An array
* @return array array with TypoScript as usual (with dot)
*/
protected function convertPlainArrayToTypoScriptArray(array $plainArray)
{
$typoScriptArray = [];
foreach ($plainArray as $key => $value) {
if (is_array($value)) {
if (!isset($typoScriptArray[$key])) {
$typoScriptArray[$key] = 1;
}
$typoScriptArray[$key . '.'] = $this->convertPlainArrayToTypoScriptArray($value);
} else {
$typoScriptArray[$key] = is_null($value) ? '' : $value;
}
}
return $typoScriptArray;
}
/**
* @return BackendUserAuthentication
*/
protected function getBackendUser(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
/**
* Add all PageTS.RTE options keys to configuration without dots
*
* We need to keep the dotted keys for backwards compatibility like ext:rtehtmlarea
*
* @param array $typoScriptArray TypoScriptArray
* @return array array with config without dots added
*/
protected function addFlattenedPageTsConfig(array $typoScriptArray): array
{
foreach ($typoScriptArray as $key => $data) {
if (substr($key, -1) !== '.') {
continue;
}
/** @var TypoScriptService $typoScriptService */
$typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
$typoScriptArray[substr($key, 0, -1)] = $typoScriptService->convertTypoScriptArrayToPlainArray($typoScriptArray[$key]);
}
return $typoScriptArray;
}
/**
* Load PageTS configuration for the RTE
*
* Return RTE section of page TS, taking into account overloading via table, field and record type
*
* @param string $table The table the field is in
* @param string $field Field name
* @param int $pid Real page id
* @param string $recordType Record type value
* @return array
*/
protected function getPageTsConfiguration(string $table, string $field, int $pid, string $recordType): array
{
// Load PageTSconfig configuration
$fullPageTsConfig = $this->getRtePageTsConfigOfPid($pid);
$fullPageTsConfig = !empty($fullPageTsConfig['properties']) ? $fullPageTsConfig['properties'] : [];
$defaultPageTsConfigOverrides = isset($fullPageTsConfig['default.']) ? $fullPageTsConfig['default.'] : null;
$fieldSpecificPageTsConfigOverrides = isset($fullPageTsConfig['config.'][$table . '.'][$field . '.']) ? $fullPageTsConfig['config.'][$table . '.'][$field . '.'] : null;
unset($fullPageTsConfig['default.'], $fullPageTsConfig['config.']);
// First use RTE.*
$rtePageTsConfiguration = $fullPageTsConfig;
// Then overload with RTE.default.*
if (is_array($defaultPageTsConfigOverrides)) {
ArrayUtility::mergeRecursiveWithOverrule($rtePageTsConfiguration, $defaultPageTsConfigOverrides);
}
// Then overload with RTE.config.tt_content.bodytext
if (is_array($fieldSpecificPageTsConfigOverrides)) {
$fieldSpecificPageTsConfigOverridesWithoutType = $fieldSpecificPageTsConfigOverrides;
unset($fieldSpecificPageTsConfigOverridesWithoutType['types.']);
ArrayUtility::mergeRecursiveWithOverrule($rtePageTsConfiguration, $fieldSpecificPageTsConfigOverridesWithoutType);
// Then overload with RTE.config.tt_content.bodytext.types.textmedia
if (
$recordType
&& isset($fieldSpecificPageTsConfigOverrides['types.'][$recordType . '.'])
&& is_array($fieldSpecificPageTsConfigOverrides['types.'][$recordType . '.'])
) {
ArrayUtility::mergeRecursiveWithOverrule(
$rtePageTsConfiguration,
$fieldSpecificPageTsConfigOverrides['types.'][$recordType . '.']
);
}
}
return $rtePageTsConfiguration;
}
}