fce8365868144ee83b2e9720dfa9dc46ff9633f0
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / DataHandling / Localization / State.php
1 <?php
2 namespace TYPO3\CMS\Core\DataHandling\Localization;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\GeneralUtility;
18
19 /**
20 * Value object for l10n_state field value.
21 */
22 class State
23 {
24 const STATE_CUSTOM = 'custom';
25 const STATE_PARENT = 'parent';
26 const STATE_SOURCE = 'source';
27
28 /**
29 * @param string $tableName
30 * @return null|State
31 */
32 public static function create(string $tableName)
33 {
34 if (!static::isApplicable($tableName)) {
35 return null;
36 }
37
38 return GeneralUtility::makeInstance(
39 static::class,
40 $tableName
41 );
42 }
43
44 /**
45 * @param string $tableName
46 * @param string|null $json
47 * @return null|State
48 */
49 public static function fromJSON(string $tableName, string $json = null)
50 {
51 if (!static::isApplicable($tableName)) {
52 return null;
53 }
54
55 $states = json_decode($json ?? '', true);
56 return GeneralUtility::makeInstance(
57 static::class,
58 $tableName,
59 $states ?? []
60 );
61 }
62
63 /**
64 * @param string $tableName
65 * @return bool
66 */
67 public static function isApplicable(string $tableName)
68 {
69 return
70 static::hasColumns($tableName)
71 && static::hasLanguageFieldName($tableName)
72 && static::hasTranslationParentFieldName($tableName)
73 && count(static::getFieldNames($tableName)) > 0
74 ;
75 }
76
77 /**
78 * @param string $tableName
79 * @return array
80 */
81 public static function getFieldNames(string $tableName)
82 {
83 return array_keys(
84 array_filter(
85 $GLOBALS['TCA'][$tableName]['columns'] ?? [],
86 function (array $fieldConfiguration) {
87 return !empty(
88 $fieldConfiguration['config']
89 ['behaviour']['allowLanguageSynchronization']
90 );
91 }
92 )
93 );
94 }
95
96 /**
97 * @param string $tableName
98 * @return bool
99 */
100 protected static function hasColumns(string $tableName)
101 {
102 return
103 !empty($GLOBALS['TCA'][$tableName]['columns'])
104 && is_array($GLOBALS['TCA'][$tableName]['columns'])
105 ;
106 }
107
108 /**
109 * @param string $tableName
110 * @return bool
111 */
112 protected static function hasLanguageFieldName(string $tableName)
113 {
114 return !empty($GLOBALS['TCA'][$tableName]['ctrl']['languageField']);
115 }
116
117 /**
118 * @param string $tableName
119 * @return bool
120 */
121 protected static function hasTranslationParentFieldName(string $tableName)
122 {
123 return !empty($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']);
124 }
125
126 /**
127 * @var string
128 */
129 protected $tableName;
130
131 /**
132 * @var array
133 */
134 protected $states;
135
136 /**
137 * @var array
138 */
139 protected $originalStates;
140
141 /**
142 * @var array
143 */
144 protected $validStates = [
145 self::STATE_CUSTOM,
146 self::STATE_SOURCE,
147 self::STATE_PARENT,
148 ];
149
150 /**
151 * @param string $tableName
152 * @param array $states
153 */
154 public function __construct(string $tableName, array $states = [])
155 {
156 $this->tableName = $tableName;
157 $this->states = $states;
158 $this->originalStates = $states;
159
160 $this->states = $this->enrich(
161 $this->sanitize($states)
162 );
163 }
164
165 /**
166 * @param array $states
167 */
168 public function update(array $states)
169 {
170 $this->states = array_merge(
171 $this->states,
172 $this->sanitize($states)
173 );
174 }
175
176 /**
177 * Updates field names having a particular state to a target state.
178 *
179 * @param string $currentState
180 * @param string $targetState
181 */
182 public function updateStates(string $currentState, string $targetState)
183 {
184 $states = [];
185 foreach ($this->filterFieldNames($currentState) as $fieldName) {
186 $states[$fieldName] = $targetState;
187 }
188 if (!empty($states)) {
189 $this->update($states);
190 }
191 }
192
193 /**
194 * @return string|null
195 */
196 public function export()
197 {
198 if (empty($this->states)) {
199 return null;
200 }
201 return json_encode($this->states);
202 }
203
204 /**
205 * @return array
206 */
207 public function toArray(): array
208 {
209 return $this->states ?? [];
210 }
211
212 /**
213 * @return string[]
214 */
215 public function getModifiedFieldNames()
216 {
217 return array_keys(
218 array_diff_assoc(
219 $this->states,
220 $this->originalStates
221 )
222 );
223 }
224
225 /**
226 * @return bool
227 */
228 public function isModified()
229 {
230 return !empty($this->getModifiedFieldNames());
231 }
232
233 /**
234 * @param string $fieldName
235 * @return bool
236 */
237 public function isUndefined(string $fieldName)
238 {
239 return !isset($this->states[$fieldName]);
240 }
241
242 /**
243 * @param string $fieldName
244 * @return bool
245 */
246 public function isCustomState(string $fieldName)
247 {
248 return ($this->states[$fieldName] ?? null) === static::STATE_CUSTOM;
249 }
250
251 /**
252 * @param string $fieldName
253 * @return bool
254 */
255 public function isParentState(string $fieldName)
256 {
257 return ($this->states[$fieldName] ?? null) === static::STATE_PARENT;
258 }
259
260 /**
261 * @param string $fieldName
262 * @return bool
263 */
264 public function isSourceState(string $fieldName)
265 {
266 return ($this->states[$fieldName] ?? null) === static::STATE_SOURCE;
267 }
268
269 /**
270 * @param string $fieldName
271 * @return null|string
272 */
273 public function getState(string $fieldName)
274 {
275 return $this->states[$fieldName] ?? null;
276 }
277
278 /**
279 * Filters field names having a desired state.
280 *
281 * @param string $desiredState
282 * @param bool $modified
283 * @return string[]
284 */
285 public function filterFieldNames(string $desiredState, bool $modified = false)
286 {
287 if (!$modified) {
288 $fieldNames = array_keys($this->states);
289 } else {
290 $fieldNames = $this->getModifiedFieldNames();
291 }
292 return array_filter(
293 $fieldNames,
294 function ($fieldName) use ($desiredState) {
295 return $this->states[$fieldName] === $desiredState;
296 }
297 );
298 }
299
300 /**
301 * Filter out field names that don't exist in TCA.
302 *
303 * @param array $states
304 * @return array
305 */
306 protected function sanitize(array $states)
307 {
308 $fieldNames = static::getFieldNames($this->tableName);
309 return array_intersect_key(
310 $states,
311 array_combine($fieldNames, $fieldNames)
312 );
313 }
314
315 /**
316 * Add missing states for field names.
317 *
318 * @param array $states
319 * @return array
320 */
321 protected function enrich(array $states)
322 {
323 foreach (static::getFieldNames($this->tableName) as $fieldName) {
324 $isValid = in_array(
325 $states[$fieldName] ?? null,
326 $this->validStates,
327 true
328 );
329 if ($isValid) {
330 continue;
331 }
332 $states[$fieldName] = static::STATE_PARENT;
333 }
334 return $states;
335 }
336 }