[BUGFIX] FormEngine: Fix placeholder traversal for select type relations
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Tests / Unit / Form / FormDataProvider / TcaInputPlaceholdersTest.php
1 <?php
2 namespace TYPO3\CMS\Backend\Tests\Unit\Form\FormDataProvider;
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 Prophecy\Argument;
18 use Prophecy\Prophecy\ObjectProphecy;
19 use TYPO3\CMS\Backend\Form\FormDataCompiler;
20 use TYPO3\CMS\Backend\Form\FormDataGroup\TcaInputPlaceholderRecord;
21 use TYPO3\CMS\Backend\Form\FormDataProvider\TcaInputPlaceholders;
22 use TYPO3\CMS\Core\Tests\UnitTestCase;
23 use TYPO3\CMS\Core\Utility\GeneralUtility;
24 use TYPO3\CMS\Lang\LanguageService;
25
26 /**
27 * Test case
28 */
29 class TcaInputPlaceholdersTest extends UnitTestCase
30 {
31 /**
32 * @var TcaInputPlaceholders
33 */
34 protected $subject;
35
36 public function setUp()
37 {
38 $this->subject = new TcaInputPlaceholders();
39 }
40
41 /**
42 * @test
43 */
44 public function addDataRemovesEmptyPlaceholderOption()
45 {
46 $input = [
47 'tableName' => 'aTable',
48 'databaseRow' => [],
49 'processedTca' => [
50 'columns' => [
51 'aField' => [
52 'config' => [
53 'type' => 'input',
54 'placeholder' => '',
55 ]
56 ]
57 ],
58 ],
59 ];
60
61 $expected = $input;
62 unset($expected['processedTca']['columns']['aField']['config']['placeholder']);
63
64 $this->assertSame($expected, $this->subject->addData($input));
65 }
66
67 /**
68 * @test
69 */
70 public function addDataReturnsUnmodifiedSimpleStringPlaceholder()
71 {
72 $input = [
73 'tableName' => 'aTable',
74 'databaseRow' => [],
75 'processedTca' => [
76 'columns' => [
77 'aField' => [
78 'config' => [
79 'type' => 'input',
80 'placeholder' => 'aPlaceholder',
81 ]
82 ]
83 ],
84 ],
85 ];
86
87 $expected = $input;
88
89 $this->assertSame($expected, $this->subject->addData($input));
90 }
91
92 /**
93 * @test
94 */
95 public function addDataReturnsValueFromDatabaseRowAsPlaceholder()
96 {
97 $input = [
98 'tableName' => 'aTable',
99 'databaseRow' => [
100 'anotherField' => 'anotherPlaceholder'
101 ],
102 'processedTca' => [
103 'columns' => [
104 'aField' => [
105 'config' => [
106 'type' => 'input',
107 'placeholder' => '__row|anotherField',
108 ]
109 ]
110 ],
111 ],
112 ];
113
114 $expected = $input;
115 $expected['processedTca']['columns']['aField']['config']['placeholder'] = 'anotherPlaceholder';
116
117 $this->assertSame($expected, $this->subject->addData($input));
118 }
119
120 /**
121 * @test
122 */
123 public function addDataReturnsValueFromSelectTypeRelation()
124 {
125 $input = [
126 'tableName' => 'aTable',
127 'databaseRow' => [
128 'aField' => '',
129 'aRelationField' => ['42'],
130 ],
131 'processedTca' => [
132 'columns' => [
133 'aField' => [
134 'config' => [
135 'type' => 'input',
136 'placeholder' => '__row|aRelationField|aForeignField',
137 ]
138 ],
139 'aRelationField' => [
140 'config' => [
141 'type' => 'select',
142 'foreign_table' => 'aForeignTable'
143 ]
144 ]
145 ],
146 ],
147 ];
148
149 $aForeignTableInput = [
150 'tableName' => 'aForeignTable',
151 'databaseRow' => [
152 'aForeignField' => 'aForeignValue',
153 ],
154 'processedTca' => [
155 'columns' => [
156 'aForeignField' => [
157 'config' => [
158 'type' => 'input',
159 ]
160 ],
161 ],
162 ],
163 ];
164
165 /** @var FormDataCompiler|ObjectProphecy $formDataCompilerProphecy */
166 $formDataCompilerProphecy = $this->prophesize(FormDataCompiler::class);
167 GeneralUtility::addInstance(FormDataCompiler::class, $formDataCompilerProphecy->reveal());
168 $formDataCompilerProphecy->compile([
169 'command' => 'edit',
170 'vanillaUid' => 42,
171 'tableName' => 'aForeignTable',
172 'inlineCompileExistingChildren' => false,
173 'columnsToProcess' => ['aForeignField']
174 ])
175 ->shouldBeCalled()
176 ->willReturn($aForeignTableInput);
177
178 $expected = $input;
179 $expected['processedTca']['columns']['aField']['config']['placeholder'] = $aForeignTableInput['databaseRow']['aForeignField'];
180
181 $this->assertSame($expected, $this->subject->addData($input));
182 }
183
184 /**
185 * @test
186 */
187 public function addDataReturnsNoPlaceholderForNewSelectTypeRelation()
188 {
189 $input = [
190 'tableName' => 'aTable',
191 'databaseRow' => [
192 'aField' => '',
193 'aRelationField' => [],
194 ],
195 'processedTca' => [
196 'columns' => [
197 'aField' => [
198 'config' => [
199 'type' => 'input',
200 'placeholder' => '__row|aRelationField|aForeignField',
201 ]
202 ],
203 'aRelationField' => [
204 'config' => [
205 'type' => 'select',
206 'foreign_table' => 'aForeignTable'
207 ]
208 ]
209 ],
210 ],
211 ];
212
213 $expected = $input;
214 unset($expected['processedTca']['columns']['aField']['config']['placeholder']);
215
216 $this->assertSame($expected, $this->subject->addData($input));
217 }
218
219 /**
220 * @test
221 */
222 public function addDataReturnsValueFromGroupTypeRelation()
223 {
224 $input = [
225 'tableName' => 'aTable',
226 'databaseRow' => [
227 'aField' => '',
228 'uid_local' => 'sys_file_3|aLabel,sys_file_5|anotherLabel',
229 ],
230 'processedTca' => [
231 'columns' => [
232 'aField' => [
233 'config' => [
234 'type' => 'input',
235 'placeholder' => '__row|uid_local|sha1',
236 ]
237 ],
238 'uid_local' => [
239 'config' => [
240 'type' => 'group',
241 'internal_type' => 'db',
242 'allowed' => 'sys_file'
243 ]
244 ]
245 ],
246 ],
247 ];
248
249 $sysFileProphecyResult = [
250 'tableName' => 'sys_file',
251 'databaseRow' => [
252 'sha1' => 'aSha1Value',
253 ],
254 'processedTca' => [
255 'columns' => [
256 'sha1' => [
257 'config' => [
258 'type' => 'input',
259 ]
260 ],
261 ],
262 ],
263 ];
264
265 /** @var TcaInputPlaceholderRecord $languageService */
266 $formDataCompilerProphecy = $this->prophesize(FormDataCompiler::class);
267 GeneralUtility::addInstance(FormDataCompiler::class, $formDataCompilerProphecy->reveal());
268 $formDataCompilerProphecy->compile([
269 'command' => 'edit',
270 'vanillaUid' => 3,
271 'tableName' => 'sys_file',
272 'inlineCompileExistingChildren' => false,
273 'columnsToProcess' => ['sha1']
274 ])
275 ->shouldBeCalled()
276 ->willReturn($sysFileProphecyResult);
277
278 $expected = $input;
279 $expected['processedTca']['columns']['aField']['config']['placeholder'] = $sysFileProphecyResult['databaseRow']['sha1'];
280
281 $this->assertSame($expected, $this->subject->addData($input));
282 }
283
284 /**
285 * @test
286 */
287 public function addDataReturnsValueFromInlineTypeRelation()
288 {
289 $input = [
290 'tableName' => 'aTable',
291 'databaseRow' => [
292 'aField' => '',
293 'metadata' => '2',
294 ],
295 'processedTca' => [
296 'columns' => [
297 'aField' => [
298 'config' => [
299 'type' => 'input',
300 'placeholder' => '__row|metadata|title',
301 ]
302 ],
303 'metadata' => [
304 'config' => [
305 'readOnly' => 1,
306 'type' => 'inline',
307 'foreign_table' => 'sys_file_metadata',
308 'foreign_field' => 'file',
309 ]
310 ]
311 ],
312 ],
313 ];
314
315 $sysFileMetadataProphecyResult = [
316 'tableName' => 'sys_file_metadata',
317 'databaseRow' => [
318 'title' => 'aTitle',
319 ],
320 'processedTca' => [
321 'columns' => [
322 'sha1' => [
323 'config' => [
324 'type' => 'input',
325 ]
326 ],
327 ],
328 ],
329 ];
330
331 /** @var TcaInputPlaceholderRecord $languageService */
332 $formDataCompilerProphecy = $this->prophesize(FormDataCompiler::class);
333 GeneralUtility::addInstance(FormDataCompiler::class, $formDataCompilerProphecy->reveal());
334 $formDataCompilerProphecy->compile([
335 'command' => 'edit',
336 'vanillaUid' => 2,
337 'tableName' => 'sys_file_metadata',
338 'inlineCompileExistingChildren' => false,
339 'columnsToProcess' => ['title']
340 ])
341 ->shouldBeCalled()
342 ->willReturn($sysFileMetadataProphecyResult);
343
344 $expected = $input;
345 $expected['processedTca']['columns']['aField']['config']['placeholder'] = $sysFileMetadataProphecyResult['databaseRow']['title'];
346
347 $this->assertSame($expected, $this->subject->addData($input));
348 }
349
350 /**
351 * @test
352 */
353 public function addDataReturnsValueFromRelationsRecursively()
354 {
355 $input = [
356 'tableName' => 'aTable',
357 'databaseRow' => [
358 'aField' => '',
359 'uid_local' => 'sys_file_3|aLabel,sys_file_5|anotherLabel',
360 ],
361 'processedTca' => [
362 'columns' => [
363 'aField' => [
364 'config' => [
365 'type' => 'input',
366 'placeholder' => '__row|uid_local|metadata|title',
367 ]
368 ],
369 'uid_local' => [
370 'config' => [
371 'type' => 'group',
372 'internal_type' => 'db',
373 'allowed' => 'sys_file'
374 ]
375 ]
376 ],
377 ],
378 ];
379
380 $sysFileProphecyResult = [
381 'tableName' => 'sys_file',
382 'databaseRow' => [
383 'metadata' => '7',
384 ],
385 'processedTca' => [
386 'columns' => [
387 'metadata' => [
388 'config' => [
389 'readOnly' => 1,
390 'type' => 'inline',
391 'foreign_table' => 'sys_file_metadata',
392 'foreign_field' => 'file',
393 ]
394 ]
395 ],
396 ],
397 ];
398
399 $sysFileMetadataProphecyResult = [
400 'tableName' => 'sys_file_metadata',
401 'databaseRow' => [
402 'title' => 'aTitle',
403 ],
404 'processedTca' => [
405 'columns' => [
406 'sha1' => [
407 'config' => [
408 'type' => 'input',
409 ]
410 ],
411 ],
412 ],
413 ];
414
415 $sysFileFormDataCompilerProphecy = $this->prophesize(FormDataCompiler::class);
416 GeneralUtility::addInstance(FormDataCompiler::class, $sysFileFormDataCompilerProphecy->reveal());
417 $sysFileFormDataCompilerProphecy->compile([
418 'command' => 'edit',
419 'vanillaUid' => 3,
420 'tableName' => 'sys_file',
421 'inlineCompileExistingChildren' => false,
422 'columnsToProcess' => ['metadata']
423 ])
424 ->shouldBeCalled()
425 ->willReturn($sysFileProphecyResult);
426
427 $sysFileMetaDataFormDataCompilerProphecy = $this->prophesize(FormDataCompiler::class);
428 GeneralUtility::addInstance(FormDataCompiler::class, $sysFileMetaDataFormDataCompilerProphecy->reveal());
429 $sysFileMetaDataFormDataCompilerProphecy->compile([
430 'command' => 'edit',
431 'vanillaUid' => 7,
432 'tableName' => 'sys_file_metadata',
433 'inlineCompileExistingChildren' => false,
434 'columnsToProcess' => ['title']
435 ])
436 ->shouldBeCalled()
437 ->willReturn($sysFileMetadataProphecyResult);
438
439 $expected = $input;
440 $expected['processedTca']['columns']['aField']['config']['placeholder'] = $sysFileMetadataProphecyResult['databaseRow']['title'];
441
442 $this->assertSame($expected, $this->subject->addData($input));
443 }
444
445 /**
446 * @test
447 */
448 public function addDataCallsLanguageServiceForLocalizedPlaceholders()
449 {
450 $labelString = 'LLL:EXT:some_ext/Resources/Private/Language/locallang.xlf:my_placeholder';
451 $localizedString = 'My Placeholder';
452 $input = [
453 'tableName' => 'aTable',
454 'databaseRow' => [],
455 'processedTca' => [
456 'columns' => [
457 'aField' => [
458 'config' => [
459 'type' => 'input',
460 'placeholder' => $labelString,
461 ]
462 ]
463 ],
464 ],
465 ];
466 $expected = $input;
467 $expected['processedTca']['columns']['aField']['config']['placeholder'] = $localizedString;
468
469 /** @var LanguageService|ObjectProphecy $languageService */
470 $languageService = $this->prophesize(LanguageService::class);
471 $GLOBALS['LANG'] = $languageService->reveal();
472 $languageService->sL($labelString)->shouldBeCalled()->willReturn($localizedString);
473
474 $this->assertSame($expected, $this->subject->addData($input));
475 }
476 }