8c8f437494b360a261b3c32d633d82a83ec3c1f3
[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 bool
80 */
81 protected static function hasColumns(string $tableName)
82 {
83 return
84 !empty($GLOBALS['TCA'][$tableName]['columns'])
85 && is_array($GLOBALS['TCA'][$tableName]['columns'])
86 ;
87 }
88
89 /**
90 * @param string $tableName
91 * @return bool
92 */
93 protected static function hasLanguageFieldName(string $tableName)
94 {
95 return !empty($GLOBALS['TCA'][$tableName]['ctrl']['languageField']);
96 }
97
98 /**
99 * @param string $tableName
100 * @return bool
101 */
102 protected static function hasTranslationParentFieldName(string $tableName)
103 {
104 return !empty($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']);
105 }
106
107 /**
108 * @param string $tableName
109 * @return array
110 */
111 protected static function getFieldNames(string $tableName)
112 {
113 return array_keys(
114 array_filter(
115 $GLOBALS['TCA'][$tableName]['columns'],
116 function (array $fieldConfiguration) {
117 return !empty(
118 $fieldConfiguration['config']
119 ['behaviour']['allowLanguageSynchronization']
120 );
121 }
122 )
123 );
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 * @param string $tableName
143 * @param array $states
144 */
145 public function __construct(string $tableName, array $states = [])
146 {
147 $this->tableName = $tableName;
148 $this->states = $states;
149 $this->originalStates = $states;
150
151 $this->states = $this->sanitize($states);
152 $this->states = $this->enrich($states);
153 }
154
155 /**
156 * @param array $states
157 */
158 public function update(array $states)
159 {
160 $this->states = array_merge(
161 $this->states,
162 $this->sanitize($states)
163 );
164 }
165
166 /**
167 * Updates field names having a particular state to a target state.
168 *
169 * @param string $currentState
170 * @param string $targetState
171 */
172 public function updateStates(string $currentState, string $targetState)
173 {
174 $states = [];
175 foreach ($this->filterFieldNames($currentState) as $fieldName) {
176 $states[$fieldName] = $targetState;
177 }
178 if (!empty($states)) {
179 $this->update($states);
180 }
181 }
182
183 /**
184 * @return string|null
185 */
186 public function export()
187 {
188 if (empty($this->states)) {
189 return null;
190 }
191 return json_encode($this->states);
192 }
193
194 /**
195 * @return array
196 */
197 public function toArray(): array
198 {
199 return $this->states ?? [];
200 }
201
202 /**
203 * @return string[]
204 */
205 public function getModifiedFieldNames()
206 {
207 return array_keys(
208 array_diff_assoc(
209 $this->states,
210 $this->originalStates
211 )
212 );
213 }
214
215 /**
216 * @return bool
217 */
218 public function isModified()
219 {
220 return !empty($this->getModifiedFieldNames());
221 }
222
223 /**
224 * @param string $fieldName
225 * @return bool
226 */
227 public function isUndefined(string $fieldName)
228 {
229 return !isset($this->states[$fieldName]);
230 }
231
232 /**
233 * @param string $fieldName
234 * @return bool
235 */
236 public function isCustomState(string $fieldName)
237 {
238 return ($this->states[$fieldName] ?? null) === static::STATE_CUSTOM;
239 }
240
241 /**
242 * @param string $fieldName
243 * @return bool
244 */
245 public function isParentState(string $fieldName)
246 {
247 return ($this->states[$fieldName] ?? null) === static::STATE_PARENT;
248 }
249
250 /**
251 * @param string $fieldName
252 * @return bool
253 */
254 public function isSourceState(string $fieldName)
255 {
256 return ($this->states[$fieldName] ?? null) === static::STATE_SOURCE;
257 }
258
259 /**
260 * @param string $fieldName
261 * @return null|string
262 */
263 public function getState(string $fieldName)
264 {
265 return $this->states[$fieldName] ?? null;
266 }
267
268 /**
269 * Filters field names having a desired state.
270 *
271 * @param string $desiredState
272 * @param bool $modified
273 * @return string[]
274 */
275 public function filterFieldNames(string $desiredState, bool $modified = false)
276 {
277 if (!$modified) {
278 $fieldNames = array_keys($this->states);
279 } else {
280 $fieldNames = $this->getModifiedFieldNames();
281 }
282 return array_filter(
283 $fieldNames,
284 function ($fieldName) use ($desiredState) {
285 return $this->states[$fieldName] === $desiredState;
286 }
287 );
288 }
289
290 /**
291 * Filter out field names that don't exist in TCA.
292 *
293 * @param array $states
294 * @return array
295 */
296 protected function sanitize(array $states)
297 {
298 $fieldNames = static::getFieldNames($this->tableName);
299 return array_intersect_key(
300 $states,
301 array_combine($fieldNames, $fieldNames)
302 );
303 }
304
305 /**
306 * Add missing states for field names.
307 *
308 * @param array $states
309 * @return array
310 */
311 protected function enrich(array $states)
312 {
313 foreach (static::getFieldNames($this->tableName) as $fieldName) {
314 if (!empty($states[$fieldName])) {
315 continue;
316 }
317 $states[$fieldName] = static::STATE_PARENT;
318 }
319 return $states;
320 }
321 }