Completed
Push — master ( 892a3f...068784 )
by Fabien
50:28
created
Classes/Grid/VisibilityRenderer.php 2 patches
Indentation   +78 added lines, -78 removed lines patch added patch discarded remove patch
@@ -21,83 +21,83 @@
 block discarded – undo
21 21
 class VisibilityRenderer extends ColumnRendererAbstract
22 22
 {
23 23
 
24
-    /**
25
-     * Render visibility for the Grid.
26
-     *
27
-     * @return string
28
-     */
29
-    public function render()
30
-    {
31
-
32
-        $output = '';
33
-        $hiddenField = Tca::table()->getHiddenField();
34
-
35
-        if ($hiddenField) {
36
-
37
-            $spriteName = $this->object[$hiddenField] ? 'actions-edit-unhide' : 'actions-edit-hide';
38
-
39
-            $label = $this->object[$hiddenField] ? 'unHide' : 'hide';
40
-
41
-            $output = $this->makeLinkButton()
42
-                ->setHref($this->getEditUri($this->object))
43
-                ->setClasses('btn-visibility-toggle')
44
-                ->setDataAttributes([
45
-                    'toggle' => 'tooltip',
46
-                ])
47
-                ->setTitle($this->getLabelService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_mod_web_list.xlf:' . $label))
48
-                ->setIcon($this->getIconFactory()->getIcon($spriteName, Icon::SIZE_SMALL))
49
-                ->render();
50
-        }
51
-        return $output;
52
-    }
53
-
54
-    /**
55
-     * @param Content $object
56
-     * @return string
57
-     */
58
-    protected function getEditUri(Content $object)
59
-    {
60
-        $hiddenField = Tca::table()->getHiddenField();
61
-
62
-        $additionalParameters = array(
63
-            $this->getModuleLoader()->getParameterPrefix() => [
64
-                'controller' => 'Content',
65
-                'action' => 'update',
66
-                'format' => 'json',
67
-                'fieldNameAndPath' => Tca::table()->getHiddenField(),
68
-                'matches' => [
69
-                    'uid' => $object->getUid(),
70
-                ],
71
-                'content' => [$hiddenField => (int)!$this->object[$hiddenField]],
72
-            ],
73
-        );
74
-
75
-        return $this->getModuleLoader()->getModuleUrl($additionalParameters);
76
-    }
77
-
78
-    /**
79
-     * @return LinkButton
80
-     */
81
-    protected function makeLinkButton()
82
-    {
83
-        return GeneralUtility::makeInstance(LinkButton::class);
84
-    }
85
-
86
-
87
-    /**
88
-     * @return IconFactory
89
-     */
90
-    protected function getIconFactory()
91
-    {
92
-        return GeneralUtility::makeInstance(IconFactory::class);
93
-    }
94
-
95
-    /**
96
-     * @return LanguageService
97
-     */
98
-    protected function getLabelService()
99
-    {
100
-        return $GLOBALS['LANG'];
101
-    }
24
+	/**
25
+	 * Render visibility for the Grid.
26
+	 *
27
+	 * @return string
28
+	 */
29
+	public function render()
30
+	{
31
+
32
+		$output = '';
33
+		$hiddenField = Tca::table()->getHiddenField();
34
+
35
+		if ($hiddenField) {
36
+
37
+			$spriteName = $this->object[$hiddenField] ? 'actions-edit-unhide' : 'actions-edit-hide';
38
+
39
+			$label = $this->object[$hiddenField] ? 'unHide' : 'hide';
40
+
41
+			$output = $this->makeLinkButton()
42
+				->setHref($this->getEditUri($this->object))
43
+				->setClasses('btn-visibility-toggle')
44
+				->setDataAttributes([
45
+					'toggle' => 'tooltip',
46
+				])
47
+				->setTitle($this->getLabelService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_mod_web_list.xlf:' . $label))
48
+				->setIcon($this->getIconFactory()->getIcon($spriteName, Icon::SIZE_SMALL))
49
+				->render();
50
+		}
51
+		return $output;
52
+	}
53
+
54
+	/**
55
+	 * @param Content $object
56
+	 * @return string
57
+	 */
58
+	protected function getEditUri(Content $object)
59
+	{
60
+		$hiddenField = Tca::table()->getHiddenField();
61
+
62
+		$additionalParameters = array(
63
+			$this->getModuleLoader()->getParameterPrefix() => [
64
+				'controller' => 'Content',
65
+				'action' => 'update',
66
+				'format' => 'json',
67
+				'fieldNameAndPath' => Tca::table()->getHiddenField(),
68
+				'matches' => [
69
+					'uid' => $object->getUid(),
70
+				],
71
+				'content' => [$hiddenField => (int)!$this->object[$hiddenField]],
72
+			],
73
+		);
74
+
75
+		return $this->getModuleLoader()->getModuleUrl($additionalParameters);
76
+	}
77
+
78
+	/**
79
+	 * @return LinkButton
80
+	 */
81
+	protected function makeLinkButton()
82
+	{
83
+		return GeneralUtility::makeInstance(LinkButton::class);
84
+	}
85
+
86
+
87
+	/**
88
+	 * @return IconFactory
89
+	 */
90
+	protected function getIconFactory()
91
+	{
92
+		return GeneralUtility::makeInstance(IconFactory::class);
93
+	}
94
+
95
+	/**
96
+	 * @return LanguageService
97
+	 */
98
+	protected function getLabelService()
99
+	{
100
+		return $GLOBALS['LANG'];
101
+	}
102 102
 
103 103
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -44,7 +44,7 @@
 block discarded – undo
44 44
                 ->setDataAttributes([
45 45
                     'toggle' => 'tooltip',
46 46
                 ])
47
-                ->setTitle($this->getLabelService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_mod_web_list.xlf:' . $label))
47
+                ->setTitle($this->getLabelService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_mod_web_list.xlf:'.$label))
48 48
                 ->setIcon($this->getIconFactory()->getIcon($spriteName, Icon::SIZE_SMALL))
49 49
                 ->render();
50 50
         }
Please login to merge, or discard this patch.
Classes/Module/ModuleLoader.php 2 patches
Indentation   +999 added lines, -999 removed lines patch added patch discarded remove patch
@@ -37,1004 +37,1004 @@
 block discarded – undo
37 37
 class ModuleLoader
38 38
 {
39 39
 
40
-    /**
41
-     * Define the default main module
42
-     */
43
-    const DEFAULT_MAIN_MODULE = 'content';
44
-
45
-    /**
46
-     * Define the default pid
47
-     */
48
-    const DEFAULT_PID = 0;
49
-
50
-    /**
51
-     * The type of data being listed (which corresponds to a table name in TCA)
52
-     *
53
-     * @var string
54
-     */
55
-    protected $dataType;
56
-
57
-    /**
58
-     * @var string
59
-     */
60
-    protected $defaultPid;
61
-
62
-    /**
63
-     * @var bool
64
-     */
65
-    protected $isPidIgnored = false;
66
-
67
-    /**
68
-     * @var bool
69
-     */
70
-    protected $showPageTree = false;
71
-
72
-    /**
73
-     * @var bool
74
-     */
75
-    protected $isShown = true;
76
-
77
-    /**
78
-     * @var string
79
-     */
80
-    protected $access;
81
-
82
-    /**
83
-     * @var string
84
-     */
85
-    protected $mainModule;
86
-
87
-    /**
88
-     * @var string
89
-     */
90
-    protected $position = '';
91
-
92
-    /**
93
-     * @var string
94
-     */
95
-    protected $icon;
96
-
97
-    /**
98
-     * @var string
99
-     */
100
-    protected $moduleLanguageFile;
101
-
102
-    /**
103
-     * The module key such as m1, m2.
104
-     *
105
-     * @var string
106
-     */
107
-    protected $moduleKey = 'm1';
108
-
109
-    /**
110
-     * @var string[]
111
-     */
112
-    protected $additionalJavaScriptFiles = [];
113
-
114
-    /**
115
-     * @var string[]
116
-     */
117
-    protected $additionalStyleSheetFiles = [];
118
-
119
-    /**
120
-     * @var array
121
-     */
122
-    protected $components = [];
123
-
124
-    /**
125
-     * @param string $dataType
126
-     */
127
-    public function __construct($dataType = null)
128
-    {
129
-        $this->dataType = $dataType;
130
-
131
-        // Initialize components
132
-        $this->components = [
133
-            ModulePosition::DOC_HEADER => [
134
-                ModulePosition::TOP => [
135
-                    ModulePosition::LEFT => [],
136
-                    ModulePosition::RIGHT => [
137
-                        ToolButton::class,
138
-                    ],
139
-                ],
140
-                ModulePosition::BOTTOM => [
141
-                    ModulePosition::LEFT => [
142
-                        NewButton::class,
143
-                        BackViewHelper::class,
144
-                    ],
145
-                    ModulePosition::RIGHT => [],
146
-                ],
147
-            ],
148
-            ModulePosition::GRID => [
149
-                ModulePosition::TOP => [
150
-                    RelationsCheck::class,
151
-                    #\Fab\Vidi\View\Tab\DataTypeTab::class,
152
-                ],
153
-                ModulePosition::BUTTONS => [
154
-                    EditButton::class,
155
-                    DeleteButton::class,
156
-                ],
157
-                ModulePosition::BOTTOM => [],
158
-            ],
159
-            ModulePosition::MENU_MASS_ACTION => [
160
-                ExportXlsMenuItem::class,
161
-                ExportXmlMenuItem::class,
162
-                ExportCsvMenuItem::class,
163
-                DividerMenuItem::class,
164
-                MassDeleteMenuItem::class,
165
-                #\Fab\Vidi\View\MenuItem\MassEditMenuItem::class,
166
-            ],
167
-        ];
168
-    }
169
-
170
-    /**
171
-     * Tell whether a module is already registered.
172
-     *
173
-     * @param string $dataType
174
-     * @return bool
175
-     */
176
-    public function isRegistered($dataType): bool
177
-    {
178
-        $internalModuleSignature = $this->getInternalModuleSignature($dataType);
179
-        return !empty($GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature]);
180
-    }
181
-
182
-    /**
183
-     * @return array
184
-     */
185
-    protected function getExistingInternalConfiguration(): array
186
-    {
187
-        $internalModuleSignature = $this->getInternalModuleSignature();
188
-        return $GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature] ?? [];
189
-    }
190
-
191
-    /**
192
-     * @return array
193
-     */
194
-    protected function getExistingMainConfiguration(): array
195
-    {
196
-        $moduleSignature = $this->computeMainModule() . '_' . $this->getInternalModuleSignature();
197
-        return $GLOBALS['TBE_MODULES']['_configuration'][$moduleSignature] ?? [];
198
-    }
199
-
200
-    /**
201
-     * @return string
202
-     */
203
-    protected function computeMainModule(): string
204
-    {
205
-        $existingConfiguration = $this->getExistingInternalConfiguration();
206
-
207
-        if ($this->mainModule !== null) {
208
-            $mainModule = $this->mainModule;
209
-        } elseif (!empty($existingConfiguration['mainModule'])) { // existing configuration may override.
210
-            $mainModule = $existingConfiguration['mainModule'];
211
-        } else {
212
-            $mainModule = self::DEFAULT_MAIN_MODULE; //default value.
213
-        }
214
-        return $mainModule;
215
-    }
216
-
217
-    /**
218
-     * @return string
219
-     */
220
-    protected function computeDefaultPid(): string
221
-    {
222
-        $existingConfiguration = $this->getExistingInternalConfiguration();
223
-
224
-        if ($this->defaultPid !== null) {
225
-            $defaultPid = $this->defaultPid;
226
-        } elseif (!empty($existingConfiguration['defaultPid'])) { // existing configuration may override.
227
-            $defaultPid = $existingConfiguration['defaultPid'];
228
-        } else {
229
-            $defaultPid = self::DEFAULT_PID; //default value.
230
-        }
231
-        return $defaultPid;
232
-    }
233
-
234
-    /**
235
-     * @return array
236
-     */
237
-    protected function computeAdditionalJavaScriptFiles(): array
238
-    {
239
-        $additionalJavaScriptFiles = $this->additionalJavaScriptFiles;
240
-
241
-        // Possible merge of existing javascript files.
242
-        $existingConfiguration = $this->getExistingInternalConfiguration();
243
-        if (!empty($existingConfiguration['additionalJavaScriptFiles'])) {
244
-            $additionalJavaScriptFiles = array_merge($additionalJavaScriptFiles, $existingConfiguration['additionalJavaScriptFiles']);
245
-        }
246
-
247
-        return $additionalJavaScriptFiles;
248
-    }
249
-
250
-    /**
251
-     * @return array
252
-     */
253
-    protected function computeAdditionalStyleSheetFiles(): array
254
-    {
255
-        $additionalStyleSheetFiles = $this->additionalStyleSheetFiles;
256
-
257
-        // Possible merge of existing style sheets.
258
-        $existingConfiguration = $this->getExistingInternalConfiguration();
259
-        if (!empty($existingConfiguration['additionalStyleSheetFiles'])) {
260
-            $additionalStyleSheetFiles = array_merge($additionalStyleSheetFiles, $existingConfiguration['additionalStyleSheetFiles']);
261
-        }
262
-
263
-        return $additionalStyleSheetFiles;
264
-    }
265
-
266
-    /**
267
-     * @return array
268
-     */
269
-    protected function computeComponents(): array
270
-    {
271
-        // We override the config in any case. See if we need more than that.
272
-        return $this->components;
273
-    }
274
-
275
-    /**
276
-     * Register the module in two places: core + vidi internal.
277
-     *
278
-     * @return $this
279
-     */
280
-    public function register(): self
281
-    {
282
-        // Internal Vidi module registration.
283
-        $configuration = [];
284
-        $configuration['dataType'] = $this->dataType;
285
-        $configuration['mainModule'] = $this->computeMainModule();
286
-        $configuration['defaultPid'] = $this->computeDefaultPid();
287
-        $configuration['additionalJavaScriptFiles'] = $this->computeAdditionalJavaScriptFiles();
288
-        $configuration['additionalStyleSheetFiles'] = $this->computeAdditionalStyleSheetFiles();
289
-        $configuration['components'] = $this->computeComponents();
290
-        $configuration['isPidIgnored'] = $this->isPidIgnored;
291
-
292
-        $internalModuleSignature = $this->getInternalModuleSignature();
293
-        $GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature] = $configuration;
294
-
295
-        // Core module registration.
296
-        // Register and displays module in the BE only if told, default is "true".
297
-        if ($this->isShown) {
298
-
299
-            $moduleConfiguration = [];
300
-            #$moduleConfiguration['routeTarget'] = \Fab\Vidi\Controller\ContentController::class . '::mainAction', // what to do here?
301
-            $moduleConfiguration['access'] = $this->getAccess();
302
-            $moduleConfiguration['labels'] = $this->getModuleLanguageFile();
303
-            $icon = $this->getIcon();
304
-            if ($icon) {
305
-                $moduleConfiguration['icon'] = $icon;
306
-            }
307
-
308
-            if ($this->showPageTree) {
309
-                $moduleConfiguration['navigationComponentId'] = 'TYPO3/CMS/Backend/PageTree/PageTreeElement';
310
-                $moduleConfiguration['inheritNavigationComponentFromMainModule'] = true;
311
-            } else {
312
-                $moduleConfiguration['inheritNavigationComponentFromMainModule'] = true;
313
-            }
314
-
315
-            ExtensionUtility::registerModule(
316
-                'Fab.vidi',
317
-                $this->computeMainModule(),
318
-                $this->dataType . '_' . $this->moduleKey,
319
-                $this->position,
320
-                [
321
-                    ContentController::class => 'index, list, delete, update, edit, copy, move, localize, sort, copyClipboard, moveClipboard',
322
-                    ToolController::class => 'welcome, work',
323
-                    FacetController::class => 'autoSuggest, autoSuggests',
324
-                    SelectionController::class => 'edit, update, create, delete, list, show',
325
-                    UserPreferencesController::class => 'save',
326
-                    ClipboardController::class => 'save, flush, show',
327
-                ],
328
-                $moduleConfiguration
329
-            );
330
-        }
331
-        return $this;
332
-    }
333
-
334
-    /**
335
-     * Return the module code for a BE module.
336
-     *
337
-     * @return string
338
-     */
339
-    public function getSignature(): string
340
-    {
341
-        $signature = GeneralUtility::_GP(Parameter::MODULE);
342
-        $trimmedSignature = trim($signature, '/');
343
-        return str_replace(['/', 'module_'], ['_', ''], $trimmedSignature);
344
-    }
345
-
346
-    /**
347
-     * Returns the current pid.
348
-     *
349
-     * @return int
350
-     */
351
-    public function getCurrentPid(): int
352
-    {
353
-        return GeneralUtility::_GET(Parameter::PID) > 0 ? (int)GeneralUtility::_GET(Parameter::PID) : 0;
354
-    }
355
-
356
-    /**
357
-     * Return the module URL.
358
-     *
359
-     * @param array $additionalParameters
360
-     * @return string
361
-     */
362
-    public function getModuleUrl(array $additionalParameters = []): string
363
-    {
364
-        $moduleCode = $this->getSignature();
365
-
366
-        // And don't forget the pid!
367
-        if (GeneralUtility::_GET(Parameter::PID)) {
368
-            $additionalParameters[Parameter::PID] = GeneralUtility::_GET(Parameter::PID);
369
-        }
370
-
371
-        return BackendUtility::getModuleUrl($moduleCode, $additionalParameters);
372
-    }
373
-
374
-    /**
375
-     * Return the parameter prefix for a BE module.
376
-     *
377
-     * @return string
378
-     */
379
-    public function getParameterPrefix(): string
380
-    {
381
-        return 'tx_vidi_' . strtolower($this->getSignature());
382
-    }
383
-
384
-    /**
385
-     * Return a configuration key or the entire module configuration array if not key is given.
386
-     *
387
-     * @param string $key
388
-     * @return mixed
389
-     */
390
-    public function getModuleConfiguration($key = '')
391
-    {
392
-
393
-        $vidiModuleCode = $this->getSignature();
394
-
395
-        // Module code must exist
396
-        if (empty($GLOBALS['TBE_MODULES_EXT']['vidi'][$vidiModuleCode])) {
397
-            $message = sprintf('Invalid or not existing module code "%s"', $vidiModuleCode);
398
-            throw new InvalidKeyInArrayException($message, 1375092053);
399
-        }
400
-
401
-        $result = $GLOBALS['TBE_MODULES_EXT']['vidi'][$vidiModuleCode];
402
-
403
-        if (!empty($key)) {
404
-            if (isset($result[$key])) {
405
-                $result = $result[$key];
406
-            } else {
407
-                // key must exist
408
-                $message = sprintf('Invalid key configuration "%s"', $key);
409
-                throw new InvalidKeyInArrayException($message, 1375092054);
410
-            }
411
-        }
412
-        return $result;
413
-    }
414
-
415
-    /**
416
-     * @param string $icon
417
-     * @return $this
418
-     */
419
-    public function setIcon($icon): self
420
-    {
421
-        $this->icon = $icon;
422
-        return $this;
423
-    }
424
-
425
-    /**
426
-     * @return string
427
-     */
428
-    protected function getIcon(): string
429
-    {
430
-        $moduleConfiguration = $this->getExistingMainConfiguration();
431
-
432
-
433
-        if ($this->icon) {
434
-            $icon = $this->icon;
435
-        } elseif ($moduleConfiguration['icon']) { // existing configuration may override.
436
-            $icon = $moduleConfiguration['icon'];
437
-        } else {
438
-            $icon = ''; //default value.
439
-        }
440
-
441
-        return $icon;
442
-    }
443
-
444
-    /**
445
-     * @param string $mainModule
446
-     * @return $this
447
-     */
448
-    public function setMainModule($mainModule): self
449
-    {
450
-        $this->mainModule = $mainModule;
451
-        return $this;
452
-    }
453
-
454
-    /**
455
-     * @return string
456
-     */
457
-    public function getMainModule(): string
458
-    {
459
-        if ($this->mainModule === null) {
460
-            $this->mainModule = $this->getModuleConfiguration('mainModule');
461
-        }
462
-        return $this->mainModule;
463
-    }
464
-
465
-    /**
466
-     * @param string $moduleLanguageFile
467
-     * @return $this
468
-     */
469
-    public function setModuleLanguageFile($moduleLanguageFile): self
470
-    {
471
-        $this->moduleLanguageFile = $moduleLanguageFile;
472
-        return $this;
473
-    }
474
-
475
-    /**
476
-     * @param string $component
477
-     * @return $this
478
-     */
479
-    public function removeComponentFromDocHeader(string $component): self
480
-    {
481
-        foreach ($this->components[ModulePosition::DOC_HEADER] as $verticalPosition => $docHeaders) {
482
-            foreach ($docHeaders as $horizontalPosition => $docHeader) {
483
-
484
-                $index = array_search($component, $docHeader, true);
485
-                if ($index !== false) {
486
-                    // $verticalPosition: top or bottom
487
-                    // $horizontalPosition: left or right
488
-                    unset($this->components[ModulePosition::DOC_HEADER][$verticalPosition][$horizontalPosition][$index]);
489
-                }
490
-            }
491
-        }
492
-        return $this;
493
-    }
494
-
495
-    /**
496
-     * @param bool $isPidIgnored
497
-     * @return $this
498
-     */
499
-    public function ignorePid(bool $isPidIgnored): self
500
-    {
501
-        $this->isPidIgnored = $isPidIgnored;
502
-        return $this;
503
-    }
504
-
505
-    /**
506
-     * @return bool
507
-     */
508
-    public function isPidIgnored(): bool
509
-    {
510
-        return $this->getModuleConfiguration('isPidIgnored');
511
-    }
512
-
513
-    /**
514
-     * @param string $component
515
-     * @return bool
516
-     */
517
-    public function hasComponentInDocHeader(string $component): bool
518
-    {
519
-        foreach ($this->getModuleConfiguration('components')[ModulePosition::DOC_HEADER] as $verticalPosition => $docHeaders) {
520
-            foreach ($docHeaders as $horizontalPosition => $docHeader) {
521
-
522
-                $index = array_search($component, $docHeader, true);
523
-                if ($index !== false) {
524
-                    return true;
525
-                }
526
-            }
527
-        }
528
-        return false;
529
-    }
530
-
531
-    /**
532
-     * @return string
533
-     */
534
-    protected function getModuleLanguageFile(): string
535
-    {
536
-        $moduleConfiguration = $this->getExistingMainConfiguration();
537
-
538
-        if ($this->moduleLanguageFile) {
539
-            $moduleLanguageFile = $this->moduleLanguageFile;
540
-        } elseif ($moduleConfiguration['labels']) { // existing configuration may override.
541
-            $moduleLanguageFile = $moduleConfiguration['labels'];
542
-        } else {
543
-            $moduleLanguageFile = ''; //default value.
544
-        }
545
-
546
-        return $moduleLanguageFile;
547
-    }
548
-
549
-    /**
550
-     * @param string $position
551
-     * @return $this
552
-     */
553
-    public function setPosition($position): self
554
-    {
555
-        $this->position = $position;
556
-        return $this;
557
-    }
558
-
559
-    /**
560
-     * @return string
561
-     */
562
-    public function getPosition(): string
563
-    {
564
-        return $this->position;
565
-    }
566
-
567
-    /**
568
-     * @param array $files
569
-     * @return $this
570
-     */
571
-    public function addJavaScriptFiles(array $files): self
572
-    {
573
-        foreach ($files as $file) {
574
-            $this->additionalJavaScriptFiles[] = $file;
575
-        }
576
-        return $this;
577
-    }
578
-
579
-    /**
580
-     * @param string $fileNameAndPath
581
-     * @return $this
582
-     */
583
-    public function addJavaScriptFile($fileNameAndPath): self
584
-    {
585
-        $this->additionalJavaScriptFiles[] = $fileNameAndPath;
586
-        return $this;
587
-    }
588
-
589
-    /**
590
-     * @param array $files
591
-     * @return $this
592
-     */
593
-    public function addStyleSheetFiles(array $files): self
594
-    {
595
-        foreach ($files as $file) {
596
-            $this->additionalStyleSheetFiles[] = $file;
597
-        }
598
-        return $this;
599
-    }
600
-
601
-    /**
602
-     * @param string $fileNameAndPath
603
-     * @return $this
604
-     */
605
-    public function addStyleSheetFile($fileNameAndPath): self
606
-    {
607
-        $this->additionalStyleSheetFiles[] = $fileNameAndPath;
608
-        return $this;
609
-    }
610
-
611
-    /**
612
-     * @return string
613
-     */
614
-    public function getDataType(): string
615
-    {
616
-        if ($this->dataType === null) {
617
-            $this->dataType = $this->getModuleConfiguration('dataType');
618
-        }
619
-        return $this->dataType;
620
-    }
621
-
622
-    /**
623
-     * @return array
624
-     */
625
-    public function getDataTypes(): array
626
-    {
627
-        $dataTypes = [];
628
-        foreach ($GLOBALS['TBE_MODULES_EXT']['vidi'] as $module) {
629
-            $dataTypes[] = $module['dataType'];
630
-        }
631
-        return $dataTypes;
632
-    }
633
-
634
-    /**
635
-     * @param string $dataType
636
-     * @return $this
637
-     */
638
-    public function setDataType($dataType): self
639
-    {
640
-        $this->dataType = $dataType;
641
-        return $this;
642
-    }
643
-
644
-    /**
645
-     * @return int
646
-     */
647
-    public function getDefaultPid(): int
648
-    {
649
-        if (empty($this->defaultPid)) {
650
-            $this->defaultPid = $this->getModuleConfiguration('defaultPid');
651
-        }
652
-        return (int)$this->defaultPid;
653
-    }
654
-
655
-    /**
656
-     * @param string $defaultPid
657
-     * @return $this
658
-     */
659
-    public function setDefaultPid($defaultPid): self
660
-    {
661
-        $this->defaultPid = $defaultPid;
662
-        return $this;
663
-    }
664
-
665
-    /**
666
-     * @param bool $isPageTreeShown
667
-     * @return $this
668
-     */
669
-    public function showPageTree($isPageTreeShown): self
670
-    {
671
-        $this->showPageTree = $isPageTreeShown;
672
-        return $this;
673
-    }
674
-
675
-    /**
676
-     * @param string $isShown
677
-     * @return $this
678
-     */
679
-    public function isShown($isShown): self
680
-    {
681
-        $this->isShown = $isShown;
682
-        return $this;
683
-    }
684
-
685
-    /**
686
-     * @return $array
687
-     */
688
-    public function getDocHeaderTopLeftComponents()
689
-    {
690
-        $configuration = $this->getModuleConfiguration();
691
-        return $configuration['components'][ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::LEFT];
692
-    }
693
-
694
-    /**
695
-     * @param array $components
696
-     * @return $this
697
-     */
698
-    public function setDocHeaderTopLeftComponents(array $components): self
699
-    {
700
-        $this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::LEFT] = $components;
701
-        return $this;
702
-    }
703
-
704
-    /**
705
-     * @param string|array $components
706
-     * @return $this
707
-     */
708
-    public function addDocHeaderTopLeftComponents($components): self
709
-    {
710
-        if (is_string($components)) {
711
-            $components = [$components];
712
-        }
713
-        $currentComponents = $this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::LEFT];
714
-        $this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::LEFT] = array_merge($currentComponents, $components);
715
-        return $this;
716
-    }
717
-
718
-    /**
719
-     * @return $array
720
-     */
721
-    public function getDocHeaderTopRightComponents()
722
-    {
723
-        $configuration = $this->getModuleConfiguration();
724
-        return $configuration['components'][ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::RIGHT];
725
-    }
726
-
727
-    /**
728
-     * @param array $components
729
-     * @return $this
730
-     */
731
-    public function setDocHeaderTopRightComponents(array $components): self
732
-    {
733
-        $this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::RIGHT] = $components;
734
-        return $this;
735
-    }
736
-
737
-    /**
738
-     * @param string|array $components
739
-     * @return $this
740
-     */
741
-    public function addDocHeaderTopRightComponents($components): self
742
-    {
743
-        if (is_string($components)) {
744
-            $components = [$components];
745
-        }
746
-        $currentComponents = $this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::RIGHT];
747
-        $this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::RIGHT] = array_merge($currentComponents, $components);
748
-        return $this;
749
-    }
750
-
751
-    /**
752
-     * @return $array
753
-     */
754
-    public function getDocHeaderBottomLeftComponents()
755
-    {
756
-        $configuration = $this->getModuleConfiguration();
757
-        return $configuration['components'][ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::LEFT];
758
-    }
759
-
760
-    /**
761
-     * @param array $components
762
-     * @return $this
763
-     */
764
-    public function setDocHeaderBottomLeftComponents(array $components): self
765
-    {
766
-        $this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::LEFT] = $components;
767
-        return $this;
768
-    }
769
-
770
-    /**
771
-     * @param string|array $components
772
-     * @return $this
773
-     */
774
-    public function addDocHeaderBottomLeftComponents($components): self
775
-    {
776
-        if (is_string($components)) {
777
-            $components = [$components];
778
-        }
779
-        $currentComponents = $this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::LEFT];
780
-        $this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::LEFT] = array_merge($currentComponents, $components);
781
-        return $this;
782
-    }
783
-
784
-    /**
785
-     * @return $array
786
-     */
787
-    public function getDocHeaderBottomRightComponents()
788
-    {
789
-        $configuration = $this->getModuleConfiguration();
790
-        return $configuration['components'][ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::RIGHT];
791
-    }
792
-
793
-    /**
794
-     * @param array $components
795
-     * @return $this
796
-     */
797
-    public function setDocHeaderBottomRightComponents(array $components): self
798
-    {
799
-        $this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::RIGHT] = $components;
800
-        return $this;
801
-    }
802
-
803
-    /**
804
-     * @param string|array $components
805
-     * @return $this
806
-     */
807
-    public function addDocHeaderBottomRightComponents($components): self
808
-    {
809
-        if (is_string($components)) {
810
-            $components = [$components];
811
-        }
812
-        $currentComponents = $this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::RIGHT];
813
-        $this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::RIGHT] = array_merge($currentComponents, $components);
814
-        return $this;
815
-    }
816
-
817
-    /**
818
-     * @return $array
819
-     */
820
-    public function getGridTopComponents()
821
-    {
822
-        $configuration = $this->getModuleConfiguration();
823
-        return $configuration['components'][ModulePosition::GRID][ModulePosition::TOP];
824
-    }
825
-
826
-    /**
827
-     * @param array $components
828
-     * @return $this
829
-     */
830
-    public function setGridTopComponents(array $components): self
831
-    {
832
-        $this->components[ModulePosition::GRID][ModulePosition::TOP] = $components;
833
-        return $this;
834
-    }
835
-
836
-    /**
837
-     * @param string|array $components
838
-     * @return $this
839
-     */
840
-    public function addGridTopComponents($components): self
841
-    {
842
-        if (is_string($components)) {
843
-            $components = [$components];
844
-        }
845
-        $currentComponents = $this->components[ModulePosition::GRID][ModulePosition::TOP];
846
-        $this->components[ModulePosition::GRID][ModulePosition::TOP] = array_merge($currentComponents, $components);
847
-        return $this;
848
-    }
849
-
850
-    /**
851
-     * @return mixed $array
852
-     */
853
-    public function getGridBottomComponents()
854
-    {
855
-        $configuration = $this->getModuleConfiguration();
856
-        return $configuration['components'][ModulePosition::GRID][ModulePosition::BOTTOM];
857
-    }
858
-
859
-    /**
860
-     * @param array $components
861
-     * @return $this
862
-     */
863
-    public function setGridBottomComponents(array $components): self
864
-    {
865
-        $this->components[ModulePosition::GRID][ModulePosition::BOTTOM] = $components;
866
-        return $this;
867
-    }
868
-
869
-    /**
870
-     * @param string|array $components
871
-     * @return $this
872
-     */
873
-    public function addGridBottomComponents($components): self
874
-    {
875
-        if (is_string($components)) {
876
-            $components = [$components];
877
-        }
878
-        $currentComponents = $this->components[ModulePosition::GRID][ModulePosition::BOTTOM];
879
-        $this->components[ModulePosition::GRID][ModulePosition::BOTTOM] = array_merge($currentComponents, $components);
880
-        return $this;
881
-    }
882
-
883
-    /**
884
-     * @return $array
885
-     */
886
-    public function getGridButtonsComponents()
887
-    {
888
-        $configuration = $this->getModuleConfiguration();
889
-        return $configuration['components'][ModulePosition::GRID][ModulePosition::BUTTONS];
890
-    }
891
-
892
-    /**
893
-     * @param array $components
894
-     * @return $this
895
-     */
896
-    public function setGridButtonsComponents(array $components): self
897
-    {
898
-        $this->components[ModulePosition::GRID][ModulePosition::BUTTONS] = $components;
899
-        return $this;
900
-    }
901
-
902
-    /**
903
-     * @param string|array $components
904
-     * @return $this
905
-     */
906
-    public function addGridButtonsComponents($components): self
907
-    {
908
-        if (is_string($components)) {
909
-            $components = [$components];
910
-        }
911
-        $currentComponents = $this->components[ModulePosition::GRID][ModulePosition::BUTTONS];
912
-        $this->components[ModulePosition::GRID][ModulePosition::BUTTONS] = array_merge($components, $currentComponents);
913
-        return $this;
914
-    }
915
-
916
-    /**
917
-     * @return $array
918
-     */
919
-    public function getMenuMassActionComponents()
920
-    {
921
-        $configuration = $this->getModuleConfiguration();
922
-        return $configuration['components'][ModulePosition::MENU_MASS_ACTION];
923
-    }
924
-
925
-    /**
926
-     * @param array $components
927
-     * @return $this
928
-     */
929
-    public function setMenuMassActionComponents(array $components): self
930
-    {
931
-        $this->components[ModulePosition::MENU_MASS_ACTION] = $components;
932
-        return $this;
933
-    }
934
-
935
-    /**
936
-     * @param string|array $components
937
-     * @return $this
938
-     */
939
-    public function addMenuMassActionComponents($components): self
940
-    {
941
-        if (is_string($components)) {
942
-            $components = [$components];
943
-        }
944
-        $currentComponents = $this->components[ModulePosition::MENU_MASS_ACTION];
945
-        $this->components[ModulePosition::MENU_MASS_ACTION] = array_merge($components, $currentComponents);
946
-        return $this;
947
-    }
948
-
949
-    /**
950
-     * @return string
951
-     */
952
-    protected function getAccess(): string
953
-    {
954
-        $moduleConfiguration = $this->getExistingMainConfiguration();
955
-
956
-        if ($this->access !== null) {
957
-            $access = $this->access;
958
-        } elseif (!empty($moduleConfiguration['access'])) { // existing configuration may override.
959
-            $access = $moduleConfiguration['access'];
960
-        } else {
961
-            $access = Access::USER; //default value.
962
-        }
963
-        return $access;
964
-    }
965
-
966
-    /**
967
-     * @param string $access
968
-     * @return $this
969
-     */
970
-    public function setAccess($access): self
971
-    {
972
-        $this->access = $access;
973
-        return $this;
974
-    }
975
-
976
-    /**
977
-     * @return \string[]
978
-     */
979
-    public function getAdditionalJavaScriptFiles(): array
980
-    {
981
-        if (empty($this->additionalJavaScriptFiles)) {
982
-            $this->additionalJavaScriptFiles = $this->getModuleConfiguration('additionalJavaScriptFiles');
983
-        }
984
-        return $this->additionalJavaScriptFiles;
985
-    }
986
-
987
-    /**
988
-     * @return \string[]
989
-     */
990
-    public function getAdditionalStyleSheetFiles(): array
991
-    {
992
-        if (empty($this->additionalStyleSheetFiles)) {
993
-            $this->additionalStyleSheetFiles = $this->getModuleConfiguration('additionalStyleSheetFiles');
994
-        }
995
-        return $this->additionalStyleSheetFiles;
996
-    }
997
-
998
-    /**
999
-     * @return array
1000
-     */
1001
-    public function getComponents(): array
1002
-    {
1003
-        return $this->components;
1004
-    }
1005
-
1006
-    /**
1007
-     * @param string $pluginName
1008
-     * @return bool
1009
-     */
1010
-    public function hasPlugin($pluginName = ''): bool
1011
-    {
1012
-        $parameterPrefix = $this->getParameterPrefix();
1013
-        $parameters = GeneralUtility::_GET($parameterPrefix);
1014
-
1015
-        $hasPlugin = !empty($parameters['plugins']) && is_array($parameters['plugins']);
1016
-        if ($hasPlugin && $pluginName) {
1017
-            $hasPlugin = in_array($pluginName, $parameters['plugins']);
1018
-        }
1019
-        return $hasPlugin;
1020
-    }
1021
-
1022
-    /**
1023
-     * Compute the internal module code
1024
-     *
1025
-     * @param null|string $dataType
1026
-     * @return string
1027
-     */
1028
-    protected function getInternalModuleSignature($dataType = null): string
1029
-    {
1030
-        // Else we forge the module signature
1031
-        if ($dataType === null) {
1032
-            $dataType = $this->dataType;
1033
-        }
1034
-        $subModuleName = $dataType . '_' . $this->moduleKey;
1035
-
1036
-        $mainModule = $this->mainModule ?: self::DEFAULT_MAIN_MODULE;
1037
-        return $mainModule . '_Vidi' . GeneralUtility::underscoredToUpperCamelCase($subModuleName);
1038
-    }
40
+	/**
41
+	 * Define the default main module
42
+	 */
43
+	const DEFAULT_MAIN_MODULE = 'content';
44
+
45
+	/**
46
+	 * Define the default pid
47
+	 */
48
+	const DEFAULT_PID = 0;
49
+
50
+	/**
51
+	 * The type of data being listed (which corresponds to a table name in TCA)
52
+	 *
53
+	 * @var string
54
+	 */
55
+	protected $dataType;
56
+
57
+	/**
58
+	 * @var string
59
+	 */
60
+	protected $defaultPid;
61
+
62
+	/**
63
+	 * @var bool
64
+	 */
65
+	protected $isPidIgnored = false;
66
+
67
+	/**
68
+	 * @var bool
69
+	 */
70
+	protected $showPageTree = false;
71
+
72
+	/**
73
+	 * @var bool
74
+	 */
75
+	protected $isShown = true;
76
+
77
+	/**
78
+	 * @var string
79
+	 */
80
+	protected $access;
81
+
82
+	/**
83
+	 * @var string
84
+	 */
85
+	protected $mainModule;
86
+
87
+	/**
88
+	 * @var string
89
+	 */
90
+	protected $position = '';
91
+
92
+	/**
93
+	 * @var string
94
+	 */
95
+	protected $icon;
96
+
97
+	/**
98
+	 * @var string
99
+	 */
100
+	protected $moduleLanguageFile;
101
+
102
+	/**
103
+	 * The module key such as m1, m2.
104
+	 *
105
+	 * @var string
106
+	 */
107
+	protected $moduleKey = 'm1';
108
+
109
+	/**
110
+	 * @var string[]
111
+	 */
112
+	protected $additionalJavaScriptFiles = [];
113
+
114
+	/**
115
+	 * @var string[]
116
+	 */
117
+	protected $additionalStyleSheetFiles = [];
118
+
119
+	/**
120
+	 * @var array
121
+	 */
122
+	protected $components = [];
123
+
124
+	/**
125
+	 * @param string $dataType
126
+	 */
127
+	public function __construct($dataType = null)
128
+	{
129
+		$this->dataType = $dataType;
130
+
131
+		// Initialize components
132
+		$this->components = [
133
+			ModulePosition::DOC_HEADER => [
134
+				ModulePosition::TOP => [
135
+					ModulePosition::LEFT => [],
136
+					ModulePosition::RIGHT => [
137
+						ToolButton::class,
138
+					],
139
+				],
140
+				ModulePosition::BOTTOM => [
141
+					ModulePosition::LEFT => [
142
+						NewButton::class,
143
+						BackViewHelper::class,
144
+					],
145
+					ModulePosition::RIGHT => [],
146
+				],
147
+			],
148
+			ModulePosition::GRID => [
149
+				ModulePosition::TOP => [
150
+					RelationsCheck::class,
151
+					#\Fab\Vidi\View\Tab\DataTypeTab::class,
152
+				],
153
+				ModulePosition::BUTTONS => [
154
+					EditButton::class,
155
+					DeleteButton::class,
156
+				],
157
+				ModulePosition::BOTTOM => [],
158
+			],
159
+			ModulePosition::MENU_MASS_ACTION => [
160
+				ExportXlsMenuItem::class,
161
+				ExportXmlMenuItem::class,
162
+				ExportCsvMenuItem::class,
163
+				DividerMenuItem::class,
164
+				MassDeleteMenuItem::class,
165
+				#\Fab\Vidi\View\MenuItem\MassEditMenuItem::class,
166
+			],
167
+		];
168
+	}
169
+
170
+	/**
171
+	 * Tell whether a module is already registered.
172
+	 *
173
+	 * @param string $dataType
174
+	 * @return bool
175
+	 */
176
+	public function isRegistered($dataType): bool
177
+	{
178
+		$internalModuleSignature = $this->getInternalModuleSignature($dataType);
179
+		return !empty($GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature]);
180
+	}
181
+
182
+	/**
183
+	 * @return array
184
+	 */
185
+	protected function getExistingInternalConfiguration(): array
186
+	{
187
+		$internalModuleSignature = $this->getInternalModuleSignature();
188
+		return $GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature] ?? [];
189
+	}
190
+
191
+	/**
192
+	 * @return array
193
+	 */
194
+	protected function getExistingMainConfiguration(): array
195
+	{
196
+		$moduleSignature = $this->computeMainModule() . '_' . $this->getInternalModuleSignature();
197
+		return $GLOBALS['TBE_MODULES']['_configuration'][$moduleSignature] ?? [];
198
+	}
199
+
200
+	/**
201
+	 * @return string
202
+	 */
203
+	protected function computeMainModule(): string
204
+	{
205
+		$existingConfiguration = $this->getExistingInternalConfiguration();
206
+
207
+		if ($this->mainModule !== null) {
208
+			$mainModule = $this->mainModule;
209
+		} elseif (!empty($existingConfiguration['mainModule'])) { // existing configuration may override.
210
+			$mainModule = $existingConfiguration['mainModule'];
211
+		} else {
212
+			$mainModule = self::DEFAULT_MAIN_MODULE; //default value.
213
+		}
214
+		return $mainModule;
215
+	}
216
+
217
+	/**
218
+	 * @return string
219
+	 */
220
+	protected function computeDefaultPid(): string
221
+	{
222
+		$existingConfiguration = $this->getExistingInternalConfiguration();
223
+
224
+		if ($this->defaultPid !== null) {
225
+			$defaultPid = $this->defaultPid;
226
+		} elseif (!empty($existingConfiguration['defaultPid'])) { // existing configuration may override.
227
+			$defaultPid = $existingConfiguration['defaultPid'];
228
+		} else {
229
+			$defaultPid = self::DEFAULT_PID; //default value.
230
+		}
231
+		return $defaultPid;
232
+	}
233
+
234
+	/**
235
+	 * @return array
236
+	 */
237
+	protected function computeAdditionalJavaScriptFiles(): array
238
+	{
239
+		$additionalJavaScriptFiles = $this->additionalJavaScriptFiles;
240
+
241
+		// Possible merge of existing javascript files.
242
+		$existingConfiguration = $this->getExistingInternalConfiguration();
243
+		if (!empty($existingConfiguration['additionalJavaScriptFiles'])) {
244
+			$additionalJavaScriptFiles = array_merge($additionalJavaScriptFiles, $existingConfiguration['additionalJavaScriptFiles']);
245
+		}
246
+
247
+		return $additionalJavaScriptFiles;
248
+	}
249
+
250
+	/**
251
+	 * @return array
252
+	 */
253
+	protected function computeAdditionalStyleSheetFiles(): array
254
+	{
255
+		$additionalStyleSheetFiles = $this->additionalStyleSheetFiles;
256
+
257
+		// Possible merge of existing style sheets.
258
+		$existingConfiguration = $this->getExistingInternalConfiguration();
259
+		if (!empty($existingConfiguration['additionalStyleSheetFiles'])) {
260
+			$additionalStyleSheetFiles = array_merge($additionalStyleSheetFiles, $existingConfiguration['additionalStyleSheetFiles']);
261
+		}
262
+
263
+		return $additionalStyleSheetFiles;
264
+	}
265
+
266
+	/**
267
+	 * @return array
268
+	 */
269
+	protected function computeComponents(): array
270
+	{
271
+		// We override the config in any case. See if we need more than that.
272
+		return $this->components;
273
+	}
274
+
275
+	/**
276
+	 * Register the module in two places: core + vidi internal.
277
+	 *
278
+	 * @return $this
279
+	 */
280
+	public function register(): self
281
+	{
282
+		// Internal Vidi module registration.
283
+		$configuration = [];
284
+		$configuration['dataType'] = $this->dataType;
285
+		$configuration['mainModule'] = $this->computeMainModule();
286
+		$configuration['defaultPid'] = $this->computeDefaultPid();
287
+		$configuration['additionalJavaScriptFiles'] = $this->computeAdditionalJavaScriptFiles();
288
+		$configuration['additionalStyleSheetFiles'] = $this->computeAdditionalStyleSheetFiles();
289
+		$configuration['components'] = $this->computeComponents();
290
+		$configuration['isPidIgnored'] = $this->isPidIgnored;
291
+
292
+		$internalModuleSignature = $this->getInternalModuleSignature();
293
+		$GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature] = $configuration;
294
+
295
+		// Core module registration.
296
+		// Register and displays module in the BE only if told, default is "true".
297
+		if ($this->isShown) {
298
+
299
+			$moduleConfiguration = [];
300
+			#$moduleConfiguration['routeTarget'] = \Fab\Vidi\Controller\ContentController::class . '::mainAction', // what to do here?
301
+			$moduleConfiguration['access'] = $this->getAccess();
302
+			$moduleConfiguration['labels'] = $this->getModuleLanguageFile();
303
+			$icon = $this->getIcon();
304
+			if ($icon) {
305
+				$moduleConfiguration['icon'] = $icon;
306
+			}
307
+
308
+			if ($this->showPageTree) {
309
+				$moduleConfiguration['navigationComponentId'] = 'TYPO3/CMS/Backend/PageTree/PageTreeElement';
310
+				$moduleConfiguration['inheritNavigationComponentFromMainModule'] = true;
311
+			} else {
312
+				$moduleConfiguration['inheritNavigationComponentFromMainModule'] = true;
313
+			}
314
+
315
+			ExtensionUtility::registerModule(
316
+				'Fab.vidi',
317
+				$this->computeMainModule(),
318
+				$this->dataType . '_' . $this->moduleKey,
319
+				$this->position,
320
+				[
321
+					ContentController::class => 'index, list, delete, update, edit, copy, move, localize, sort, copyClipboard, moveClipboard',
322
+					ToolController::class => 'welcome, work',
323
+					FacetController::class => 'autoSuggest, autoSuggests',
324
+					SelectionController::class => 'edit, update, create, delete, list, show',
325
+					UserPreferencesController::class => 'save',
326
+					ClipboardController::class => 'save, flush, show',
327
+				],
328
+				$moduleConfiguration
329
+			);
330
+		}
331
+		return $this;
332
+	}
333
+
334
+	/**
335
+	 * Return the module code for a BE module.
336
+	 *
337
+	 * @return string
338
+	 */
339
+	public function getSignature(): string
340
+	{
341
+		$signature = GeneralUtility::_GP(Parameter::MODULE);
342
+		$trimmedSignature = trim($signature, '/');
343
+		return str_replace(['/', 'module_'], ['_', ''], $trimmedSignature);
344
+	}
345
+
346
+	/**
347
+	 * Returns the current pid.
348
+	 *
349
+	 * @return int
350
+	 */
351
+	public function getCurrentPid(): int
352
+	{
353
+		return GeneralUtility::_GET(Parameter::PID) > 0 ? (int)GeneralUtility::_GET(Parameter::PID) : 0;
354
+	}
355
+
356
+	/**
357
+	 * Return the module URL.
358
+	 *
359
+	 * @param array $additionalParameters
360
+	 * @return string
361
+	 */
362
+	public function getModuleUrl(array $additionalParameters = []): string
363
+	{
364
+		$moduleCode = $this->getSignature();
365
+
366
+		// And don't forget the pid!
367
+		if (GeneralUtility::_GET(Parameter::PID)) {
368
+			$additionalParameters[Parameter::PID] = GeneralUtility::_GET(Parameter::PID);
369
+		}
370
+
371
+		return BackendUtility::getModuleUrl($moduleCode, $additionalParameters);
372
+	}
373
+
374
+	/**
375
+	 * Return the parameter prefix for a BE module.
376
+	 *
377
+	 * @return string
378
+	 */
379
+	public function getParameterPrefix(): string
380
+	{
381
+		return 'tx_vidi_' . strtolower($this->getSignature());
382
+	}
383
+
384
+	/**
385
+	 * Return a configuration key or the entire module configuration array if not key is given.
386
+	 *
387
+	 * @param string $key
388
+	 * @return mixed
389
+	 */
390
+	public function getModuleConfiguration($key = '')
391
+	{
392
+
393
+		$vidiModuleCode = $this->getSignature();
394
+
395
+		// Module code must exist
396
+		if (empty($GLOBALS['TBE_MODULES_EXT']['vidi'][$vidiModuleCode])) {
397
+			$message = sprintf('Invalid or not existing module code "%s"', $vidiModuleCode);
398
+			throw new InvalidKeyInArrayException($message, 1375092053);
399
+		}
400
+
401
+		$result = $GLOBALS['TBE_MODULES_EXT']['vidi'][$vidiModuleCode];
402
+
403
+		if (!empty($key)) {
404
+			if (isset($result[$key])) {
405
+				$result = $result[$key];
406
+			} else {
407
+				// key must exist
408
+				$message = sprintf('Invalid key configuration "%s"', $key);
409
+				throw new InvalidKeyInArrayException($message, 1375092054);
410
+			}
411
+		}
412
+		return $result;
413
+	}
414
+
415
+	/**
416
+	 * @param string $icon
417
+	 * @return $this
418
+	 */
419
+	public function setIcon($icon): self
420
+	{
421
+		$this->icon = $icon;
422
+		return $this;
423
+	}
424
+
425
+	/**
426
+	 * @return string
427
+	 */
428
+	protected function getIcon(): string
429
+	{
430
+		$moduleConfiguration = $this->getExistingMainConfiguration();
431
+
432
+
433
+		if ($this->icon) {
434
+			$icon = $this->icon;
435
+		} elseif ($moduleConfiguration['icon']) { // existing configuration may override.
436
+			$icon = $moduleConfiguration['icon'];
437
+		} else {
438
+			$icon = ''; //default value.
439
+		}
440
+
441
+		return $icon;
442
+	}
443
+
444
+	/**
445
+	 * @param string $mainModule
446
+	 * @return $this
447
+	 */
448
+	public function setMainModule($mainModule): self
449
+	{
450
+		$this->mainModule = $mainModule;
451
+		return $this;
452
+	}
453
+
454
+	/**
455
+	 * @return string
456
+	 */
457
+	public function getMainModule(): string
458
+	{
459
+		if ($this->mainModule === null) {
460
+			$this->mainModule = $this->getModuleConfiguration('mainModule');
461
+		}
462
+		return $this->mainModule;
463
+	}
464
+
465
+	/**
466
+	 * @param string $moduleLanguageFile
467
+	 * @return $this
468
+	 */
469
+	public function setModuleLanguageFile($moduleLanguageFile): self
470
+	{
471
+		$this->moduleLanguageFile = $moduleLanguageFile;
472
+		return $this;
473
+	}
474
+
475
+	/**
476
+	 * @param string $component
477
+	 * @return $this
478
+	 */
479
+	public function removeComponentFromDocHeader(string $component): self
480
+	{
481
+		foreach ($this->components[ModulePosition::DOC_HEADER] as $verticalPosition => $docHeaders) {
482
+			foreach ($docHeaders as $horizontalPosition => $docHeader) {
483
+
484
+				$index = array_search($component, $docHeader, true);
485
+				if ($index !== false) {
486
+					// $verticalPosition: top or bottom
487
+					// $horizontalPosition: left or right
488
+					unset($this->components[ModulePosition::DOC_HEADER][$verticalPosition][$horizontalPosition][$index]);
489
+				}
490
+			}
491
+		}
492
+		return $this;
493
+	}
494
+
495
+	/**
496
+	 * @param bool $isPidIgnored
497
+	 * @return $this
498
+	 */
499
+	public function ignorePid(bool $isPidIgnored): self
500
+	{
501
+		$this->isPidIgnored = $isPidIgnored;
502
+		return $this;
503
+	}
504
+
505
+	/**
506
+	 * @return bool
507
+	 */
508
+	public function isPidIgnored(): bool
509
+	{
510
+		return $this->getModuleConfiguration('isPidIgnored');
511
+	}
512
+
513
+	/**
514
+	 * @param string $component
515
+	 * @return bool
516
+	 */
517
+	public function hasComponentInDocHeader(string $component): bool
518
+	{
519
+		foreach ($this->getModuleConfiguration('components')[ModulePosition::DOC_HEADER] as $verticalPosition => $docHeaders) {
520
+			foreach ($docHeaders as $horizontalPosition => $docHeader) {
521
+
522
+				$index = array_search($component, $docHeader, true);
523
+				if ($index !== false) {
524
+					return true;
525
+				}
526
+			}
527
+		}
528
+		return false;
529
+	}
530
+
531
+	/**
532
+	 * @return string
533
+	 */
534
+	protected function getModuleLanguageFile(): string
535
+	{
536
+		$moduleConfiguration = $this->getExistingMainConfiguration();
537
+
538
+		if ($this->moduleLanguageFile) {
539
+			$moduleLanguageFile = $this->moduleLanguageFile;
540
+		} elseif ($moduleConfiguration['labels']) { // existing configuration may override.
541
+			$moduleLanguageFile = $moduleConfiguration['labels'];
542
+		} else {
543
+			$moduleLanguageFile = ''; //default value.
544
+		}
545
+
546
+		return $moduleLanguageFile;
547
+	}
548
+
549
+	/**
550
+	 * @param string $position
551
+	 * @return $this
552
+	 */
553
+	public function setPosition($position): self
554
+	{
555
+		$this->position = $position;
556
+		return $this;
557
+	}
558
+
559
+	/**
560
+	 * @return string
561
+	 */
562
+	public function getPosition(): string
563
+	{
564
+		return $this->position;
565
+	}
566
+
567
+	/**
568
+	 * @param array $files
569
+	 * @return $this
570
+	 */
571
+	public function addJavaScriptFiles(array $files): self
572
+	{
573
+		foreach ($files as $file) {
574
+			$this->additionalJavaScriptFiles[] = $file;
575
+		}
576
+		return $this;
577
+	}
578
+
579
+	/**
580
+	 * @param string $fileNameAndPath
581
+	 * @return $this
582
+	 */
583
+	public function addJavaScriptFile($fileNameAndPath): self
584
+	{
585
+		$this->additionalJavaScriptFiles[] = $fileNameAndPath;
586
+		return $this;
587
+	}
588
+
589
+	/**
590
+	 * @param array $files
591
+	 * @return $this
592
+	 */
593
+	public function addStyleSheetFiles(array $files): self
594
+	{
595
+		foreach ($files as $file) {
596
+			$this->additionalStyleSheetFiles[] = $file;
597
+		}
598
+		return $this;
599
+	}
600
+
601
+	/**
602
+	 * @param string $fileNameAndPath
603
+	 * @return $this
604
+	 */
605
+	public function addStyleSheetFile($fileNameAndPath): self
606
+	{
607
+		$this->additionalStyleSheetFiles[] = $fileNameAndPath;
608
+		return $this;
609
+	}
610
+
611
+	/**
612
+	 * @return string
613
+	 */
614
+	public function getDataType(): string
615
+	{
616
+		if ($this->dataType === null) {
617
+			$this->dataType = $this->getModuleConfiguration('dataType');
618
+		}
619
+		return $this->dataType;
620
+	}
621
+
622
+	/**
623
+	 * @return array
624
+	 */
625
+	public function getDataTypes(): array
626
+	{
627
+		$dataTypes = [];
628
+		foreach ($GLOBALS['TBE_MODULES_EXT']['vidi'] as $module) {
629
+			$dataTypes[] = $module['dataType'];
630
+		}
631
+		return $dataTypes;
632
+	}
633
+
634
+	/**
635
+	 * @param string $dataType
636
+	 * @return $this
637
+	 */
638
+	public function setDataType($dataType): self
639
+	{
640
+		$this->dataType = $dataType;
641
+		return $this;
642
+	}
643
+
644
+	/**
645
+	 * @return int
646
+	 */
647
+	public function getDefaultPid(): int
648
+	{
649
+		if (empty($this->defaultPid)) {
650
+			$this->defaultPid = $this->getModuleConfiguration('defaultPid');
651
+		}
652
+		return (int)$this->defaultPid;
653
+	}
654
+
655
+	/**
656
+	 * @param string $defaultPid
657
+	 * @return $this
658
+	 */
659
+	public function setDefaultPid($defaultPid): self
660
+	{
661
+		$this->defaultPid = $defaultPid;
662
+		return $this;
663
+	}
664
+
665
+	/**
666
+	 * @param bool $isPageTreeShown
667
+	 * @return $this
668
+	 */
669
+	public function showPageTree($isPageTreeShown): self
670
+	{
671
+		$this->showPageTree = $isPageTreeShown;
672
+		return $this;
673
+	}
674
+
675
+	/**
676
+	 * @param string $isShown
677
+	 * @return $this
678
+	 */
679
+	public function isShown($isShown): self
680
+	{
681
+		$this->isShown = $isShown;
682
+		return $this;
683
+	}
684
+
685
+	/**
686
+	 * @return $array
687
+	 */
688
+	public function getDocHeaderTopLeftComponents()
689
+	{
690
+		$configuration = $this->getModuleConfiguration();
691
+		return $configuration['components'][ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::LEFT];
692
+	}
693
+
694
+	/**
695
+	 * @param array $components
696
+	 * @return $this
697
+	 */
698
+	public function setDocHeaderTopLeftComponents(array $components): self
699
+	{
700
+		$this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::LEFT] = $components;
701
+		return $this;
702
+	}
703
+
704
+	/**
705
+	 * @param string|array $components
706
+	 * @return $this
707
+	 */
708
+	public function addDocHeaderTopLeftComponents($components): self
709
+	{
710
+		if (is_string($components)) {
711
+			$components = [$components];
712
+		}
713
+		$currentComponents = $this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::LEFT];
714
+		$this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::LEFT] = array_merge($currentComponents, $components);
715
+		return $this;
716
+	}
717
+
718
+	/**
719
+	 * @return $array
720
+	 */
721
+	public function getDocHeaderTopRightComponents()
722
+	{
723
+		$configuration = $this->getModuleConfiguration();
724
+		return $configuration['components'][ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::RIGHT];
725
+	}
726
+
727
+	/**
728
+	 * @param array $components
729
+	 * @return $this
730
+	 */
731
+	public function setDocHeaderTopRightComponents(array $components): self
732
+	{
733
+		$this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::RIGHT] = $components;
734
+		return $this;
735
+	}
736
+
737
+	/**
738
+	 * @param string|array $components
739
+	 * @return $this
740
+	 */
741
+	public function addDocHeaderTopRightComponents($components): self
742
+	{
743
+		if (is_string($components)) {
744
+			$components = [$components];
745
+		}
746
+		$currentComponents = $this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::RIGHT];
747
+		$this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::RIGHT] = array_merge($currentComponents, $components);
748
+		return $this;
749
+	}
750
+
751
+	/**
752
+	 * @return $array
753
+	 */
754
+	public function getDocHeaderBottomLeftComponents()
755
+	{
756
+		$configuration = $this->getModuleConfiguration();
757
+		return $configuration['components'][ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::LEFT];
758
+	}
759
+
760
+	/**
761
+	 * @param array $components
762
+	 * @return $this
763
+	 */
764
+	public function setDocHeaderBottomLeftComponents(array $components): self
765
+	{
766
+		$this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::LEFT] = $components;
767
+		return $this;
768
+	}
769
+
770
+	/**
771
+	 * @param string|array $components
772
+	 * @return $this
773
+	 */
774
+	public function addDocHeaderBottomLeftComponents($components): self
775
+	{
776
+		if (is_string($components)) {
777
+			$components = [$components];
778
+		}
779
+		$currentComponents = $this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::LEFT];
780
+		$this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::LEFT] = array_merge($currentComponents, $components);
781
+		return $this;
782
+	}
783
+
784
+	/**
785
+	 * @return $array
786
+	 */
787
+	public function getDocHeaderBottomRightComponents()
788
+	{
789
+		$configuration = $this->getModuleConfiguration();
790
+		return $configuration['components'][ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::RIGHT];
791
+	}
792
+
793
+	/**
794
+	 * @param array $components
795
+	 * @return $this
796
+	 */
797
+	public function setDocHeaderBottomRightComponents(array $components): self
798
+	{
799
+		$this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::RIGHT] = $components;
800
+		return $this;
801
+	}
802
+
803
+	/**
804
+	 * @param string|array $components
805
+	 * @return $this
806
+	 */
807
+	public function addDocHeaderBottomRightComponents($components): self
808
+	{
809
+		if (is_string($components)) {
810
+			$components = [$components];
811
+		}
812
+		$currentComponents = $this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::RIGHT];
813
+		$this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::RIGHT] = array_merge($currentComponents, $components);
814
+		return $this;
815
+	}
816
+
817
+	/**
818
+	 * @return $array
819
+	 */
820
+	public function getGridTopComponents()
821
+	{
822
+		$configuration = $this->getModuleConfiguration();
823
+		return $configuration['components'][ModulePosition::GRID][ModulePosition::TOP];
824
+	}
825
+
826
+	/**
827
+	 * @param array $components
828
+	 * @return $this
829
+	 */
830
+	public function setGridTopComponents(array $components): self
831
+	{
832
+		$this->components[ModulePosition::GRID][ModulePosition::TOP] = $components;
833
+		return $this;
834
+	}
835
+
836
+	/**
837
+	 * @param string|array $components
838
+	 * @return $this
839
+	 */
840
+	public function addGridTopComponents($components): self
841
+	{
842
+		if (is_string($components)) {
843
+			$components = [$components];
844
+		}
845
+		$currentComponents = $this->components[ModulePosition::GRID][ModulePosition::TOP];
846
+		$this->components[ModulePosition::GRID][ModulePosition::TOP] = array_merge($currentComponents, $components);
847
+		return $this;
848
+	}
849
+
850
+	/**
851
+	 * @return mixed $array
852
+	 */
853
+	public function getGridBottomComponents()
854
+	{
855
+		$configuration = $this->getModuleConfiguration();
856
+		return $configuration['components'][ModulePosition::GRID][ModulePosition::BOTTOM];
857
+	}
858
+
859
+	/**
860
+	 * @param array $components
861
+	 * @return $this
862
+	 */
863
+	public function setGridBottomComponents(array $components): self
864
+	{
865
+		$this->components[ModulePosition::GRID][ModulePosition::BOTTOM] = $components;
866
+		return $this;
867
+	}
868
+
869
+	/**
870
+	 * @param string|array $components
871
+	 * @return $this
872
+	 */
873
+	public function addGridBottomComponents($components): self
874
+	{
875
+		if (is_string($components)) {
876
+			$components = [$components];
877
+		}
878
+		$currentComponents = $this->components[ModulePosition::GRID][ModulePosition::BOTTOM];
879
+		$this->components[ModulePosition::GRID][ModulePosition::BOTTOM] = array_merge($currentComponents, $components);
880
+		return $this;
881
+	}
882
+
883
+	/**
884
+	 * @return $array
885
+	 */
886
+	public function getGridButtonsComponents()
887
+	{
888
+		$configuration = $this->getModuleConfiguration();
889
+		return $configuration['components'][ModulePosition::GRID][ModulePosition::BUTTONS];
890
+	}
891
+
892
+	/**
893
+	 * @param array $components
894
+	 * @return $this
895
+	 */
896
+	public function setGridButtonsComponents(array $components): self
897
+	{
898
+		$this->components[ModulePosition::GRID][ModulePosition::BUTTONS] = $components;
899
+		return $this;
900
+	}
901
+
902
+	/**
903
+	 * @param string|array $components
904
+	 * @return $this
905
+	 */
906
+	public function addGridButtonsComponents($components): self
907
+	{
908
+		if (is_string($components)) {
909
+			$components = [$components];
910
+		}
911
+		$currentComponents = $this->components[ModulePosition::GRID][ModulePosition::BUTTONS];
912
+		$this->components[ModulePosition::GRID][ModulePosition::BUTTONS] = array_merge($components, $currentComponents);
913
+		return $this;
914
+	}
915
+
916
+	/**
917
+	 * @return $array
918
+	 */
919
+	public function getMenuMassActionComponents()
920
+	{
921
+		$configuration = $this->getModuleConfiguration();
922
+		return $configuration['components'][ModulePosition::MENU_MASS_ACTION];
923
+	}
924
+
925
+	/**
926
+	 * @param array $components
927
+	 * @return $this
928
+	 */
929
+	public function setMenuMassActionComponents(array $components): self
930
+	{
931
+		$this->components[ModulePosition::MENU_MASS_ACTION] = $components;
932
+		return $this;
933
+	}
934
+
935
+	/**
936
+	 * @param string|array $components
937
+	 * @return $this
938
+	 */
939
+	public function addMenuMassActionComponents($components): self
940
+	{
941
+		if (is_string($components)) {
942
+			$components = [$components];
943
+		}
944
+		$currentComponents = $this->components[ModulePosition::MENU_MASS_ACTION];
945
+		$this->components[ModulePosition::MENU_MASS_ACTION] = array_merge($components, $currentComponents);
946
+		return $this;
947
+	}
948
+
949
+	/**
950
+	 * @return string
951
+	 */
952
+	protected function getAccess(): string
953
+	{
954
+		$moduleConfiguration = $this->getExistingMainConfiguration();
955
+
956
+		if ($this->access !== null) {
957
+			$access = $this->access;
958
+		} elseif (!empty($moduleConfiguration['access'])) { // existing configuration may override.
959
+			$access = $moduleConfiguration['access'];
960
+		} else {
961
+			$access = Access::USER; //default value.
962
+		}
963
+		return $access;
964
+	}
965
+
966
+	/**
967
+	 * @param string $access
968
+	 * @return $this
969
+	 */
970
+	public function setAccess($access): self
971
+	{
972
+		$this->access = $access;
973
+		return $this;
974
+	}
975
+
976
+	/**
977
+	 * @return \string[]
978
+	 */
979
+	public function getAdditionalJavaScriptFiles(): array
980
+	{
981
+		if (empty($this->additionalJavaScriptFiles)) {
982
+			$this->additionalJavaScriptFiles = $this->getModuleConfiguration('additionalJavaScriptFiles');
983
+		}
984
+		return $this->additionalJavaScriptFiles;
985
+	}
986
+
987
+	/**
988
+	 * @return \string[]
989
+	 */
990
+	public function getAdditionalStyleSheetFiles(): array
991
+	{
992
+		if (empty($this->additionalStyleSheetFiles)) {
993
+			$this->additionalStyleSheetFiles = $this->getModuleConfiguration('additionalStyleSheetFiles');
994
+		}
995
+		return $this->additionalStyleSheetFiles;
996
+	}
997
+
998
+	/**
999
+	 * @return array
1000
+	 */
1001
+	public function getComponents(): array
1002
+	{
1003
+		return $this->components;
1004
+	}
1005
+
1006
+	/**
1007
+	 * @param string $pluginName
1008
+	 * @return bool
1009
+	 */
1010
+	public function hasPlugin($pluginName = ''): bool
1011
+	{
1012
+		$parameterPrefix = $this->getParameterPrefix();
1013
+		$parameters = GeneralUtility::_GET($parameterPrefix);
1014
+
1015
+		$hasPlugin = !empty($parameters['plugins']) && is_array($parameters['plugins']);
1016
+		if ($hasPlugin && $pluginName) {
1017
+			$hasPlugin = in_array($pluginName, $parameters['plugins']);
1018
+		}
1019
+		return $hasPlugin;
1020
+	}
1021
+
1022
+	/**
1023
+	 * Compute the internal module code
1024
+	 *
1025
+	 * @param null|string $dataType
1026
+	 * @return string
1027
+	 */
1028
+	protected function getInternalModuleSignature($dataType = null): string
1029
+	{
1030
+		// Else we forge the module signature
1031
+		if ($dataType === null) {
1032
+			$dataType = $this->dataType;
1033
+		}
1034
+		$subModuleName = $dataType . '_' . $this->moduleKey;
1035
+
1036
+		$mainModule = $this->mainModule ?: self::DEFAULT_MAIN_MODULE;
1037
+		return $mainModule . '_Vidi' . GeneralUtility::underscoredToUpperCamelCase($subModuleName);
1038
+	}
1039 1039
 
1040 1040
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -193,7 +193,7 @@  discard block
 block discarded – undo
193 193
      */
194 194
     protected function getExistingMainConfiguration(): array
195 195
     {
196
-        $moduleSignature = $this->computeMainModule() . '_' . $this->getInternalModuleSignature();
196
+        $moduleSignature = $this->computeMainModule().'_'.$this->getInternalModuleSignature();
197 197
         return $GLOBALS['TBE_MODULES']['_configuration'][$moduleSignature] ?? [];
198 198
     }
199 199
 
@@ -315,7 +315,7 @@  discard block
 block discarded – undo
315 315
             ExtensionUtility::registerModule(
316 316
                 'Fab.vidi',
317 317
                 $this->computeMainModule(),
318
-                $this->dataType . '_' . $this->moduleKey,
318
+                $this->dataType.'_'.$this->moduleKey,
319 319
                 $this->position,
320 320
                 [
321 321
                     ContentController::class => 'index, list, delete, update, edit, copy, move, localize, sort, copyClipboard, moveClipboard',
@@ -378,7 +378,7 @@  discard block
 block discarded – undo
378 378
      */
379 379
     public function getParameterPrefix(): string
380 380
     {
381
-        return 'tx_vidi_' . strtolower($this->getSignature());
381
+        return 'tx_vidi_'.strtolower($this->getSignature());
382 382
     }
383 383
 
384 384
     /**
@@ -1031,10 +1031,10 @@  discard block
 block discarded – undo
1031 1031
         if ($dataType === null) {
1032 1032
             $dataType = $this->dataType;
1033 1033
         }
1034
-        $subModuleName = $dataType . '_' . $this->moduleKey;
1034
+        $subModuleName = $dataType.'_'.$this->moduleKey;
1035 1035
 
1036 1036
         $mainModule = $this->mainModule ?: self::DEFAULT_MAIN_MODULE;
1037
-        return $mainModule . '_Vidi' . GeneralUtility::underscoredToUpperCamelCase($subModuleName);
1037
+        return $mainModule.'_Vidi'.GeneralUtility::underscoredToUpperCamelCase($subModuleName);
1038 1038
     }
1039 1039
 
1040 1040
 }
Please login to merge, or discard this patch.
Classes/DataHandler/CoreDataHandler.php 1 patch
Indentation   +135 added lines, -135 removed lines patch added patch discarded remove patch
@@ -18,140 +18,140 @@
 block discarded – undo
18 18
 class CoreDataHandler extends AbstractDataHandler
19 19
 {
20 20
 
21
-    /**
22
-     * @var array
23
-     */
24
-    protected $dataHandler;
25
-
26
-    /**
27
-     * Process Content with action "update".
28
-     *
29
-     * @param Content $content
30
-     * @throws \Exception
31
-     * @return bool
32
-     */
33
-    public function processUpdate(Content $content)
34
-    {
35
-
36
-        $values = [];
37
-
38
-        // Check the field to be updated exists
39
-        foreach ($content->toArray() as $fieldName => $value) {
40
-            if (!Tca::table($content->getDataType())->hasField($fieldName)) {
41
-                $message = sprintf('It looks field "%s" does not exist for data type "%s"', $fieldName, $content->getDataType());
42
-                throw new \Exception($message, 1390668497);
43
-            }
44
-
45
-            // Flatten value if array given which is required for the DataHandler.
46
-            if (is_array($value)) {
47
-                $value = implode(',', $value);
48
-            }
49
-            $values[$fieldName] = $value;
50
-        }
51
-
52
-        $data[$content->getDataType()][$content->getUid()] = $values;
53
-
54
-        $dataHandler = $this->getDataHandler();
55
-        $dataHandler->start($data, array());
56
-        $dataHandler->process_datamap();
57
-        $this->errorMessages = $dataHandler->errorLog;
58
-
59
-        // Returns true is log does not contain errors.
60
-        return empty($dataHandler->errorLog);
61
-    }
62
-
63
-    /**
64
-     * Process Content with action "remove".
65
-     *
66
-     * @param Content $content
67
-     * @return bool
68
-     */
69
-    public function processRemove(Content $content)
70
-    {
71
-
72
-        // Build command
73
-        $cmd[$content->getDataType()][$content->getUid()]['delete'] = 1;
74
-
75
-        /** @var $dataHandler \TYPO3\CMS\Core\DataHandling\DataHandler */
76
-        $dataHandler = $this->getDataHandler();
77
-        $dataHandler->start([], $cmd);
78
-        $dataHandler->process_datamap();
79
-        $dataHandler->process_cmdmap();
80
-        $this->errorMessages = $dataHandler->errorLog;
81
-
82
-        // Returns true is log does not contain errors.
83
-        return empty($dataHandler->errorLog);
84
-    }
85
-
86
-    /**
87
-     * Process Content with action "copy".
88
-     *
89
-     * @param Content $content
90
-     * @param string $target
91
-     * @return bool
92
-     */
93
-    public function processCopy(Content $content, $target)
94
-    {
95
-        // TODO: Implement processCopy() method.
96
-    }
97
-
98
-    /**
99
-     * Process Content with action "move".
100
-     * The $target corresponds to the pid to move the records to.
101
-     * It can also be a negative value in case of sorting. The negative value would be the uid of its predecessor.
102
-     *
103
-     * @param Content $content
104
-     * @param int $target corresponds
105
-     * @return bool
106
-     */
107
-    public function processMove(Content $content, $target)
108
-    {
109
-
110
-        // Build command
111
-        $cmd[$content->getDataType()][$content->getUid()]['move'] = $target;
112
-
113
-        /** @var $dataHandler \TYPO3\CMS\Core\DataHandling\DataHandler */
114
-        $dataHandler = $this->getDataHandler();
115
-        $dataHandler->start([], $cmd);
116
-        $dataHandler->process_datamap();
117
-        $dataHandler->process_cmdmap();
118
-        $this->errorMessages = $dataHandler->errorLog;
119
-
120
-        // Returns true is log does not contain errors.
121
-        return empty($dataHandler->errorLog);
122
-    }
123
-
124
-    /**
125
-     * Process Content with action "localize".
126
-     *
127
-     * @param Content $content
128
-     * @param int $language
129
-     * @return bool
130
-     */
131
-    public function processLocalize(Content $content, $language)
132
-    {
133
-
134
-        $command[$content->getDataType()][$content->getUid()]['localize'] = $language;
135
-
136
-        $dataHandler = $this->getDataHandler();
137
-        $dataHandler->start([], $command);
138
-        $dataHandler->process_datamap();
139
-        $dataHandler->process_cmdmap();
140
-        $this->errorMessages = $dataHandler->errorLog;
141
-
142
-        // Returns true is log does not contain errors.
143
-        return empty($dataHandler->errorLog);
144
-    }
145
-
146
-    /**
147
-     * @return DataHandler
148
-     */
149
-    protected function getDataHandler()
150
-    {
151
-        if (!$this->dataHandler) {
152
-            $this->dataHandler = GeneralUtility::makeInstance(DataHandler::class);
153
-        }
154
-        return $this->dataHandler;
155
-    }
21
+	/**
22
+	 * @var array
23
+	 */
24
+	protected $dataHandler;
25
+
26
+	/**
27
+	 * Process Content with action "update".
28
+	 *
29
+	 * @param Content $content
30
+	 * @throws \Exception
31
+	 * @return bool
32
+	 */
33
+	public function processUpdate(Content $content)
34
+	{
35
+
36
+		$values = [];
37
+
38
+		// Check the field to be updated exists
39
+		foreach ($content->toArray() as $fieldName => $value) {
40
+			if (!Tca::table($content->getDataType())->hasField($fieldName)) {
41
+				$message = sprintf('It looks field "%s" does not exist for data type "%s"', $fieldName, $content->getDataType());
42
+				throw new \Exception($message, 1390668497);
43
+			}
44
+
45
+			// Flatten value if array given which is required for the DataHandler.
46
+			if (is_array($value)) {
47
+				$value = implode(',', $value);
48
+			}
49
+			$values[$fieldName] = $value;
50
+		}
51
+
52
+		$data[$content->getDataType()][$content->getUid()] = $values;
53
+
54
+		$dataHandler = $this->getDataHandler();
55
+		$dataHandler->start($data, array());
56
+		$dataHandler->process_datamap();
57
+		$this->errorMessages = $dataHandler->errorLog;
58
+
59
+		// Returns true is log does not contain errors.
60
+		return empty($dataHandler->errorLog);
61
+	}
62
+
63
+	/**
64
+	 * Process Content with action "remove".
65
+	 *
66
+	 * @param Content $content
67
+	 * @return bool
68
+	 */
69
+	public function processRemove(Content $content)
70
+	{
71
+
72
+		// Build command
73
+		$cmd[$content->getDataType()][$content->getUid()]['delete'] = 1;
74
+
75
+		/** @var $dataHandler \TYPO3\CMS\Core\DataHandling\DataHandler */
76
+		$dataHandler = $this->getDataHandler();
77
+		$dataHandler->start([], $cmd);
78
+		$dataHandler->process_datamap();
79
+		$dataHandler->process_cmdmap();
80
+		$this->errorMessages = $dataHandler->errorLog;
81
+
82
+		// Returns true is log does not contain errors.
83
+		return empty($dataHandler->errorLog);
84
+	}
85
+
86
+	/**
87
+	 * Process Content with action "copy".
88
+	 *
89
+	 * @param Content $content
90
+	 * @param string $target
91
+	 * @return bool
92
+	 */
93
+	public function processCopy(Content $content, $target)
94
+	{
95
+		// TODO: Implement processCopy() method.
96
+	}
97
+
98
+	/**
99
+	 * Process Content with action "move".
100
+	 * The $target corresponds to the pid to move the records to.
101
+	 * It can also be a negative value in case of sorting. The negative value would be the uid of its predecessor.
102
+	 *
103
+	 * @param Content $content
104
+	 * @param int $target corresponds
105
+	 * @return bool
106
+	 */
107
+	public function processMove(Content $content, $target)
108
+	{
109
+
110
+		// Build command
111
+		$cmd[$content->getDataType()][$content->getUid()]['move'] = $target;
112
+
113
+		/** @var $dataHandler \TYPO3\CMS\Core\DataHandling\DataHandler */
114
+		$dataHandler = $this->getDataHandler();
115
+		$dataHandler->start([], $cmd);
116
+		$dataHandler->process_datamap();
117
+		$dataHandler->process_cmdmap();
118
+		$this->errorMessages = $dataHandler->errorLog;
119
+
120
+		// Returns true is log does not contain errors.
121
+		return empty($dataHandler->errorLog);
122
+	}
123
+
124
+	/**
125
+	 * Process Content with action "localize".
126
+	 *
127
+	 * @param Content $content
128
+	 * @param int $language
129
+	 * @return bool
130
+	 */
131
+	public function processLocalize(Content $content, $language)
132
+	{
133
+
134
+		$command[$content->getDataType()][$content->getUid()]['localize'] = $language;
135
+
136
+		$dataHandler = $this->getDataHandler();
137
+		$dataHandler->start([], $command);
138
+		$dataHandler->process_datamap();
139
+		$dataHandler->process_cmdmap();
140
+		$this->errorMessages = $dataHandler->errorLog;
141
+
142
+		// Returns true is log does not contain errors.
143
+		return empty($dataHandler->errorLog);
144
+	}
145
+
146
+	/**
147
+	 * @return DataHandler
148
+	 */
149
+	protected function getDataHandler()
150
+	{
151
+		if (!$this->dataHandler) {
152
+			$this->dataHandler = GeneralUtility::makeInstance(DataHandler::class);
153
+		}
154
+		return $this->dataHandler;
155
+	}
156 156
 
157 157
 }
Please login to merge, or discard this patch.
Classes/Persistence/PagerObjectFactory.php 1 patch
Indentation   +37 added lines, -37 removed lines patch added patch discarded remove patch
@@ -17,48 +17,48 @@
 block discarded – undo
17 17
 class PagerObjectFactory implements SingletonInterface
18 18
 {
19 19
 
20
-    /**
21
-     * Gets a singleton instance of this class.
22
-     *
23
-     * @return \Fab\Vidi\Persistence\PagerObjectFactory|object
24
-     */
25
-    static public function getInstance()
26
-    {
27
-        return GeneralUtility::makeInstance(\Fab\Vidi\Persistence\PagerObjectFactory::class);
28
-    }
20
+	/**
21
+	 * Gets a singleton instance of this class.
22
+	 *
23
+	 * @return \Fab\Vidi\Persistence\PagerObjectFactory|object
24
+	 */
25
+	static public function getInstance()
26
+	{
27
+		return GeneralUtility::makeInstance(\Fab\Vidi\Persistence\PagerObjectFactory::class);
28
+	}
29 29
 
30
-    /**
31
-     * Returns a pager object.
32
-     *
33
-     * @return Pager
34
-     */
35
-    public function getPager()
36
-    {
30
+	/**
31
+	 * Returns a pager object.
32
+	 *
33
+	 * @return Pager
34
+	 */
35
+	public function getPager()
36
+	{
37 37
 
38
-        /** @var $pager \Fab\Vidi\Persistence\Pager */
39
-        $pager = GeneralUtility::makeInstance(Pager::class);
38
+		/** @var $pager \Fab\Vidi\Persistence\Pager */
39
+		$pager = GeneralUtility::makeInstance(Pager::class);
40 40
 
41
-        // Set items per page
42
-        if (GeneralUtility::_GET('length') !== null) {
43
-            $limit = (int)GeneralUtility::_GET('length');
44
-            $pager->setLimit($limit);
45
-        }
41
+		// Set items per page
42
+		if (GeneralUtility::_GET('length') !== null) {
43
+			$limit = (int)GeneralUtility::_GET('length');
44
+			$pager->setLimit($limit);
45
+		}
46 46
 
47
-        // Set offset
48
-        $offset = 0;
49
-        if (GeneralUtility::_GET('start') !== null) {
50
-            $offset = (int)GeneralUtility::_GET('start');
51
-        }
52
-        $pager->setOffset($offset);
47
+		// Set offset
48
+		$offset = 0;
49
+		if (GeneralUtility::_GET('start') !== null) {
50
+			$offset = (int)GeneralUtility::_GET('start');
51
+		}
52
+		$pager->setOffset($offset);
53 53
 
54
-        // set page
55
-        $page = 1;
56
-        if ($pager->getLimit() > 0) {
57
-            $page = round($pager->getOffset() / $pager->getLimit());
58
-        }
59
-        $pager->setPage($page);
54
+		// set page
55
+		$page = 1;
56
+		if ($pager->getLimit() > 0) {
57
+			$page = round($pager->getOffset() / $pager->getLimit());
58
+		}
59
+		$pager->setPage($page);
60 60
 
61
-        return $pager;
62
-    }
61
+		return $pager;
62
+	}
63 63
 
64 64
 }
Please login to merge, or discard this patch.
Classes/Persistence/OrderObjectFactory.php 1 patch
Indentation   +39 added lines, -39 removed lines patch added patch discarded remove patch
@@ -18,43 +18,43 @@
 block discarded – undo
18 18
 class OrderObjectFactory implements SingletonInterface
19 19
 {
20 20
 
21
-    /**
22
-     * Gets a singleton instance of this class.
23
-     *
24
-     * @return \Fab\Vidi\Persistence\OrderObjectFactory|object
25
-     */
26
-    static public function getInstance()
27
-    {
28
-        return GeneralUtility::makeInstance(\Fab\Vidi\Persistence\OrderObjectFactory::class);
29
-    }
30
-
31
-    /**
32
-     * Returns an order object.
33
-     *
34
-     * @param string $dataType
35
-     * @return Order|object
36
-     */
37
-    public function getOrder($dataType = '')
38
-    {
39
-
40
-        // Default ordering
41
-        $order = Tca::table($dataType)->getDefaultOrderings();
42
-
43
-        // Retrieve a possible id of the column from the request
44
-        $orderings = GeneralUtility::_GP('order');
45
-
46
-        if (is_array($orderings) && isset($orderings[0])) {
47
-            $columnPosition = $orderings[0]['column'];
48
-            $direction = $orderings[0]['dir'];
49
-
50
-            if ($columnPosition > 0) {
51
-                $field = Tca::grid()->getFieldNameByPosition($columnPosition);
52
-
53
-                $order = array(
54
-                    $field => strtoupper($direction)
55
-                );
56
-            }
57
-        }
58
-        return GeneralUtility::makeInstance(Order::class, $order);
59
-    }
21
+	/**
22
+	 * Gets a singleton instance of this class.
23
+	 *
24
+	 * @return \Fab\Vidi\Persistence\OrderObjectFactory|object
25
+	 */
26
+	static public function getInstance()
27
+	{
28
+		return GeneralUtility::makeInstance(\Fab\Vidi\Persistence\OrderObjectFactory::class);
29
+	}
30
+
31
+	/**
32
+	 * Returns an order object.
33
+	 *
34
+	 * @param string $dataType
35
+	 * @return Order|object
36
+	 */
37
+	public function getOrder($dataType = '')
38
+	{
39
+
40
+		// Default ordering
41
+		$order = Tca::table($dataType)->getDefaultOrderings();
42
+
43
+		// Retrieve a possible id of the column from the request
44
+		$orderings = GeneralUtility::_GP('order');
45
+
46
+		if (is_array($orderings) && isset($orderings[0])) {
47
+			$columnPosition = $orderings[0]['column'];
48
+			$direction = $orderings[0]['dir'];
49
+
50
+			if ($columnPosition > 0) {
51
+				$field = Tca::grid()->getFieldNameByPosition($columnPosition);
52
+
53
+				$order = array(
54
+					$field => strtoupper($direction)
55
+				);
56
+			}
57
+		}
58
+		return GeneralUtility::makeInstance(Order::class, $order);
59
+	}
60 60
 }
Please login to merge, or discard this patch.
Classes/Persistence/Storage/VidiDbBackend.php 2 patches
Spacing   +37 added lines, -37 removed lines patch added patch discarded remove patch
@@ -169,7 +169,7 @@  discard block
 block discarded – undo
169 169
             if (isset($statementParts['keywords']['distinct'])) {
170 170
                 unset($statementParts['keywords']['distinct']);
171 171
                 $distinctField = $this->query->getDistinct() ? $this->query->getDistinct() : 'uid';
172
-                $statementParts['fields'] = array('COUNT(DISTINCT ' . $statementParts['mainTable'] . '.' . $distinctField . ')');
172
+                $statementParts['fields'] = array('COUNT(DISTINCT '.$statementParts['mainTable'].'.'.$distinctField.')');
173 173
             }
174 174
 
175 175
             $sql = $this->buildQuery($statementParts);
@@ -232,14 +232,14 @@  discard block
 block discarded – undo
232 232
             // tx_domain_model_foo0 (the alias) <--> tx_domain_model_foo (the origin table name)
233 233
             $values = [];
234 234
             foreach ($statementParts['fields'] as $key => $value) {
235
-                $values[$key] = str_replace($tableName, $tableName . '0', $value);
235
+                $values[$key] = str_replace($tableName, $tableName.'0', $value);
236 236
             }
237 237
             $statementParts['fields'] = $values;
238 238
 
239 239
             // Same comment as above.
240 240
             $values = [];
241 241
             foreach ($statementParts['where'] as $key => $value) {
242
-                $values[$key] = str_replace($tableName . '0', $tableName, $value);
242
+                $values[$key] = str_replace($tableName.'0', $tableName, $value);
243 243
             }
244 244
             $statementParts['where'] = $values;
245 245
 
@@ -278,25 +278,25 @@  discard block
 block discarded – undo
278 278
         if (!empty($statementParts['unions'])) {
279 279
             foreach ($statementParts['unions'] as $tableName => $unionPart) {
280 280
                 if (!empty($statementParts['additionalWhereClause'][$tableName])) {
281
-                    $statementParts['unions'][$tableName] .= ' AND ' . implode(' AND ', $statementParts['additionalWhereClause'][$tableName]);
281
+                    $statementParts['unions'][$tableName] .= ' AND '.implode(' AND ', $statementParts['additionalWhereClause'][$tableName]);
282 282
                 }
283 283
             }
284 284
         }
285 285
 
286
-        $statement = 'SELECT ' . implode(' ', $statementParts['keywords']) . ' ' . implode(',', $statementParts['fields']) . ' FROM ' . implode(' ', $statementParts['tables']) . ' ' . implode(' ', $statementParts['unions']);
286
+        $statement = 'SELECT '.implode(' ', $statementParts['keywords']).' '.implode(',', $statementParts['fields']).' FROM '.implode(' ', $statementParts['tables']).' '.implode(' ', $statementParts['unions']);
287 287
         if (!empty($statementParts['where'])) {
288
-            $statement .= ' WHERE ' . implode('', $statementParts['where']);
288
+            $statement .= ' WHERE '.implode('', $statementParts['where']);
289 289
             if (!empty($statementParts['additionalWhereClause'][$this->query->getType()])) {
290
-                $statement .= ' AND ' . implode(' AND ', $statementParts['additionalWhereClause'][$this->query->getType()]);
290
+                $statement .= ' AND '.implode(' AND ', $statementParts['additionalWhereClause'][$this->query->getType()]);
291 291
             }
292 292
         } elseif (!empty($statementParts['additionalWhereClause'])) {
293
-            $statement .= ' WHERE ' . implode(' AND ', $statementParts['additionalWhereClause'][$this->query->getType()]);
293
+            $statement .= ' WHERE '.implode(' AND ', $statementParts['additionalWhereClause'][$this->query->getType()]);
294 294
         }
295 295
         if (!empty($statementParts['orderings'])) {
296
-            $statement .= ' ORDER BY ' . implode(', ', $statementParts['orderings']);
296
+            $statement .= ' ORDER BY '.implode(', ', $statementParts['orderings']);
297 297
         }
298 298
         if (!empty($statementParts['limit'])) {
299
-            $statement .= ' LIMIT ' . $statementParts['limit'];
299
+            $statement .= ' LIMIT '.$statementParts['limit'];
300 300
         }
301 301
 
302 302
         return $statement;
@@ -312,9 +312,9 @@  discard block
 block discarded – undo
312 312
     protected function parseSource(SourceInterface $source, array &$sql)
313 313
     {
314 314
         $tableName = $this->getTableName();
315
-        $sql['fields'][$tableName] = $tableName . '.*';
315
+        $sql['fields'][$tableName] = $tableName.'.*';
316 316
         if ($this->query->getDistinct()) {
317
-            $sql['fields'][$tableName] = $tableName . '.' . $this->query->getDistinct();
317
+            $sql['fields'][$tableName] = $tableName.'.'.$this->query->getDistinct();
318 318
             $sql['keywords']['distinct'] = 'DISTINCT';
319 319
         }
320 320
         $sql['tables'][$tableName] = $tableName;
@@ -452,7 +452,7 @@  discard block
 block discarded – undo
452 452
             if ($realInput instanceof DomainObjectInterface) {
453 453
                 return $realInput->getUid();
454 454
             } else {
455
-                throw new UnexpectedTypeException('An object of class "' . get_class($realInput) . '" could not be converted to a plain value.', 1274799934);
455
+                throw new UnexpectedTypeException('An object of class "'.get_class($realInput).'" could not be converted to a plain value.', 1274799934);
456 456
             }
457 457
         } elseif (is_bool($input)) {
458 458
             return $input === true ? 1 : 0;
@@ -502,9 +502,9 @@  discard block
 block discarded – undo
502 502
                 : '?';
503 503
 
504 504
             if ($valueFunction === null) {
505
-                $constraintSQL .= (!empty($tableName) ? $tableName . '.' : '') . $columnName . ' ' . $resolvedOperator . ' ' . $marker;
505
+                $constraintSQL .= (!empty($tableName) ? $tableName.'.' : '').$columnName.' '.$resolvedOperator.' '.$marker;
506 506
             } else {
507
-                $constraintSQL .= $valueFunction . '(' . (!empty($tableName) ? $tableName . '.' : '') . $columnName . ') ' . $resolvedOperator . ' ' . $marker;
507
+                $constraintSQL .= $valueFunction.'('.(!empty($tableName) ? $tableName.'.' : '').$columnName.') '.$resolvedOperator.' '.$marker;
508 508
             }
509 509
 
510 510
             if (isset($tableName) && !empty($this->currentChildTableNameAlias)) {
@@ -531,7 +531,7 @@  discard block
 block discarded – undo
531 531
         // to determine the relation type. Example for sys_category, property path will look like "items.sys_file"
532 532
         $parts = explode('.', $propertyPath, 3);
533 533
         if ($table->field($fieldName)->isGroup() && count($parts) > 2) {
534
-            $explodedPropertyPath[0] = $parts[0] . '.' . $parts[1];
534
+            $explodedPropertyPath[0] = $parts[0].'.'.$parts[1];
535 535
             $explodedPropertyPath[1] = $parts[2];
536 536
             $fieldName = $explodedPropertyPath[0];
537 537
         }
@@ -540,16 +540,16 @@  discard block
 block discarded – undo
540 540
         $childTableName = $table->field($fieldName)->getForeignTable();
541 541
 
542 542
         if ($childTableName === null) {
543
-            throw new InvalidRelationConfigurationException('The relation information for property "' . $fieldName . '" of class "' . $tableName . '" is missing.', 1353170925);
543
+            throw new InvalidRelationConfigurationException('The relation information for property "'.$fieldName.'" of class "'.$tableName.'" is missing.', 1353170925);
544 544
         }
545 545
 
546 546
         if ($table->field($fieldName)->hasOne()) { // includes relation "one-to-one" and "many-to-one"
547 547
             // sometimes the opposite relation is not defined. We don't want to force this config for backward compatibility reasons.
548 548
             // $parentKeyFieldName === null does the trick somehow. Before condition was if (isset($parentKeyFieldName))
549 549
             if ($table->field($fieldName)->hasRelationManyToOne() || $parentKeyFieldName === null) {
550
-                $statementParts['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.' . $fieldName . '=' . $childTableName . '.uid';
550
+                $statementParts['unions'][$childTableName] = 'LEFT JOIN '.$childTableName.' ON '.$tableName.'.'.$fieldName.'='.$childTableName.'.uid';
551 551
             } else {
552
-                $statementParts['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.uid=' . $childTableName . '.' . $parentKeyFieldName;
552
+                $statementParts['unions'][$childTableName] = 'LEFT JOIN '.$childTableName.' ON '.$tableName.'.uid='.$childTableName.'.'.$parentKeyFieldName;
553 553
             }
554 554
         } elseif ($table->field($fieldName)->hasRelationManyToMany()) {
555 555
             $relationTableName = $table->field($fieldName)->getManyToManyTable();
@@ -779,18 +779,18 @@  discard block
 block discarded – undo
779 779
         // If the table is found to have "workspace" support, add the corresponding fields in the statement.
780 780
         if (Tca::table($tableName)->hasWorkspaceSupport()) {
781 781
             if ($this->getBackendUser()->workspace === 0) {
782
-                $statement .= ' AND ' . $tableName . '.t3ver_state<=' . new VersionState(VersionState::DEFAULT_STATE);
782
+                $statement .= ' AND '.$tableName.'.t3ver_state<='.new VersionState(VersionState::DEFAULT_STATE);
783 783
             } else {
784 784
                 // Show only records of live and of the current workspace
785 785
                 // In case we are in a Versioning preview
786
-                $statement .= ' AND (' .
787
-                    $tableName . '.t3ver_wsid=0 OR ' .
788
-                    $tableName . '.t3ver_wsid=' . (int)$this->getBackendUser()->workspace .
786
+                $statement .= ' AND ('.
787
+                    $tableName.'.t3ver_wsid=0 OR '.
788
+                    $tableName.'.t3ver_wsid='.(int)$this->getBackendUser()->workspace.
789 789
                     ')';
790 790
             }
791 791
 
792 792
             // Check if this segment make sense here or whether it should be in the "if" part when we have workspace = 0
793
-            $statement .= ' AND ' . $tableName . '.pid<>-1';
793
+            $statement .= ' AND '.$tableName.'.pid<>-1';
794 794
         }
795 795
 
796 796
         if (!$includeDeleted) {
@@ -815,25 +815,25 @@  discard block
 block discarded – undo
815 815
         if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
816 816
             if (!empty($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])) {
817 817
                 // Select all entries for the current language
818
-                $additionalWhereClause = $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . ' IN (' . intval($querySettings->getLanguageUid()) . ',-1)';
818
+                $additionalWhereClause = $tableNameOrAlias.'.'.$GLOBALS['TCA'][$tableName]['ctrl']['languageField'].' IN ('.intval($querySettings->getLanguageUid()).',-1)';
819 819
                 // If any language is set -> get those entries which are not translated yet
820 820
                 // They will be removed by t3lib_page::getRecordOverlay if not matching overlay mode
821 821
                 if (isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
822 822
                     && $querySettings->getLanguageUid() > 0
823 823
                 ) {
824
-                    $additionalWhereClause .= ' OR (' . $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=0' .
825
-                        ' AND ' . $tableNameOrAlias . '.uid NOT IN (SELECT ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] .
826
-                        ' FROM ' . $tableName .
827
-                        ' WHERE ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] . '>0' .
828
-                        ' AND ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '>0';
824
+                    $additionalWhereClause .= ' OR ('.$tableNameOrAlias.'.'.$GLOBALS['TCA'][$tableName]['ctrl']['languageField'].'=0'.
825
+                        ' AND '.$tableNameOrAlias.'.uid NOT IN (SELECT '.$tableName.'.'.$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'].
826
+                        ' FROM '.$tableName.
827
+                        ' WHERE '.$tableName.'.'.$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'].'>0'.
828
+                        ' AND '.$tableName.'.'.$GLOBALS['TCA'][$tableName]['ctrl']['languageField'].'>0';
829 829
 
830 830
                     // Add delete clause to ensure all entries are loaded
831 831
                     if (isset($GLOBALS['TCA'][$tableName]['ctrl']['delete'])) {
832
-                        $additionalWhereClause .= ' AND ' . $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['delete'] . '=0';
832
+                        $additionalWhereClause .= ' AND '.$tableNameOrAlias.'.'.$GLOBALS['TCA'][$tableName]['ctrl']['delete'].'=0';
833 833
                     }
834 834
                     $additionalWhereClause .= '))';
835 835
                 }
836
-                $statementParts['additionalWhereClause'][$tableNameOrAlias][] = '(' . $additionalWhereClause . ')';
836
+                $statementParts['additionalWhereClause'][$tableNameOrAlias][] = '('.$additionalWhereClause.')';
837 837
             }
838 838
         }
839 839
     }
@@ -878,7 +878,7 @@  discard block
 block discarded – undo
878 878
     protected function parseLimitAndOffset($limit, $offset, array &$statementParts)
879 879
     {
880 880
         if ($limit !== null && $offset !== null) {
881
-            $statementParts['limit'] = intval($offset) . ', ' . intval($limit);
881
+            $statementParts['limit'] = intval($offset).', '.intval($limit);
882 882
         } elseif ($limit !== null) {
883 883
             $statementParts['limit'] = intval($limit);
884 884
         }
@@ -946,11 +946,11 @@  discard block
 block discarded – undo
946 946
             ) {
947 947
                 $queryBuilder = $this->getQueryBuilder();
948 948
                 $row = $queryBuilder
949
-                    ->select($tableName . '.*')
949
+                    ->select($tableName.'.*')
950 950
                     ->from($tableName)
951 951
                     ->andWhere(
952
-                        $tableName . '.uid=' . (int)$row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']],
953
-                        $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . ' = 0'
952
+                        $tableName.'.uid='.(int)$row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']],
953
+                        $tableName.'.'.$GLOBALS['TCA'][$tableName]['ctrl']['languageField'].' = 0'
954 954
                     )
955 955
                     ->execute()
956 956
                     ->fetch();
@@ -1011,7 +1011,7 @@  discard block
 block discarded – undo
1011 1011
         }
1012 1012
 
1013 1013
         $numberOfAliases = $this->tableNameAliases['aliasIncrement'][$tableName];
1014
-        $tableNameAlias = $tableName . $numberOfAliases;
1014
+        $tableNameAlias = $tableName.$numberOfAliases;
1015 1015
 
1016 1016
         $this->tableNameAliases['aliasIncrement'][$tableName]++;
1017 1017
         $this->tableNameAliases['aliases'][$tableNameAlias] = $tableName;
Please login to merge, or discard this patch.
Indentation   +1047 added lines, -1047 removed lines patch added patch discarded remove patch
@@ -51,1052 +51,1052 @@
 block discarded – undo
51 51
 class VidiDbBackend
52 52
 {
53 53
 
54
-    const OPERATOR_EQUAL_TO_NULL = 'operatorEqualToNull';
55
-    const OPERATOR_NOT_EQUAL_TO_NULL = 'operatorNotEqualToNull';
56
-
57
-    /**
58
-     * The TYPO3 page repository. Used for language and workspace overlay
59
-     *
60
-     * @var PageRepository
61
-     */
62
-    protected $pageRepository;
63
-
64
-    /**
65
-     * @var Query
66
-     */
67
-    protected $query;
68
-
69
-    /**
70
-     * Store some info related to table name and its aliases.
71
-     *
72
-     * @var array
73
-     */
74
-    protected $tableNameAliases = array(
75
-        'aliases' => [],
76
-        'aliasIncrement' => [],
77
-    );
78
-
79
-    /**
80
-     * Use to store the current foreign table name alias.
81
-     *
82
-     * @var string
83
-     */
84
-    protected $currentChildTableNameAlias = '';
85
-
86
-    /**
87
-     * @param Query $query
88
-     */
89
-    public function __construct(Query $query)
90
-    {
91
-        $this->query = $query;
92
-    }
93
-
94
-    /**
95
-     * @param $parameters
96
-     * @return array
97
-     */
98
-    protected static function getTypes($parameters)
99
-    {
100
-        $types = [];
101
-        foreach ($parameters as $parameter) {
102
-            if (is_array($parameter)) {
103
-                if (MathUtility::canBeInterpretedAsInteger($parameter[0])) {
104
-                    $types[] = \Doctrine\DBAL\Connection::PARAM_INT_ARRAY;
105
-                } else {
106
-                    $types[] = \Doctrine\DBAL\Connection::PARAM_STR_ARRAY;
107
-                }
108
-            } else {
109
-                if (MathUtility::canBeInterpretedAsInteger($parameter)) {
110
-                    $types[] = ParameterType::INTEGER;
111
-                } else {
112
-                    $types[] = ParameterType::STRING;
113
-                }
114
-            }
115
-        }
116
-        return $types;
117
-    }
118
-
119
-    /**
120
-     * Returns the result of the query
121
-     */
122
-    public function fetchResult()
123
-    {
124
-        $parameters = [];
125
-        $statementParts = $this->parseQuery($parameters);
126
-        $statementParts = $this->processStatementStructureForRecursiveMMRelation($statementParts);
127
-        $sql = $this->buildQuery($statementParts);
128
-        //print $sql; exit();
129
-
130
-        $rows = $this->getConnection()
131
-            ->executeQuery($sql, $parameters, self::getTypes($parameters))
132
-            ->fetchAll();
133
-
134
-        return $this->getContentObjects($rows);
135
-    }
136
-
137
-    /**
138
-     * Returns the number of tuples matching the query.
139
-     *
140
-     * @return int The number of matching tuples
141
-     */
142
-    public function countResult()
143
-    {
144
-        $parameters = [];
145
-        $statementParts = $this->parseQuery($parameters);
146
-        $statementParts = $this->processStatementStructureForRecursiveMMRelation($statementParts);
147
-        $types = self::getTypes($parameters);
148
-
149
-        // if limit is set, we need to count the rows "manually" as COUNT(*) ignores LIMIT constraints
150
-        if (!empty($statementParts['limit'])) {
151
-            $sql = $this->buildQuery($statementParts);
152
-
153
-            $count = $this
154
-                ->getConnection()
155
-                ->executeQuery($sql, $parameters, $types)
156
-                ->rowCount();
157
-        } else {
158
-            $statementParts['fields'] = array('COUNT(*)');
159
-            // having orderings without grouping is not compatible with non-MySQL DBMS
160
-            $statementParts['orderings'] = [];
161
-            if (isset($statementParts['keywords']['distinct'])) {
162
-                unset($statementParts['keywords']['distinct']);
163
-                $distinctField = $this->query->getDistinct() ? $this->query->getDistinct() : 'uid';
164
-                $statementParts['fields'] = array('COUNT(DISTINCT ' . $statementParts['mainTable'] . '.' . $distinctField . ')');
165
-            }
166
-
167
-            $sql = $this->buildQuery($statementParts);
168
-            $count = $this
169
-                ->getConnection()
170
-                ->executeQuery($sql, $parameters, $types)
171
-                ->fetchColumn(0);
172
-        }
173
-        return (int)$count;
174
-    }
175
-
176
-    /**
177
-     * Parses the query and returns the SQL statement parts.
178
-     *
179
-     * @param array &$parameters
180
-     * @return array
181
-     */
182
-    public function parseQuery(array &$parameters)
183
-    {
184
-        $statementParts = [];
185
-        $statementParts['keywords'] = [];
186
-        $statementParts['tables'] = [];
187
-        $statementParts['unions'] = [];
188
-        $statementParts['fields'] = [];
189
-        $statementParts['where'] = [];
190
-        $statementParts['additionalWhereClause'] = [];
191
-        $statementParts['orderings'] = [];
192
-        $statementParts['limit'] = [];
193
-        $query = $this->query;
194
-        $source = $query->getSource();
195
-        $this->parseSource($source, $statementParts);
196
-        $this->parseConstraint($query->getConstraint(), $source, $statementParts, $parameters);
197
-        $this->parseOrderings($query->getOrderings(), $source, $statementParts);
198
-        $this->parseLimitAndOffset($query->getLimit(), $query->getOffset(), $statementParts);
199
-        $tableNames = array_unique(array_keys($statementParts['tables'] + $statementParts['unions']));
200
-        foreach ($tableNames as $tableNameOrAlias) {
201
-            if (is_string($tableNameOrAlias) && strlen($tableNameOrAlias) > 0) {
202
-                $this->addAdditionalWhereClause($query->getTypo3QuerySettings(), $tableNameOrAlias, $statementParts);
203
-            }
204
-        }
205
-
206
-        return $statementParts;
207
-    }
208
-
209
-    /**
210
-     * Fiddle with the statement structure to handle recursive MM relations.
211
-     * For the recursive MM query to work, we must invert some values.
212
-     * Let see if that is the best way of doing that...
213
-     *
214
-     * @param array $statementParts
215
-     * @return array
216
-     */
217
-    public function processStatementStructureForRecursiveMMRelation(array $statementParts)
218
-    {
219
-
220
-        if ($this->hasRecursiveMMRelation()) {
221
-            $tableName = $this->query->getType();
222
-
223
-            // In order the MM query to work for a recursive MM query, we must invert some values.
224
-            // tx_domain_model_foo0 (the alias) <--> tx_domain_model_foo (the origin table name)
225
-            $values = [];
226
-            foreach ($statementParts['fields'] as $key => $value) {
227
-                $values[$key] = str_replace($tableName, $tableName . '0', $value);
228
-            }
229
-            $statementParts['fields'] = $values;
230
-
231
-            // Same comment as above.
232
-            $values = [];
233
-            foreach ($statementParts['where'] as $key => $value) {
234
-                $values[$key] = str_replace($tableName . '0', $tableName, $value);
235
-            }
236
-            $statementParts['where'] = $values;
237
-
238
-            // We must be more restrictive by transforming the "left" union by "inner"
239
-            $values = [];
240
-            foreach ($statementParts['unions'] as $key => $value) {
241
-                $values[$key] = str_replace('LEFT JOIN', 'INNER JOIN', $value);
242
-            }
243
-            $statementParts['unions'] = $values;
244
-        }
245
-
246
-        return $statementParts;
247
-    }
248
-
249
-    /**
250
-     * Tell whether there is a recursive MM relation.
251
-     *
252
-     * @return bool
253
-     */
254
-    public function hasRecursiveMMRelation()
255
-    {
256
-        return isset($this->tableNameAliases['aliasIncrement'][$this->query->getType()]);
257
-
258
-    }
259
-
260
-    /**
261
-     * Returns the statement, ready to be executed.
262
-     *
263
-     * @param array $statementParts The SQL statement parts
264
-     * @return string The SQL statement
265
-     */
266
-    public function buildQuery(array $statementParts)
267
-    {
268
-
269
-        // Add more statement to the UNION part.
270
-        if (!empty($statementParts['unions'])) {
271
-            foreach ($statementParts['unions'] as $tableName => $unionPart) {
272
-                if (!empty($statementParts['additionalWhereClause'][$tableName])) {
273
-                    $statementParts['unions'][$tableName] .= ' AND ' . implode(' AND ', $statementParts['additionalWhereClause'][$tableName]);
274
-                }
275
-            }
276
-        }
277
-
278
-        $statement = 'SELECT ' . implode(' ', $statementParts['keywords']) . ' ' . implode(',', $statementParts['fields']) . ' FROM ' . implode(' ', $statementParts['tables']) . ' ' . implode(' ', $statementParts['unions']);
279
-        if (!empty($statementParts['where'])) {
280
-            $statement .= ' WHERE ' . implode('', $statementParts['where']);
281
-            if (!empty($statementParts['additionalWhereClause'][$this->query->getType()])) {
282
-                $statement .= ' AND ' . implode(' AND ', $statementParts['additionalWhereClause'][$this->query->getType()]);
283
-            }
284
-        } elseif (!empty($statementParts['additionalWhereClause'])) {
285
-            $statement .= ' WHERE ' . implode(' AND ', $statementParts['additionalWhereClause'][$this->query->getType()]);
286
-        }
287
-        if (!empty($statementParts['orderings'])) {
288
-            $statement .= ' ORDER BY ' . implode(', ', $statementParts['orderings']);
289
-        }
290
-        if (!empty($statementParts['limit'])) {
291
-            $statement .= ' LIMIT ' . $statementParts['limit'];
292
-        }
293
-
294
-        return $statement;
295
-    }
296
-
297
-    /**
298
-     * Transforms a Query Source into SQL and parameter arrays
299
-     *
300
-     * @param SourceInterface $source The source
301
-     * @param array &$sql
302
-     * @return void
303
-     */
304
-    protected function parseSource(SourceInterface $source, array &$sql)
305
-    {
306
-        $tableName = $this->getTableName();
307
-        $sql['fields'][$tableName] = $tableName . '.*';
308
-        if ($this->query->getDistinct()) {
309
-            $sql['fields'][$tableName] = $tableName . '.' . $this->query->getDistinct();
310
-            $sql['keywords']['distinct'] = 'DISTINCT';
311
-        }
312
-        $sql['tables'][$tableName] = $tableName;
313
-        $sql['mainTable'] = $tableName;
314
-    }
315
-
316
-    /**
317
-     * Transforms a constraint into SQL and parameter arrays
318
-     *
319
-     * @param ConstraintInterface $constraint The constraint
320
-     * @param SourceInterface $source The source
321
-     * @param array &$statementParts The query parts
322
-     * @param array &$parameters The parameters that will replace the markers
323
-     * @return void
324
-     */
325
-    protected function parseConstraint(ConstraintInterface $constraint = null, SourceInterface $source, array &$statementParts, array &$parameters)
326
-    {
327
-        if ($constraint instanceof AndInterface) {
328
-            $statementParts['where'][] = '(';
329
-            $this->parseConstraint($constraint->getConstraint1(), $source, $statementParts, $parameters);
330
-            $statementParts['where'][] = ' AND ';
331
-            $this->parseConstraint($constraint->getConstraint2(), $source, $statementParts, $parameters);
332
-            $statementParts['where'][] = ')';
333
-        } elseif ($constraint instanceof OrInterface) {
334
-            $statementParts['where'][] = '(';
335
-            $this->parseConstraint($constraint->getConstraint1(), $source, $statementParts, $parameters);
336
-            $statementParts['where'][] = ' OR ';
337
-            $this->parseConstraint($constraint->getConstraint2(), $source, $statementParts, $parameters);
338
-            $statementParts['where'][] = ')';
339
-        } elseif ($constraint instanceof NotInterface) {
340
-            $statementParts['where'][] = 'NOT (';
341
-            $this->parseConstraint($constraint->getConstraint(), $source, $statementParts, $parameters);
342
-            $statementParts['where'][] = ')';
343
-        } elseif ($constraint instanceof ComparisonInterface) {
344
-            $this->parseComparison($constraint, $source, $statementParts, $parameters);
345
-        }
346
-    }
347
-
348
-    /**
349
-     * Parse a Comparison into SQL and parameter arrays.
350
-     *
351
-     * @param ComparisonInterface $comparison The comparison to parse
352
-     * @param SourceInterface $source The source
353
-     * @param array &$statementParts SQL query parts to add to
354
-     * @param array &$parameters Parameters to bind to the SQL
355
-     * @return void
356
-     * @throws Exception\RepositoryException
357
-     */
358
-    protected function parseComparison(ComparisonInterface $comparison, SourceInterface $source, array &$statementParts, array &$parameters)
359
-    {
360
-        $operand1 = $comparison->getOperand1();
361
-        $operator = $comparison->getOperator();
362
-        $operand2 = $comparison->getOperand2();
363
-        if ($operator === QueryInterface::OPERATOR_IN) {
364
-            $items = [];
365
-            $hasValue = false;
366
-            foreach ($operand2 as $value) {
367
-                $value = $this->getPlainValue($value);
368
-                if ($value !== null) {
369
-                    $items[] = $value;
370
-                    $hasValue = true;
371
-                }
372
-            }
373
-            if ($hasValue === false) {
374
-                $statementParts['where'][] = '1<>1';
375
-            } else {
376
-                $this->parseDynamicOperand($operand1, $operator, $source, $statementParts, $parameters, null);
377
-                $parameters[] = $items;
378
-            }
379
-        } elseif ($operator === QueryInterface::OPERATOR_CONTAINS) {
380
-            if ($operand2 === null) {
381
-                $statementParts['where'][] = '1<>1';
382
-            } else {
383
-                throw new \Exception('Not implemented! Contact extension author.', 1412931227);
384
-                # @todo re-implement me if necessary.
385
-                #$tableName = $this->query->getType();
386
-                #$propertyName = $operand1->getPropertyName();
387
-                #while (strpos($propertyName, '.') !== false) {
388
-                #	$this->addUnionStatement($tableName, $propertyName, $statementParts);
389
-                #}
390
-                #$columnName = $propertyName;
391
-                #$columnMap = $propertyName;
392
-                #$typeOfRelation = $columnMap instanceof ColumnMap ? $columnMap->getTypeOfRelation() : null;
393
-                #if ($typeOfRelation === ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
394
-                #	$relationTableName = $columnMap->getRelationTableName();
395
-                #	$statementParts['where'][] = $tableName . '.uid IN (SELECT ' . $columnMap->getParentKeyFieldName() . ' FROM ' . $relationTableName . ' WHERE ' . $columnMap->getChildKeyFieldName() . '=?)';
396
-                #	$parameters[] = intval($this->getPlainValue($operand2));
397
-                #} elseif ($typeOfRelation === ColumnMap::RELATION_HAS_MANY) {
398
-                #	$parentKeyFieldName = $columnMap->getParentKeyFieldName();
399
-                #	if (isset($parentKeyFieldName)) {
400
-                #		$childTableName = $columnMap->getChildTableName();
401
-                #		$statementParts['where'][] = $tableName . '.uid=(SELECT ' . $childTableName . '.' . $parentKeyFieldName . ' FROM ' . $childTableName . ' WHERE ' . $childTableName . '.uid=?)';
402
-                #		$parameters[] = intval($this->getPlainValue($operand2));
403
-                #	} else {
404
-                #		$statementParts['where'][] = 'FIND_IN_SET(?,' . $tableName . '.' . $columnName . ')';
405
-                #		$parameters[] = intval($this->getPlainValue($operand2));
406
-                #	}
407
-                #} else {
408
-                #	throw new Exception\RepositoryException('Unsupported or non-existing property name "' . $propertyName . '" used in relation matching.', 1327065745);
409
-                #}
410
-            }
411
-        } else {
412
-            if ($operand2 === null) {
413
-                if ($operator === QueryInterface::OPERATOR_EQUAL_TO) {
414
-                    $operator = self::OPERATOR_EQUAL_TO_NULL;
415
-                } elseif ($operator === QueryInterface::OPERATOR_NOT_EQUAL_TO) {
416
-                    $operator = self::OPERATOR_NOT_EQUAL_TO_NULL;
417
-                }
418
-            }
419
-            $this->parseDynamicOperand($operand1, $operator, $source, $statementParts, $parameters);
420
-            $parameters[] = $this->getPlainValue($operand2);
421
-        }
422
-    }
423
-
424
-    /**
425
-     * Returns a plain value, i.e. objects are flattened if possible.
426
-     *
427
-     * @param mixed $input
428
-     * @return mixed
429
-     * @throws UnexpectedTypeException
430
-     */
431
-    protected function getPlainValue($input)
432
-    {
433
-        if (is_array($input)) {
434
-            throw new UnexpectedTypeException('An array could not be converted to a plain value.', 1274799932);
435
-        }
436
-        if ($input instanceof \DateTime) {
437
-            return $input->format('U');
438
-        } elseif (is_object($input)) {
439
-            if ($input instanceof LazyLoadingProxy) {
440
-                $realInput = $input->_loadRealInstance();
441
-            } else {
442
-                $realInput = $input;
443
-            }
444
-            if ($realInput instanceof DomainObjectInterface) {
445
-                return $realInput->getUid();
446
-            } else {
447
-                throw new UnexpectedTypeException('An object of class "' . get_class($realInput) . '" could not be converted to a plain value.', 1274799934);
448
-            }
449
-        } elseif (is_bool($input)) {
450
-            return $input === true ? 1 : 0;
451
-        } else {
452
-            return $input;
453
-        }
454
-    }
455
-
456
-    /**
457
-     * Parse a DynamicOperand into SQL and parameter arrays.
458
-     *
459
-     * @param DynamicOperandInterface $operand
460
-     * @param string $operator One of the JCR_OPERATOR_* constants
461
-     * @param SourceInterface $source The source
462
-     * @param array &$statementParts The query parts
463
-     * @param array &$parameters The parameters that will replace the markers
464
-     * @param string $valueFunction an optional SQL function to apply to the operand value
465
-     * @return void
466
-     */
467
-    protected function parseDynamicOperand(DynamicOperandInterface $operand, $operator, SourceInterface $source, array &$statementParts, array &$parameters, $valueFunction = null)
468
-    {
469
-        if ($operand instanceof LowerCaseInterface) {
470
-            $this->parseDynamicOperand($operand->getOperand(), $operator, $source, $statementParts, $parameters, 'LOWER');
471
-        } elseif ($operand instanceof UpperCaseInterface) {
472
-            $this->parseDynamicOperand($operand->getOperand(), $operator, $source, $statementParts, $parameters, 'UPPER');
473
-        } elseif ($operand instanceof PropertyValueInterface) {
474
-            $propertyName = $operand->getPropertyName();
475
-
476
-            // Reset value.
477
-            $this->currentChildTableNameAlias = '';
478
-
479
-            if ($source instanceof SelectorInterface) {
480
-                $tableName = $this->query->getType();
481
-                while (strpos($propertyName, '.') !== false) {
482
-                    $this->addUnionStatement($tableName, $propertyName, $statementParts);
483
-                }
484
-            } elseif ($source instanceof JoinInterface) {
485
-                $tableName = $source->getJoinCondition()->getSelector1Name();
486
-            }
487
-
488
-            $columnName = $propertyName;
489
-            $resolvedOperator = $this->resolveOperator($operator);
490
-            $constraintSQL = '';
491
-
492
-            $marker = $operator === QueryInterface::OPERATOR_IN
493
-                ? '(?)'
494
-                : '?';
495
-
496
-            if ($valueFunction === null) {
497
-                $constraintSQL .= (!empty($tableName) ? $tableName . '.' : '') . $columnName . ' ' . $resolvedOperator . ' ' . $marker;
498
-            } else {
499
-                $constraintSQL .= $valueFunction . '(' . (!empty($tableName) ? $tableName . '.' : '') . $columnName . ') ' . $resolvedOperator . ' ' . $marker;
500
-            }
501
-
502
-            if (isset($tableName) && !empty($this->currentChildTableNameAlias)) {
503
-                $constraintSQL = $this->replaceTableNameByAlias($tableName, $this->currentChildTableNameAlias, $constraintSQL);
504
-            }
505
-            $statementParts['where'][] = $constraintSQL;
506
-        }
507
-    }
508
-
509
-    /**
510
-     * @param string &$tableName
511
-     * @param string &$propertyPath
512
-     * @param array &$statementParts
513
-     */
514
-    protected function addUnionStatement(&$tableName, &$propertyPath, array &$statementParts)
515
-    {
516
-
517
-        $table = Tca::table($tableName);
518
-
519
-        $explodedPropertyPath = explode('.', $propertyPath, 2);
520
-        $fieldName = $explodedPropertyPath[0];
521
-
522
-        // Field of type "group" are special because property path must contain the table name
523
-        // to determine the relation type. Example for sys_category, property path will look like "items.sys_file"
524
-        $parts = explode('.', $propertyPath, 3);
525
-        if ($table->field($fieldName)->isGroup() && count($parts) > 2) {
526
-            $explodedPropertyPath[0] = $parts[0] . '.' . $parts[1];
527
-            $explodedPropertyPath[1] = $parts[2];
528
-            $fieldName = $explodedPropertyPath[0];
529
-        }
530
-
531
-        $parentKeyFieldName = $table->field($fieldName)->getForeignField();
532
-        $childTableName = $table->field($fieldName)->getForeignTable();
533
-
534
-        if ($childTableName === null) {
535
-            throw new InvalidRelationConfigurationException('The relation information for property "' . $fieldName . '" of class "' . $tableName . '" is missing.', 1353170925);
536
-        }
537
-
538
-        if ($table->field($fieldName)->hasOne()) { // includes relation "one-to-one" and "many-to-one"
539
-            // sometimes the opposite relation is not defined. We don't want to force this config for backward compatibility reasons.
540
-            // $parentKeyFieldName === null does the trick somehow. Before condition was if (isset($parentKeyFieldName))
541
-            if ($table->field($fieldName)->hasRelationManyToOne() || $parentKeyFieldName === null) {
542
-                $statementParts['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.' . $fieldName . '=' . $childTableName . '.uid';
543
-            } else {
544
-                $statementParts['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.uid=' . $childTableName . '.' . $parentKeyFieldName;
545
-            }
546
-        } elseif ($table->field($fieldName)->hasRelationManyToMany()) {
547
-            $relationTableName = $table->field($fieldName)->getManyToManyTable();
548
-
549
-            $parentKeyFieldName = $table->field($fieldName)->isOppositeRelation() ? 'uid_foreign' : 'uid_local';
550
-            $childKeyFieldName = !$table->field($fieldName)->isOppositeRelation() ? 'uid_foreign' : 'uid_local';
551
-
552
-            // MM table e.g sys_category_record_mm
553
-            $relationTableNameAlias = $this->generateAlias($relationTableName);
554
-            $join = sprintf(
555
-                'LEFT JOIN %s AS %s ON %s.uid=%s.%s', $relationTableName,
556
-                $relationTableNameAlias,
557
-                $tableName,
558
-                $relationTableNameAlias,
559
-                $parentKeyFieldName
560
-            );
561
-            $statementParts['unions'][$relationTableNameAlias] = $join;
562
-
563
-            // Foreign table e.g sys_category
564
-            $childTableNameAlias = $this->generateAlias($childTableName);
565
-            $this->currentChildTableNameAlias = $childTableNameAlias;
566
-            $join = sprintf(
567
-                'LEFT JOIN %s AS %s ON %s.%s=%s.uid',
568
-                $childTableName,
569
-                $childTableNameAlias,
570
-                $relationTableNameAlias,
571
-                $childKeyFieldName,
572
-                $childTableNameAlias
573
-            );
574
-            $statementParts['unions'][$childTableNameAlias] = $join;
575
-
576
-            // Find a possible table name for a MM condition.
577
-            $tableNameCondition = $table->field($fieldName)->getAdditionalTableNameCondition();
578
-            if ($tableNameCondition) {
579
-
580
-                // If we can find a source file name,  we can then retrieve more MM conditions from the TCA such as a field name.
581
-                $sourceFileName = $this->query->getSourceFieldName();
582
-                if (empty($sourceFileName)) {
583
-                    $additionalMMConditions = array(
584
-                        'tablenames' => $tableNameCondition,
585
-                    );
586
-                } else {
587
-                    $additionalMMConditions = Tca::table($tableNameCondition)->field($sourceFileName)->getAdditionalMMCondition();
588
-                }
589
-
590
-                foreach ($additionalMMConditions as $additionalFieldName => $additionalMMCondition) {
591
-                    $additionalJoin = sprintf(' AND %s.%s = "%s"', $relationTableNameAlias, $additionalFieldName, $additionalMMCondition);
592
-                    $statementParts['unions'][$relationTableNameAlias] .= $additionalJoin;
593
-
594
-                    $additionalJoin = sprintf(' AND %s.%s = "%s"', $relationTableNameAlias, $additionalFieldName, $additionalMMCondition);
595
-                    $statementParts['unions'][$childTableNameAlias] .= $additionalJoin;
596
-                }
597
-            }
598
-
599
-        } elseif ($table->field($fieldName)->hasMany()) { // includes relations "many-to-one" and "csv" relations
600
-            $childTableNameAlias = $this->generateAlias($childTableName);
601
-            $this->currentChildTableNameAlias = $childTableNameAlias;
602
-
603
-            if (isset($parentKeyFieldName)) {
604
-                $join = sprintf(
605
-                    'LEFT JOIN %s AS %s ON %s.uid=%s.%s',
606
-                    $childTableName,
607
-                    $childTableNameAlias,
608
-                    $tableName,
609
-                    $childTableNameAlias,
610
-                    $parentKeyFieldName
611
-                );
612
-                $statementParts['unions'][$childTableNameAlias] = $join;
613
-            } else {
614
-                $join = sprintf(
615
-                    'LEFT JOIN %s AS %s ON (FIND_IN_SET(%s.uid, %s.%s))',
616
-                    $childTableName,
617
-                    $childTableNameAlias,
618
-                    $childTableNameAlias,
619
-                    $tableName,
620
-                    $fieldName
621
-                );
622
-                $statementParts['unions'][$childTableNameAlias] = $join;
623
-            }
624
-        } else {
625
-            throw new Exception('Could not determine type of relation.', 1252502725);
626
-        }
627
-
628
-        $statementParts['keywords']['distinct'] = 'DISTINCT';
629
-        $propertyPath = $explodedPropertyPath[1];
630
-        $tableName = $childTableName;
631
-    }
632
-
633
-    /**
634
-     * Returns the SQL operator for the given JCR operator type.
635
-     *
636
-     * @param string $operator One of the JCR_OPERATOR_* constants
637
-     * @return string an SQL operator
638
-     * @throws Exception
639
-     */
640
-    protected function resolveOperator($operator)
641
-    {
642
-        switch ($operator) {
643
-            case self::OPERATOR_EQUAL_TO_NULL:
644
-                $operator = 'IS';
645
-                break;
646
-            case self::OPERATOR_NOT_EQUAL_TO_NULL:
647
-                $operator = 'IS NOT';
648
-                break;
649
-            case QueryInterface::OPERATOR_IN:
650
-                $operator = 'IN';
651
-                break;
652
-            case QueryInterface::OPERATOR_EQUAL_TO:
653
-                $operator = '=';
654
-                break;
655
-            case QueryInterface::OPERATOR_NOT_EQUAL_TO:
656
-                $operator = '!=';
657
-                break;
658
-            case QueryInterface::OPERATOR_LESS_THAN:
659
-                $operator = '<';
660
-                break;
661
-            case QueryInterface::OPERATOR_LESS_THAN_OR_EQUAL_TO:
662
-                $operator = '<=';
663
-                break;
664
-            case QueryInterface::OPERATOR_GREATER_THAN:
665
-                $operator = '>';
666
-                break;
667
-            case QueryInterface::OPERATOR_GREATER_THAN_OR_EQUAL_TO:
668
-                $operator = '>=';
669
-                break;
670
-            case QueryInterface::OPERATOR_LIKE:
671
-                $operator = 'LIKE';
672
-                break;
673
-            default:
674
-                throw new Exception('Unsupported operator encountered.', 1242816073);
675
-        }
676
-        return $operator;
677
-    }
678
-
679
-    /**
680
-     * Adds additional WHERE statements according to the query settings.
681
-     *
682
-     * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
683
-     * @param string $tableNameOrAlias The table name to add the additional where clause for
684
-     * @param array &$statementParts
685
-     * @return void
686
-     */
687
-    protected function addAdditionalWhereClause(QuerySettingsInterface $querySettings, $tableNameOrAlias, &$statementParts)
688
-    {
689
-        $this->addVisibilityConstraintStatement($querySettings, $tableNameOrAlias, $statementParts);
690
-        if ($querySettings->getRespectSysLanguage()) {
691
-            $this->addSysLanguageStatement($tableNameOrAlias, $statementParts, $querySettings);
692
-        }
693
-    }
694
-
695
-    /**
696
-     * Adds enableFields and deletedClause to the query if necessary
697
-     *
698
-     * @param QuerySettingsInterface $querySettings
699
-     * @param string $tableNameOrAlias The database table name
700
-     * @param array &$statementParts The query parts
701
-     * @return void
702
-     */
703
-    protected function addVisibilityConstraintStatement(QuerySettingsInterface $querySettings, $tableNameOrAlias, array &$statementParts)
704
-    {
705
-        $statement = '';
706
-        $tableName = $this->resolveTableNameAlias($tableNameOrAlias);
707
-        if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
708
-            $ignoreEnableFields = $querySettings->getIgnoreEnableFields();
709
-            $enableFieldsToBeIgnored = $querySettings->getEnableFieldsToBeIgnored();
710
-            $includeDeleted = $querySettings->getIncludeDeleted();
711
-            if (ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend()) {
712
-                $statement .= $this->getFrontendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $enableFieldsToBeIgnored, $includeDeleted);
713
-            } else {
714
-                // 'BE' case
715
-                $statement .= $this->getBackendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $includeDeleted);
716
-            }
717
-
718
-            // Remove the prefixing "AND" if any.
719
-            if (!empty($statement)) {
720
-                $statement = strtolower(substr($statement, 1, 3)) === 'and' ? substr($statement, 5) : $statement;
721
-                $statementParts['additionalWhereClause'][$tableNameOrAlias][] = $statement;
722
-            }
723
-        }
724
-    }
725
-
726
-    /**
727
-     * Returns constraint statement for frontend context
728
-     *
729
-     * @param string $tableNameOrAlias
730
-     * @param boolean $ignoreEnableFields A flag indicating whether the enable fields should be ignored
731
-     * @param array $enableFieldsToBeIgnored If $ignoreEnableFields is true, this array specifies enable fields to be ignored. If it is null or an empty array (default) all enable fields are ignored.
732
-     * @param boolean $includeDeleted A flag indicating whether deleted records should be included
733
-     * @return string
734
-     * @throws Exception\InconsistentQuerySettingsException
735
-     */
736
-    protected function getFrontendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $enableFieldsToBeIgnored, $includeDeleted)
737
-    {
738
-        $statement = '';
739
-        $tableName = $this->resolveTableNameAlias($tableNameOrAlias);
740
-        if ($ignoreEnableFields && !$includeDeleted) {
741
-            if (count($enableFieldsToBeIgnored)) {
742
-                // array_combine() is necessary because of the way \TYPO3\CMS\Frontend\Page\PageRepository::enableFields() is implemented
743
-                $statement .= $this->getPageRepository()->enableFields($tableName, -1, array_combine($enableFieldsToBeIgnored, $enableFieldsToBeIgnored));
744
-            } else {
745
-                $statement .= $this->getPageRepository()->deleteClause($tableName);
746
-            }
747
-        } elseif (!$ignoreEnableFields && !$includeDeleted) {
748
-            $statement .= $this->getPageRepository()->enableFields($tableName);
749
-        } elseif (!$ignoreEnableFields && $includeDeleted) {
750
-            throw new InconsistentQuerySettingsException('Query setting "ignoreEnableFields=false" can not be used together with "includeDeleted=true" in frontend context.', 1327678173);
751
-        }
752
-        return $this->replaceTableNameByAlias($tableName, $tableNameOrAlias, $statement);
753
-    }
754
-
755
-    /**
756
-     * Returns constraint statement for backend context
757
-     *
758
-     * @param string $tableNameOrAlias
759
-     * @param boolean $ignoreEnableFields A flag indicating whether the enable fields should be ignored
760
-     * @param boolean $includeDeleted A flag indicating whether deleted records should be included
761
-     * @return string
762
-     */
763
-    protected function getBackendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $includeDeleted)
764
-    {
765
-        $tableName = $this->resolveTableNameAlias($tableNameOrAlias);
766
-        $statement = '';
767
-        if (!$ignoreEnableFields) {
768
-            $statement .= BackendUtility::BEenableFields($tableName);
769
-        }
770
-
771
-        // If the table is found to have "workspace" support, add the corresponding fields in the statement.
772
-        if (Tca::table($tableName)->hasWorkspaceSupport()) {
773
-            if ($this->getBackendUser()->workspace === 0) {
774
-                $statement .= ' AND ' . $tableName . '.t3ver_state<=' . new VersionState(VersionState::DEFAULT_STATE);
775
-            } else {
776
-                // Show only records of live and of the current workspace
777
-                // In case we are in a Versioning preview
778
-                $statement .= ' AND (' .
779
-                    $tableName . '.t3ver_wsid=0 OR ' .
780
-                    $tableName . '.t3ver_wsid=' . (int)$this->getBackendUser()->workspace .
781
-                    ')';
782
-            }
783
-
784
-            // Check if this segment make sense here or whether it should be in the "if" part when we have workspace = 0
785
-            $statement .= ' AND ' . $tableName . '.pid<>-1';
786
-        }
787
-
788
-        if (!$includeDeleted) {
789
-            $statement .= BackendUtility::deleteClause($tableName);
790
-        }
791
-
792
-        return $this->replaceTableNameByAlias($tableName, $tableNameOrAlias, $statement);
793
-    }
794
-
795
-    /**
796
-     * Builds the language field statement
797
-     *
798
-     * @param string $tableNameOrAlias The database table name
799
-     * @param array &$statementParts The query parts
800
-     * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
801
-     * @return void
802
-     * @throws Exception
803
-     */
804
-    protected function addSysLanguageStatement($tableNameOrAlias, array &$statementParts, $querySettings)
805
-    {
806
-        $tableName = $this->resolveTableNameAlias($tableNameOrAlias);
807
-        if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
808
-            if (!empty($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])) {
809
-                // Select all entries for the current language
810
-                $additionalWhereClause = $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . ' IN (' . intval($querySettings->getLanguageUid()) . ',-1)';
811
-                // If any language is set -> get those entries which are not translated yet
812
-                // They will be removed by t3lib_page::getRecordOverlay if not matching overlay mode
813
-                if (isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
814
-                    && $querySettings->getLanguageUid() > 0
815
-                ) {
816
-                    $additionalWhereClause .= ' OR (' . $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=0' .
817
-                        ' AND ' . $tableNameOrAlias . '.uid NOT IN (SELECT ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] .
818
-                        ' FROM ' . $tableName .
819
-                        ' WHERE ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] . '>0' .
820
-                        ' AND ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '>0';
821
-
822
-                    // Add delete clause to ensure all entries are loaded
823
-                    if (isset($GLOBALS['TCA'][$tableName]['ctrl']['delete'])) {
824
-                        $additionalWhereClause .= ' AND ' . $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['delete'] . '=0';
825
-                    }
826
-                    $additionalWhereClause .= '))';
827
-                }
828
-                $statementParts['additionalWhereClause'][$tableNameOrAlias][] = '(' . $additionalWhereClause . ')';
829
-            }
830
-        }
831
-    }
832
-
833
-    /**
834
-     * Transforms orderings into SQL.
835
-     *
836
-     * @param array $orderings An array of orderings (Tx_Extbase_Persistence_QOM_Ordering)
837
-     * @param SourceInterface $source The source
838
-     * @param array &$statementParts The query parts
839
-     * @return void
840
-     * @throws Exception\UnsupportedOrderException
841
-     */
842
-    protected function parseOrderings(array $orderings, SourceInterface $source, array &$statementParts)
843
-    {
844
-        foreach ($orderings as $fieldNameAndPath => $order) {
845
-            switch ($order) {
846
-                case QueryInterface::ORDER_ASCENDING:
847
-                    $order = 'ASC';
848
-                    break;
849
-                case QueryInterface::ORDER_DESCENDING:
850
-                    $order = 'DESC';
851
-                    break;
852
-                default:
853
-                    throw new UnsupportedOrderException('Unsupported order encountered.', 1456845126);
854
-            }
855
-
856
-            $tableName = $this->getFieldPathResolver()->getDataType($fieldNameAndPath, $this->query->getType());
857
-            $fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath, $tableName);
858
-            $statementParts['orderings'][] = sprintf('%s.%s %s', $tableName, $fieldName, $order);
859
-        }
860
-    }
861
-
862
-    /**
863
-     * Transforms limit and offset into SQL
864
-     *
865
-     * @param int $limit
866
-     * @param int $offset
867
-     * @param array &$statementParts
868
-     * @return void
869
-     */
870
-    protected function parseLimitAndOffset($limit, $offset, array &$statementParts)
871
-    {
872
-        if ($limit !== null && $offset !== null) {
873
-            $statementParts['limit'] = intval($offset) . ', ' . intval($limit);
874
-        } elseif ($limit !== null) {
875
-            $statementParts['limit'] = intval($limit);
876
-        }
877
-    }
878
-
879
-    /**
880
-     * @param array $rows
881
-     * @return array
882
-     */
883
-    protected function getContentObjects(array $rows): array
884
-    {
885
-        $contentObjects = [];
886
-        foreach ($rows as $row) {
887
-
888
-            // Get language uid from querySettings.
889
-            // Ensure the backend handling is not broken (fallback to Get parameter 'L' if needed)
890
-            $overlaidRow = $this->doLanguageAndWorkspaceOverlay(
891
-                $row,
892
-                $this->query->getTypo3QuerySettings()
893
-            );
894
-
895
-            $contentObjects[] = GeneralUtility::makeInstance(
896
-                Content::class,
897
-                $this->query->getType(),
898
-                $overlaidRow
899
-            );
900
-        }
901
-
902
-        return $contentObjects;
903
-    }
904
-
905
-    /**
906
-     * Performs workspace and language overlay on the given row array. The language and workspace id is automatically
907
-     * detected (depending on FE or BE context). You can also explicitly set the language/workspace id.
908
-     *
909
-     * @param array $row
910
-     * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
911
-     * @return array
912
-     */
913
-    protected function doLanguageAndWorkspaceOverlay(array $row, $querySettings)
914
-    {
915
-        $tableName = $this->getTableName();
916
-
917
-        $pageRepository = $this->getPageRepository();
918
-        if (is_object($GLOBALS['TSFE'])) {
919
-            $languageMode = $GLOBALS['TSFE']->sys_language_mode;
920
-            #if ($this->isBackendUserLogged() && $this->getBackendUser()->workspace !== 0) {
921
-            #    $pageRepository->versioningWorkspaceId = $this->getBackendUser()->workspace;
922
-            #}
923
-        } else {
924
-            $languageMode = '';
925
-            $workspaceUid = $this->getBackendUser()->workspace;
926
-            #$pageRepository->versioningWorkspaceId = $workspaceUid;
927
-            #if ($this->getBackendUser()->workspace !== 0) {
928
-            #    $pageRepository->versioningPreview = 1;
929
-            #}
930
-        }
931
-
932
-        // If current row is a translation select its parent
933
-        if (isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
934
-            && isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
935
-        ) {
936
-            if (isset($row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']])
937
-                && $row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']] > 0
938
-            ) {
939
-                $queryBuilder = $this->getQueryBuilder();
940
-                $row = $queryBuilder
941
-                    ->select($tableName . '.*')
942
-                    ->from($tableName)
943
-                    ->andWhere(
944
-                        $tableName . '.uid=' . (int)$row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']],
945
-                        $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . ' = 0'
946
-                    )
947
-                    ->execute()
948
-                    ->fetch();
949
-            }
950
-        }
951
-
952
-        // Retrieve the original uid; Used for Workspaces!
953
-        if (ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isBackend()) {
954
-            $pageRepository->versionOL($tableName, $row, true, true);
955
-        } else {
956
-            \TYPO3\CMS\Backend\Utility\BackendUtility::workspaceOL($tableName, $row);
957
-        }
958
-        if ($pageRepository->versioningPreview && isset($row['_ORIG_uid'])) {
959
-            $row['uid'] = $row['_ORIG_uid'];
960
-        }
961
-
962
-        // Special case for table "pages"
963
-        if ($tableName == 'pages') {
964
-            $row = $pageRepository->getPageOverlay($row, $querySettings->getLanguageUid());
965
-        } elseif (isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
966
-            && $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] !== ''
967
-        ) {
968
-            if (in_array($row[$GLOBALS['TCA'][$tableName]['ctrl']['languageField']], array(-1, 0))) {
969
-                $overlayMode = $languageMode === 'strict' ? 'hideNonTranslated' : '';
970
-                $row = $pageRepository->getRecordOverlay($tableName, $row, $querySettings->getLanguageUid(), $overlayMode);
971
-            }
972
-        }
973
-
974
-        return $row;
975
-    }
976
-
977
-    /**
978
-     * Return a resolved table name given a possible table name alias.
979
-     *
980
-     * @param string $tableNameOrAlias
981
-     * @return string
982
-     */
983
-    protected function resolveTableNameAlias($tableNameOrAlias)
984
-    {
985
-        $resolvedTableName = $tableNameOrAlias;
986
-        if (!empty($this->tableNameAliases['aliases'][$tableNameOrAlias])) {
987
-            $resolvedTableName = $this->tableNameAliases['aliases'][$tableNameOrAlias];
988
-        }
989
-        return $resolvedTableName;
990
-    }
991
-
992
-    /**
993
-     * Generate a unique table name alias for the given table name.
994
-     *
995
-     * @param string $tableName
996
-     * @return string
997
-     */
998
-    protected function generateAlias($tableName)
999
-    {
1000
-
1001
-        if (!isset($this->tableNameAliases['aliasIncrement'][$tableName])) {
1002
-            $this->tableNameAliases['aliasIncrement'][$tableName] = 0;
1003
-        }
1004
-
1005
-        $numberOfAliases = $this->tableNameAliases['aliasIncrement'][$tableName];
1006
-        $tableNameAlias = $tableName . $numberOfAliases;
1007
-
1008
-        $this->tableNameAliases['aliasIncrement'][$tableName]++;
1009
-        $this->tableNameAliases['aliases'][$tableNameAlias] = $tableName;
1010
-
1011
-        return $tableNameAlias;
1012
-    }
1013
-
1014
-    /**
1015
-     * Replace the table names by its table name alias within the given statement.
1016
-     *
1017
-     * @param string $tableName
1018
-     * @param string $tableNameAlias
1019
-     * @param string $statement
1020
-     * @return string
1021
-     */
1022
-    protected function replaceTableNameByAlias($tableName, $tableNameAlias, $statement)
1023
-    {
1024
-        if ($statement && $tableName !== $tableNameAlias) {
1025
-            $statement = str_replace($tableName, $tableNameAlias, $statement);
1026
-        }
1027
-        return $statement;
1028
-    }
1029
-
1030
-    /**
1031
-     * Returns an instance of the current Backend User.
1032
-     *
1033
-     * @return BackendUserAuthentication
1034
-     */
1035
-    protected function getBackendUser()
1036
-    {
1037
-        return $GLOBALS['BE_USER'];
1038
-    }
1039
-
1040
-    /**
1041
-     * Tell whether a Backend User is logged in.
1042
-     *
1043
-     * @return bool
1044
-     */
1045
-    protected function isBackendUserLogged()
1046
-    {
1047
-        return is_object($GLOBALS['BE_USER']);
1048
-    }
1049
-
1050
-    /**
1051
-     * @return PageRepository|object
1052
-     */
1053
-    protected function getPageRepository()
1054
-    {
1055
-        if (!$this->pageRepository instanceof PageRepository) {
1056
-            if (ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend() && is_object($GLOBALS['TSFE'])) {
1057
-                $this->pageRepository = $GLOBALS['TSFE']->sys_page;
1058
-            } else {
1059
-                $this->pageRepository = GeneralUtility::makeInstance(PageRepository::class);
1060
-            }
1061
-        }
1062
-
1063
-        return $this->pageRepository;
1064
-    }
1065
-
1066
-    /**
1067
-     * @return FieldPathResolver|object
1068
-     */
1069
-    protected function getFieldPathResolver()
1070
-    {
1071
-        return GeneralUtility::makeInstance(FieldPathResolver::class);
1072
-    }
1073
-
1074
-    /**
1075
-     * @return object|Connection
1076
-     */
1077
-    protected function getConnection(): Connection
1078
-    {
1079
-        /** @var ConnectionPool $connectionPool */
1080
-        return GeneralUtility::makeInstance(ConnectionPool::class)
1081
-            ->getConnectionForTable($this->getTableName());
1082
-    }
1083
-
1084
-    /**
1085
-     * @return object|QueryBuilder
1086
-     */
1087
-    protected function getQueryBuilder(): QueryBuilder
1088
-    {
1089
-        /** @var ConnectionPool $connectionPool */
1090
-        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
1091
-        return $connectionPool->getQueryBuilderForTable($this->getTableName());
1092
-    }
1093
-
1094
-    /**
1095
-     * @return string
1096
-     */
1097
-    public function getTableName(): string
1098
-    {
1099
-        return $this->query->getSource()->getNodeTypeName(); // getSelectorName()
1100
-    }
54
+	const OPERATOR_EQUAL_TO_NULL = 'operatorEqualToNull';
55
+	const OPERATOR_NOT_EQUAL_TO_NULL = 'operatorNotEqualToNull';
56
+
57
+	/**
58
+	 * The TYPO3 page repository. Used for language and workspace overlay
59
+	 *
60
+	 * @var PageRepository
61
+	 */
62
+	protected $pageRepository;
63
+
64
+	/**
65
+	 * @var Query
66
+	 */
67
+	protected $query;
68
+
69
+	/**
70
+	 * Store some info related to table name and its aliases.
71
+	 *
72
+	 * @var array
73
+	 */
74
+	protected $tableNameAliases = array(
75
+		'aliases' => [],
76
+		'aliasIncrement' => [],
77
+	);
78
+
79
+	/**
80
+	 * Use to store the current foreign table name alias.
81
+	 *
82
+	 * @var string
83
+	 */
84
+	protected $currentChildTableNameAlias = '';
85
+
86
+	/**
87
+	 * @param Query $query
88
+	 */
89
+	public function __construct(Query $query)
90
+	{
91
+		$this->query = $query;
92
+	}
93
+
94
+	/**
95
+	 * @param $parameters
96
+	 * @return array
97
+	 */
98
+	protected static function getTypes($parameters)
99
+	{
100
+		$types = [];
101
+		foreach ($parameters as $parameter) {
102
+			if (is_array($parameter)) {
103
+				if (MathUtility::canBeInterpretedAsInteger($parameter[0])) {
104
+					$types[] = \Doctrine\DBAL\Connection::PARAM_INT_ARRAY;
105
+				} else {
106
+					$types[] = \Doctrine\DBAL\Connection::PARAM_STR_ARRAY;
107
+				}
108
+			} else {
109
+				if (MathUtility::canBeInterpretedAsInteger($parameter)) {
110
+					$types[] = ParameterType::INTEGER;
111
+				} else {
112
+					$types[] = ParameterType::STRING;
113
+				}
114
+			}
115
+		}
116
+		return $types;
117
+	}
118
+
119
+	/**
120
+	 * Returns the result of the query
121
+	 */
122
+	public function fetchResult()
123
+	{
124
+		$parameters = [];
125
+		$statementParts = $this->parseQuery($parameters);
126
+		$statementParts = $this->processStatementStructureForRecursiveMMRelation($statementParts);
127
+		$sql = $this->buildQuery($statementParts);
128
+		//print $sql; exit();
129
+
130
+		$rows = $this->getConnection()
131
+			->executeQuery($sql, $parameters, self::getTypes($parameters))
132
+			->fetchAll();
133
+
134
+		return $this->getContentObjects($rows);
135
+	}
136
+
137
+	/**
138
+	 * Returns the number of tuples matching the query.
139
+	 *
140
+	 * @return int The number of matching tuples
141
+	 */
142
+	public function countResult()
143
+	{
144
+		$parameters = [];
145
+		$statementParts = $this->parseQuery($parameters);
146
+		$statementParts = $this->processStatementStructureForRecursiveMMRelation($statementParts);
147
+		$types = self::getTypes($parameters);
148
+
149
+		// if limit is set, we need to count the rows "manually" as COUNT(*) ignores LIMIT constraints
150
+		if (!empty($statementParts['limit'])) {
151
+			$sql = $this->buildQuery($statementParts);
152
+
153
+			$count = $this
154
+				->getConnection()
155
+				->executeQuery($sql, $parameters, $types)
156
+				->rowCount();
157
+		} else {
158
+			$statementParts['fields'] = array('COUNT(*)');
159
+			// having orderings without grouping is not compatible with non-MySQL DBMS
160
+			$statementParts['orderings'] = [];
161
+			if (isset($statementParts['keywords']['distinct'])) {
162
+				unset($statementParts['keywords']['distinct']);
163
+				$distinctField = $this->query->getDistinct() ? $this->query->getDistinct() : 'uid';
164
+				$statementParts['fields'] = array('COUNT(DISTINCT ' . $statementParts['mainTable'] . '.' . $distinctField . ')');
165
+			}
166
+
167
+			$sql = $this->buildQuery($statementParts);
168
+			$count = $this
169
+				->getConnection()
170
+				->executeQuery($sql, $parameters, $types)
171
+				->fetchColumn(0);
172
+		}
173
+		return (int)$count;
174
+	}
175
+
176
+	/**
177
+	 * Parses the query and returns the SQL statement parts.
178
+	 *
179
+	 * @param array &$parameters
180
+	 * @return array
181
+	 */
182
+	public function parseQuery(array &$parameters)
183
+	{
184
+		$statementParts = [];
185
+		$statementParts['keywords'] = [];
186
+		$statementParts['tables'] = [];
187
+		$statementParts['unions'] = [];
188
+		$statementParts['fields'] = [];
189
+		$statementParts['where'] = [];
190
+		$statementParts['additionalWhereClause'] = [];
191
+		$statementParts['orderings'] = [];
192
+		$statementParts['limit'] = [];
193
+		$query = $this->query;
194
+		$source = $query->getSource();
195
+		$this->parseSource($source, $statementParts);
196
+		$this->parseConstraint($query->getConstraint(), $source, $statementParts, $parameters);
197
+		$this->parseOrderings($query->getOrderings(), $source, $statementParts);
198
+		$this->parseLimitAndOffset($query->getLimit(), $query->getOffset(), $statementParts);
199
+		$tableNames = array_unique(array_keys($statementParts['tables'] + $statementParts['unions']));
200
+		foreach ($tableNames as $tableNameOrAlias) {
201
+			if (is_string($tableNameOrAlias) && strlen($tableNameOrAlias) > 0) {
202
+				$this->addAdditionalWhereClause($query->getTypo3QuerySettings(), $tableNameOrAlias, $statementParts);
203
+			}
204
+		}
205
+
206
+		return $statementParts;
207
+	}
208
+
209
+	/**
210
+	 * Fiddle with the statement structure to handle recursive MM relations.
211
+	 * For the recursive MM query to work, we must invert some values.
212
+	 * Let see if that is the best way of doing that...
213
+	 *
214
+	 * @param array $statementParts
215
+	 * @return array
216
+	 */
217
+	public function processStatementStructureForRecursiveMMRelation(array $statementParts)
218
+	{
219
+
220
+		if ($this->hasRecursiveMMRelation()) {
221
+			$tableName = $this->query->getType();
222
+
223
+			// In order the MM query to work for a recursive MM query, we must invert some values.
224
+			// tx_domain_model_foo0 (the alias) <--> tx_domain_model_foo (the origin table name)
225
+			$values = [];
226
+			foreach ($statementParts['fields'] as $key => $value) {
227
+				$values[$key] = str_replace($tableName, $tableName . '0', $value);
228
+			}
229
+			$statementParts['fields'] = $values;
230
+
231
+			// Same comment as above.
232
+			$values = [];
233
+			foreach ($statementParts['where'] as $key => $value) {
234
+				$values[$key] = str_replace($tableName . '0', $tableName, $value);
235
+			}
236
+			$statementParts['where'] = $values;
237
+
238
+			// We must be more restrictive by transforming the "left" union by "inner"
239
+			$values = [];
240
+			foreach ($statementParts['unions'] as $key => $value) {
241
+				$values[$key] = str_replace('LEFT JOIN', 'INNER JOIN', $value);
242
+			}
243
+			$statementParts['unions'] = $values;
244
+		}
245
+
246
+		return $statementParts;
247
+	}
248
+
249
+	/**
250
+	 * Tell whether there is a recursive MM relation.
251
+	 *
252
+	 * @return bool
253
+	 */
254
+	public function hasRecursiveMMRelation()
255
+	{
256
+		return isset($this->tableNameAliases['aliasIncrement'][$this->query->getType()]);
257
+
258
+	}
259
+
260
+	/**
261
+	 * Returns the statement, ready to be executed.
262
+	 *
263
+	 * @param array $statementParts The SQL statement parts
264
+	 * @return string The SQL statement
265
+	 */
266
+	public function buildQuery(array $statementParts)
267
+	{
268
+
269
+		// Add more statement to the UNION part.
270
+		if (!empty($statementParts['unions'])) {
271
+			foreach ($statementParts['unions'] as $tableName => $unionPart) {
272
+				if (!empty($statementParts['additionalWhereClause'][$tableName])) {
273
+					$statementParts['unions'][$tableName] .= ' AND ' . implode(' AND ', $statementParts['additionalWhereClause'][$tableName]);
274
+				}
275
+			}
276
+		}
277
+
278
+		$statement = 'SELECT ' . implode(' ', $statementParts['keywords']) . ' ' . implode(',', $statementParts['fields']) . ' FROM ' . implode(' ', $statementParts['tables']) . ' ' . implode(' ', $statementParts['unions']);
279
+		if (!empty($statementParts['where'])) {
280
+			$statement .= ' WHERE ' . implode('', $statementParts['where']);
281
+			if (!empty($statementParts['additionalWhereClause'][$this->query->getType()])) {
282
+				$statement .= ' AND ' . implode(' AND ', $statementParts['additionalWhereClause'][$this->query->getType()]);
283
+			}
284
+		} elseif (!empty($statementParts['additionalWhereClause'])) {
285
+			$statement .= ' WHERE ' . implode(' AND ', $statementParts['additionalWhereClause'][$this->query->getType()]);
286
+		}
287
+		if (!empty($statementParts['orderings'])) {
288
+			$statement .= ' ORDER BY ' . implode(', ', $statementParts['orderings']);
289
+		}
290
+		if (!empty($statementParts['limit'])) {
291
+			$statement .= ' LIMIT ' . $statementParts['limit'];
292
+		}
293
+
294
+		return $statement;
295
+	}
296
+
297
+	/**
298
+	 * Transforms a Query Source into SQL and parameter arrays
299
+	 *
300
+	 * @param SourceInterface $source The source
301
+	 * @param array &$sql
302
+	 * @return void
303
+	 */
304
+	protected function parseSource(SourceInterface $source, array &$sql)
305
+	{
306
+		$tableName = $this->getTableName();
307
+		$sql['fields'][$tableName] = $tableName . '.*';
308
+		if ($this->query->getDistinct()) {
309
+			$sql['fields'][$tableName] = $tableName . '.' . $this->query->getDistinct();
310
+			$sql['keywords']['distinct'] = 'DISTINCT';
311
+		}
312
+		$sql['tables'][$tableName] = $tableName;
313
+		$sql['mainTable'] = $tableName;
314
+	}
315
+
316
+	/**
317
+	 * Transforms a constraint into SQL and parameter arrays
318
+	 *
319
+	 * @param ConstraintInterface $constraint The constraint
320
+	 * @param SourceInterface $source The source
321
+	 * @param array &$statementParts The query parts
322
+	 * @param array &$parameters The parameters that will replace the markers
323
+	 * @return void
324
+	 */
325
+	protected function parseConstraint(ConstraintInterface $constraint = null, SourceInterface $source, array &$statementParts, array &$parameters)
326
+	{
327
+		if ($constraint instanceof AndInterface) {
328
+			$statementParts['where'][] = '(';
329
+			$this->parseConstraint($constraint->getConstraint1(), $source, $statementParts, $parameters);
330
+			$statementParts['where'][] = ' AND ';
331
+			$this->parseConstraint($constraint->getConstraint2(), $source, $statementParts, $parameters);
332
+			$statementParts['where'][] = ')';
333
+		} elseif ($constraint instanceof OrInterface) {
334
+			$statementParts['where'][] = '(';
335
+			$this->parseConstraint($constraint->getConstraint1(), $source, $statementParts, $parameters);
336
+			$statementParts['where'][] = ' OR ';
337
+			$this->parseConstraint($constraint->getConstraint2(), $source, $statementParts, $parameters);
338
+			$statementParts['where'][] = ')';
339
+		} elseif ($constraint instanceof NotInterface) {
340
+			$statementParts['where'][] = 'NOT (';
341
+			$this->parseConstraint($constraint->getConstraint(), $source, $statementParts, $parameters);
342
+			$statementParts['where'][] = ')';
343
+		} elseif ($constraint instanceof ComparisonInterface) {
344
+			$this->parseComparison($constraint, $source, $statementParts, $parameters);
345
+		}
346
+	}
347
+
348
+	/**
349
+	 * Parse a Comparison into SQL and parameter arrays.
350
+	 *
351
+	 * @param ComparisonInterface $comparison The comparison to parse
352
+	 * @param SourceInterface $source The source
353
+	 * @param array &$statementParts SQL query parts to add to
354
+	 * @param array &$parameters Parameters to bind to the SQL
355
+	 * @return void
356
+	 * @throws Exception\RepositoryException
357
+	 */
358
+	protected function parseComparison(ComparisonInterface $comparison, SourceInterface $source, array &$statementParts, array &$parameters)
359
+	{
360
+		$operand1 = $comparison->getOperand1();
361
+		$operator = $comparison->getOperator();
362
+		$operand2 = $comparison->getOperand2();
363
+		if ($operator === QueryInterface::OPERATOR_IN) {
364
+			$items = [];
365
+			$hasValue = false;
366
+			foreach ($operand2 as $value) {
367
+				$value = $this->getPlainValue($value);
368
+				if ($value !== null) {
369
+					$items[] = $value;
370
+					$hasValue = true;
371
+				}
372
+			}
373
+			if ($hasValue === false) {
374
+				$statementParts['where'][] = '1<>1';
375
+			} else {
376
+				$this->parseDynamicOperand($operand1, $operator, $source, $statementParts, $parameters, null);
377
+				$parameters[] = $items;
378
+			}
379
+		} elseif ($operator === QueryInterface::OPERATOR_CONTAINS) {
380
+			if ($operand2 === null) {
381
+				$statementParts['where'][] = '1<>1';
382
+			} else {
383
+				throw new \Exception('Not implemented! Contact extension author.', 1412931227);
384
+				# @todo re-implement me if necessary.
385
+				#$tableName = $this->query->getType();
386
+				#$propertyName = $operand1->getPropertyName();
387
+				#while (strpos($propertyName, '.') !== false) {
388
+				#	$this->addUnionStatement($tableName, $propertyName, $statementParts);
389
+				#}
390
+				#$columnName = $propertyName;
391
+				#$columnMap = $propertyName;
392
+				#$typeOfRelation = $columnMap instanceof ColumnMap ? $columnMap->getTypeOfRelation() : null;
393
+				#if ($typeOfRelation === ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
394
+				#	$relationTableName = $columnMap->getRelationTableName();
395
+				#	$statementParts['where'][] = $tableName . '.uid IN (SELECT ' . $columnMap->getParentKeyFieldName() . ' FROM ' . $relationTableName . ' WHERE ' . $columnMap->getChildKeyFieldName() . '=?)';
396
+				#	$parameters[] = intval($this->getPlainValue($operand2));
397
+				#} elseif ($typeOfRelation === ColumnMap::RELATION_HAS_MANY) {
398
+				#	$parentKeyFieldName = $columnMap->getParentKeyFieldName();
399
+				#	if (isset($parentKeyFieldName)) {
400
+				#		$childTableName = $columnMap->getChildTableName();
401
+				#		$statementParts['where'][] = $tableName . '.uid=(SELECT ' . $childTableName . '.' . $parentKeyFieldName . ' FROM ' . $childTableName . ' WHERE ' . $childTableName . '.uid=?)';
402
+				#		$parameters[] = intval($this->getPlainValue($operand2));
403
+				#	} else {
404
+				#		$statementParts['where'][] = 'FIND_IN_SET(?,' . $tableName . '.' . $columnName . ')';
405
+				#		$parameters[] = intval($this->getPlainValue($operand2));
406
+				#	}
407
+				#} else {
408
+				#	throw new Exception\RepositoryException('Unsupported or non-existing property name "' . $propertyName . '" used in relation matching.', 1327065745);
409
+				#}
410
+			}
411
+		} else {
412
+			if ($operand2 === null) {
413
+				if ($operator === QueryInterface::OPERATOR_EQUAL_TO) {
414
+					$operator = self::OPERATOR_EQUAL_TO_NULL;
415
+				} elseif ($operator === QueryInterface::OPERATOR_NOT_EQUAL_TO) {
416
+					$operator = self::OPERATOR_NOT_EQUAL_TO_NULL;
417
+				}
418
+			}
419
+			$this->parseDynamicOperand($operand1, $operator, $source, $statementParts, $parameters);
420
+			$parameters[] = $this->getPlainValue($operand2);
421
+		}
422
+	}
423
+
424
+	/**
425
+	 * Returns a plain value, i.e. objects are flattened if possible.
426
+	 *
427
+	 * @param mixed $input
428
+	 * @return mixed
429
+	 * @throws UnexpectedTypeException
430
+	 */
431
+	protected function getPlainValue($input)
432
+	{
433
+		if (is_array($input)) {
434
+			throw new UnexpectedTypeException('An array could not be converted to a plain value.', 1274799932);
435
+		}
436
+		if ($input instanceof \DateTime) {
437
+			return $input->format('U');
438
+		} elseif (is_object($input)) {
439
+			if ($input instanceof LazyLoadingProxy) {
440
+				$realInput = $input->_loadRealInstance();
441
+			} else {
442
+				$realInput = $input;
443
+			}
444
+			if ($realInput instanceof DomainObjectInterface) {
445
+				return $realInput->getUid();
446
+			} else {
447
+				throw new UnexpectedTypeException('An object of class "' . get_class($realInput) . '" could not be converted to a plain value.', 1274799934);
448
+			}
449
+		} elseif (is_bool($input)) {
450
+			return $input === true ? 1 : 0;
451
+		} else {
452
+			return $input;
453
+		}
454
+	}
455
+
456
+	/**
457
+	 * Parse a DynamicOperand into SQL and parameter arrays.
458
+	 *
459
+	 * @param DynamicOperandInterface $operand
460
+	 * @param string $operator One of the JCR_OPERATOR_* constants
461
+	 * @param SourceInterface $source The source
462
+	 * @param array &$statementParts The query parts
463
+	 * @param array &$parameters The parameters that will replace the markers
464
+	 * @param string $valueFunction an optional SQL function to apply to the operand value
465
+	 * @return void
466
+	 */
467
+	protected function parseDynamicOperand(DynamicOperandInterface $operand, $operator, SourceInterface $source, array &$statementParts, array &$parameters, $valueFunction = null)
468
+	{
469
+		if ($operand instanceof LowerCaseInterface) {
470
+			$this->parseDynamicOperand($operand->getOperand(), $operator, $source, $statementParts, $parameters, 'LOWER');
471
+		} elseif ($operand instanceof UpperCaseInterface) {
472
+			$this->parseDynamicOperand($operand->getOperand(), $operator, $source, $statementParts, $parameters, 'UPPER');
473
+		} elseif ($operand instanceof PropertyValueInterface) {
474
+			$propertyName = $operand->getPropertyName();
475
+
476
+			// Reset value.
477
+			$this->currentChildTableNameAlias = '';
478
+
479
+			if ($source instanceof SelectorInterface) {
480
+				$tableName = $this->query->getType();
481
+				while (strpos($propertyName, '.') !== false) {
482
+					$this->addUnionStatement($tableName, $propertyName, $statementParts);
483
+				}
484
+			} elseif ($source instanceof JoinInterface) {
485
+				$tableName = $source->getJoinCondition()->getSelector1Name();
486
+			}
487
+
488
+			$columnName = $propertyName;
489
+			$resolvedOperator = $this->resolveOperator($operator);
490
+			$constraintSQL = '';
491
+
492
+			$marker = $operator === QueryInterface::OPERATOR_IN
493
+				? '(?)'
494
+				: '?';
495
+
496
+			if ($valueFunction === null) {
497
+				$constraintSQL .= (!empty($tableName) ? $tableName . '.' : '') . $columnName . ' ' . $resolvedOperator . ' ' . $marker;
498
+			} else {
499
+				$constraintSQL .= $valueFunction . '(' . (!empty($tableName) ? $tableName . '.' : '') . $columnName . ') ' . $resolvedOperator . ' ' . $marker;
500
+			}
501
+
502
+			if (isset($tableName) && !empty($this->currentChildTableNameAlias)) {
503
+				$constraintSQL = $this->replaceTableNameByAlias($tableName, $this->currentChildTableNameAlias, $constraintSQL);
504
+			}
505
+			$statementParts['where'][] = $constraintSQL;
506
+		}
507
+	}
508
+
509
+	/**
510
+	 * @param string &$tableName
511
+	 * @param string &$propertyPath
512
+	 * @param array &$statementParts
513
+	 */
514
+	protected function addUnionStatement(&$tableName, &$propertyPath, array &$statementParts)
515
+	{
516
+
517
+		$table = Tca::table($tableName);
518
+
519
+		$explodedPropertyPath = explode('.', $propertyPath, 2);
520
+		$fieldName = $explodedPropertyPath[0];
521
+
522
+		// Field of type "group" are special because property path must contain the table name
523
+		// to determine the relation type. Example for sys_category, property path will look like "items.sys_file"
524
+		$parts = explode('.', $propertyPath, 3);
525
+		if ($table->field($fieldName)->isGroup() && count($parts) > 2) {
526
+			$explodedPropertyPath[0] = $parts[0] . '.' . $parts[1];
527
+			$explodedPropertyPath[1] = $parts[2];
528
+			$fieldName = $explodedPropertyPath[0];
529
+		}
530
+
531
+		$parentKeyFieldName = $table->field($fieldName)->getForeignField();
532
+		$childTableName = $table->field($fieldName)->getForeignTable();
533
+
534
+		if ($childTableName === null) {
535
+			throw new InvalidRelationConfigurationException('The relation information for property "' . $fieldName . '" of class "' . $tableName . '" is missing.', 1353170925);
536
+		}
537
+
538
+		if ($table->field($fieldName)->hasOne()) { // includes relation "one-to-one" and "many-to-one"
539
+			// sometimes the opposite relation is not defined. We don't want to force this config for backward compatibility reasons.
540
+			// $parentKeyFieldName === null does the trick somehow. Before condition was if (isset($parentKeyFieldName))
541
+			if ($table->field($fieldName)->hasRelationManyToOne() || $parentKeyFieldName === null) {
542
+				$statementParts['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.' . $fieldName . '=' . $childTableName . '.uid';
543
+			} else {
544
+				$statementParts['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.uid=' . $childTableName . '.' . $parentKeyFieldName;
545
+			}
546
+		} elseif ($table->field($fieldName)->hasRelationManyToMany()) {
547
+			$relationTableName = $table->field($fieldName)->getManyToManyTable();
548
+
549
+			$parentKeyFieldName = $table->field($fieldName)->isOppositeRelation() ? 'uid_foreign' : 'uid_local';
550
+			$childKeyFieldName = !$table->field($fieldName)->isOppositeRelation() ? 'uid_foreign' : 'uid_local';
551
+
552
+			// MM table e.g sys_category_record_mm
553
+			$relationTableNameAlias = $this->generateAlias($relationTableName);
554
+			$join = sprintf(
555
+				'LEFT JOIN %s AS %s ON %s.uid=%s.%s', $relationTableName,
556
+				$relationTableNameAlias,
557
+				$tableName,
558
+				$relationTableNameAlias,
559
+				$parentKeyFieldName
560
+			);
561
+			$statementParts['unions'][$relationTableNameAlias] = $join;
562
+
563
+			// Foreign table e.g sys_category
564
+			$childTableNameAlias = $this->generateAlias($childTableName);
565
+			$this->currentChildTableNameAlias = $childTableNameAlias;
566
+			$join = sprintf(
567
+				'LEFT JOIN %s AS %s ON %s.%s=%s.uid',
568
+				$childTableName,
569
+				$childTableNameAlias,
570
+				$relationTableNameAlias,
571
+				$childKeyFieldName,
572
+				$childTableNameAlias
573
+			);
574
+			$statementParts['unions'][$childTableNameAlias] = $join;
575
+
576
+			// Find a possible table name for a MM condition.
577
+			$tableNameCondition = $table->field($fieldName)->getAdditionalTableNameCondition();
578
+			if ($tableNameCondition) {
579
+
580
+				// If we can find a source file name,  we can then retrieve more MM conditions from the TCA such as a field name.
581
+				$sourceFileName = $this->query->getSourceFieldName();
582
+				if (empty($sourceFileName)) {
583
+					$additionalMMConditions = array(
584
+						'tablenames' => $tableNameCondition,
585
+					);
586
+				} else {
587
+					$additionalMMConditions = Tca::table($tableNameCondition)->field($sourceFileName)->getAdditionalMMCondition();
588
+				}
589
+
590
+				foreach ($additionalMMConditions as $additionalFieldName => $additionalMMCondition) {
591
+					$additionalJoin = sprintf(' AND %s.%s = "%s"', $relationTableNameAlias, $additionalFieldName, $additionalMMCondition);
592
+					$statementParts['unions'][$relationTableNameAlias] .= $additionalJoin;
593
+
594
+					$additionalJoin = sprintf(' AND %s.%s = "%s"', $relationTableNameAlias, $additionalFieldName, $additionalMMCondition);
595
+					$statementParts['unions'][$childTableNameAlias] .= $additionalJoin;
596
+				}
597
+			}
598
+
599
+		} elseif ($table->field($fieldName)->hasMany()) { // includes relations "many-to-one" and "csv" relations
600
+			$childTableNameAlias = $this->generateAlias($childTableName);
601
+			$this->currentChildTableNameAlias = $childTableNameAlias;
602
+
603
+			if (isset($parentKeyFieldName)) {
604
+				$join = sprintf(
605
+					'LEFT JOIN %s AS %s ON %s.uid=%s.%s',
606
+					$childTableName,
607
+					$childTableNameAlias,
608
+					$tableName,
609
+					$childTableNameAlias,
610
+					$parentKeyFieldName
611
+				);
612
+				$statementParts['unions'][$childTableNameAlias] = $join;
613
+			} else {
614
+				$join = sprintf(
615
+					'LEFT JOIN %s AS %s ON (FIND_IN_SET(%s.uid, %s.%s))',
616
+					$childTableName,
617
+					$childTableNameAlias,
618
+					$childTableNameAlias,
619
+					$tableName,
620
+					$fieldName
621
+				);
622
+				$statementParts['unions'][$childTableNameAlias] = $join;
623
+			}
624
+		} else {
625
+			throw new Exception('Could not determine type of relation.', 1252502725);
626
+		}
627
+
628
+		$statementParts['keywords']['distinct'] = 'DISTINCT';
629
+		$propertyPath = $explodedPropertyPath[1];
630
+		$tableName = $childTableName;
631
+	}
632
+
633
+	/**
634
+	 * Returns the SQL operator for the given JCR operator type.
635
+	 *
636
+	 * @param string $operator One of the JCR_OPERATOR_* constants
637
+	 * @return string an SQL operator
638
+	 * @throws Exception
639
+	 */
640
+	protected function resolveOperator($operator)
641
+	{
642
+		switch ($operator) {
643
+			case self::OPERATOR_EQUAL_TO_NULL:
644
+				$operator = 'IS';
645
+				break;
646
+			case self::OPERATOR_NOT_EQUAL_TO_NULL:
647
+				$operator = 'IS NOT';
648
+				break;
649
+			case QueryInterface::OPERATOR_IN:
650
+				$operator = 'IN';
651
+				break;
652
+			case QueryInterface::OPERATOR_EQUAL_TO:
653
+				$operator = '=';
654
+				break;
655
+			case QueryInterface::OPERATOR_NOT_EQUAL_TO:
656
+				$operator = '!=';
657
+				break;
658
+			case QueryInterface::OPERATOR_LESS_THAN:
659
+				$operator = '<';
660
+				break;
661
+			case QueryInterface::OPERATOR_LESS_THAN_OR_EQUAL_TO:
662
+				$operator = '<=';
663
+				break;
664
+			case QueryInterface::OPERATOR_GREATER_THAN:
665
+				$operator = '>';
666
+				break;
667
+			case QueryInterface::OPERATOR_GREATER_THAN_OR_EQUAL_TO:
668
+				$operator = '>=';
669
+				break;
670
+			case QueryInterface::OPERATOR_LIKE:
671
+				$operator = 'LIKE';
672
+				break;
673
+			default:
674
+				throw new Exception('Unsupported operator encountered.', 1242816073);
675
+		}
676
+		return $operator;
677
+	}
678
+
679
+	/**
680
+	 * Adds additional WHERE statements according to the query settings.
681
+	 *
682
+	 * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
683
+	 * @param string $tableNameOrAlias The table name to add the additional where clause for
684
+	 * @param array &$statementParts
685
+	 * @return void
686
+	 */
687
+	protected function addAdditionalWhereClause(QuerySettingsInterface $querySettings, $tableNameOrAlias, &$statementParts)
688
+	{
689
+		$this->addVisibilityConstraintStatement($querySettings, $tableNameOrAlias, $statementParts);
690
+		if ($querySettings->getRespectSysLanguage()) {
691
+			$this->addSysLanguageStatement($tableNameOrAlias, $statementParts, $querySettings);
692
+		}
693
+	}
694
+
695
+	/**
696
+	 * Adds enableFields and deletedClause to the query if necessary
697
+	 *
698
+	 * @param QuerySettingsInterface $querySettings
699
+	 * @param string $tableNameOrAlias The database table name
700
+	 * @param array &$statementParts The query parts
701
+	 * @return void
702
+	 */
703
+	protected function addVisibilityConstraintStatement(QuerySettingsInterface $querySettings, $tableNameOrAlias, array &$statementParts)
704
+	{
705
+		$statement = '';
706
+		$tableName = $this->resolveTableNameAlias($tableNameOrAlias);
707
+		if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
708
+			$ignoreEnableFields = $querySettings->getIgnoreEnableFields();
709
+			$enableFieldsToBeIgnored = $querySettings->getEnableFieldsToBeIgnored();
710
+			$includeDeleted = $querySettings->getIncludeDeleted();
711
+			if (ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend()) {
712
+				$statement .= $this->getFrontendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $enableFieldsToBeIgnored, $includeDeleted);
713
+			} else {
714
+				// 'BE' case
715
+				$statement .= $this->getBackendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $includeDeleted);
716
+			}
717
+
718
+			// Remove the prefixing "AND" if any.
719
+			if (!empty($statement)) {
720
+				$statement = strtolower(substr($statement, 1, 3)) === 'and' ? substr($statement, 5) : $statement;
721
+				$statementParts['additionalWhereClause'][$tableNameOrAlias][] = $statement;
722
+			}
723
+		}
724
+	}
725
+
726
+	/**
727
+	 * Returns constraint statement for frontend context
728
+	 *
729
+	 * @param string $tableNameOrAlias
730
+	 * @param boolean $ignoreEnableFields A flag indicating whether the enable fields should be ignored
731
+	 * @param array $enableFieldsToBeIgnored If $ignoreEnableFields is true, this array specifies enable fields to be ignored. If it is null or an empty array (default) all enable fields are ignored.
732
+	 * @param boolean $includeDeleted A flag indicating whether deleted records should be included
733
+	 * @return string
734
+	 * @throws Exception\InconsistentQuerySettingsException
735
+	 */
736
+	protected function getFrontendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $enableFieldsToBeIgnored, $includeDeleted)
737
+	{
738
+		$statement = '';
739
+		$tableName = $this->resolveTableNameAlias($tableNameOrAlias);
740
+		if ($ignoreEnableFields && !$includeDeleted) {
741
+			if (count($enableFieldsToBeIgnored)) {
742
+				// array_combine() is necessary because of the way \TYPO3\CMS\Frontend\Page\PageRepository::enableFields() is implemented
743
+				$statement .= $this->getPageRepository()->enableFields($tableName, -1, array_combine($enableFieldsToBeIgnored, $enableFieldsToBeIgnored));
744
+			} else {
745
+				$statement .= $this->getPageRepository()->deleteClause($tableName);
746
+			}
747
+		} elseif (!$ignoreEnableFields && !$includeDeleted) {
748
+			$statement .= $this->getPageRepository()->enableFields($tableName);
749
+		} elseif (!$ignoreEnableFields && $includeDeleted) {
750
+			throw new InconsistentQuerySettingsException('Query setting "ignoreEnableFields=false" can not be used together with "includeDeleted=true" in frontend context.', 1327678173);
751
+		}
752
+		return $this->replaceTableNameByAlias($tableName, $tableNameOrAlias, $statement);
753
+	}
754
+
755
+	/**
756
+	 * Returns constraint statement for backend context
757
+	 *
758
+	 * @param string $tableNameOrAlias
759
+	 * @param boolean $ignoreEnableFields A flag indicating whether the enable fields should be ignored
760
+	 * @param boolean $includeDeleted A flag indicating whether deleted records should be included
761
+	 * @return string
762
+	 */
763
+	protected function getBackendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $includeDeleted)
764
+	{
765
+		$tableName = $this->resolveTableNameAlias($tableNameOrAlias);
766
+		$statement = '';
767
+		if (!$ignoreEnableFields) {
768
+			$statement .= BackendUtility::BEenableFields($tableName);
769
+		}
770
+
771
+		// If the table is found to have "workspace" support, add the corresponding fields in the statement.
772
+		if (Tca::table($tableName)->hasWorkspaceSupport()) {
773
+			if ($this->getBackendUser()->workspace === 0) {
774
+				$statement .= ' AND ' . $tableName . '.t3ver_state<=' . new VersionState(VersionState::DEFAULT_STATE);
775
+			} else {
776
+				// Show only records of live and of the current workspace
777
+				// In case we are in a Versioning preview
778
+				$statement .= ' AND (' .
779
+					$tableName . '.t3ver_wsid=0 OR ' .
780
+					$tableName . '.t3ver_wsid=' . (int)$this->getBackendUser()->workspace .
781
+					')';
782
+			}
783
+
784
+			// Check if this segment make sense here or whether it should be in the "if" part when we have workspace = 0
785
+			$statement .= ' AND ' . $tableName . '.pid<>-1';
786
+		}
787
+
788
+		if (!$includeDeleted) {
789
+			$statement .= BackendUtility::deleteClause($tableName);
790
+		}
791
+
792
+		return $this->replaceTableNameByAlias($tableName, $tableNameOrAlias, $statement);
793
+	}
794
+
795
+	/**
796
+	 * Builds the language field statement
797
+	 *
798
+	 * @param string $tableNameOrAlias The database table name
799
+	 * @param array &$statementParts The query parts
800
+	 * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
801
+	 * @return void
802
+	 * @throws Exception
803
+	 */
804
+	protected function addSysLanguageStatement($tableNameOrAlias, array &$statementParts, $querySettings)
805
+	{
806
+		$tableName = $this->resolveTableNameAlias($tableNameOrAlias);
807
+		if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
808
+			if (!empty($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])) {
809
+				// Select all entries for the current language
810
+				$additionalWhereClause = $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . ' IN (' . intval($querySettings->getLanguageUid()) . ',-1)';
811
+				// If any language is set -> get those entries which are not translated yet
812
+				// They will be removed by t3lib_page::getRecordOverlay if not matching overlay mode
813
+				if (isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
814
+					&& $querySettings->getLanguageUid() > 0
815
+				) {
816
+					$additionalWhereClause .= ' OR (' . $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=0' .
817
+						' AND ' . $tableNameOrAlias . '.uid NOT IN (SELECT ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] .
818
+						' FROM ' . $tableName .
819
+						' WHERE ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] . '>0' .
820
+						' AND ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '>0';
821
+
822
+					// Add delete clause to ensure all entries are loaded
823
+					if (isset($GLOBALS['TCA'][$tableName]['ctrl']['delete'])) {
824
+						$additionalWhereClause .= ' AND ' . $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['delete'] . '=0';
825
+					}
826
+					$additionalWhereClause .= '))';
827
+				}
828
+				$statementParts['additionalWhereClause'][$tableNameOrAlias][] = '(' . $additionalWhereClause . ')';
829
+			}
830
+		}
831
+	}
832
+
833
+	/**
834
+	 * Transforms orderings into SQL.
835
+	 *
836
+	 * @param array $orderings An array of orderings (Tx_Extbase_Persistence_QOM_Ordering)
837
+	 * @param SourceInterface $source The source
838
+	 * @param array &$statementParts The query parts
839
+	 * @return void
840
+	 * @throws Exception\UnsupportedOrderException
841
+	 */
842
+	protected function parseOrderings(array $orderings, SourceInterface $source, array &$statementParts)
843
+	{
844
+		foreach ($orderings as $fieldNameAndPath => $order) {
845
+			switch ($order) {
846
+				case QueryInterface::ORDER_ASCENDING:
847
+					$order = 'ASC';
848
+					break;
849
+				case QueryInterface::ORDER_DESCENDING:
850
+					$order = 'DESC';
851
+					break;
852
+				default:
853
+					throw new UnsupportedOrderException('Unsupported order encountered.', 1456845126);
854
+			}
855
+
856
+			$tableName = $this->getFieldPathResolver()->getDataType($fieldNameAndPath, $this->query->getType());
857
+			$fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath, $tableName);
858
+			$statementParts['orderings'][] = sprintf('%s.%s %s', $tableName, $fieldName, $order);
859
+		}
860
+	}
861
+
862
+	/**
863
+	 * Transforms limit and offset into SQL
864
+	 *
865
+	 * @param int $limit
866
+	 * @param int $offset
867
+	 * @param array &$statementParts
868
+	 * @return void
869
+	 */
870
+	protected function parseLimitAndOffset($limit, $offset, array &$statementParts)
871
+	{
872
+		if ($limit !== null && $offset !== null) {
873
+			$statementParts['limit'] = intval($offset) . ', ' . intval($limit);
874
+		} elseif ($limit !== null) {
875
+			$statementParts['limit'] = intval($limit);
876
+		}
877
+	}
878
+
879
+	/**
880
+	 * @param array $rows
881
+	 * @return array
882
+	 */
883
+	protected function getContentObjects(array $rows): array
884
+	{
885
+		$contentObjects = [];
886
+		foreach ($rows as $row) {
887
+
888
+			// Get language uid from querySettings.
889
+			// Ensure the backend handling is not broken (fallback to Get parameter 'L' if needed)
890
+			$overlaidRow = $this->doLanguageAndWorkspaceOverlay(
891
+				$row,
892
+				$this->query->getTypo3QuerySettings()
893
+			);
894
+
895
+			$contentObjects[] = GeneralUtility::makeInstance(
896
+				Content::class,
897
+				$this->query->getType(),
898
+				$overlaidRow
899
+			);
900
+		}
901
+
902
+		return $contentObjects;
903
+	}
904
+
905
+	/**
906
+	 * Performs workspace and language overlay on the given row array. The language and workspace id is automatically
907
+	 * detected (depending on FE or BE context). You can also explicitly set the language/workspace id.
908
+	 *
909
+	 * @param array $row
910
+	 * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
911
+	 * @return array
912
+	 */
913
+	protected function doLanguageAndWorkspaceOverlay(array $row, $querySettings)
914
+	{
915
+		$tableName = $this->getTableName();
916
+
917
+		$pageRepository = $this->getPageRepository();
918
+		if (is_object($GLOBALS['TSFE'])) {
919
+			$languageMode = $GLOBALS['TSFE']->sys_language_mode;
920
+			#if ($this->isBackendUserLogged() && $this->getBackendUser()->workspace !== 0) {
921
+			#    $pageRepository->versioningWorkspaceId = $this->getBackendUser()->workspace;
922
+			#}
923
+		} else {
924
+			$languageMode = '';
925
+			$workspaceUid = $this->getBackendUser()->workspace;
926
+			#$pageRepository->versioningWorkspaceId = $workspaceUid;
927
+			#if ($this->getBackendUser()->workspace !== 0) {
928
+			#    $pageRepository->versioningPreview = 1;
929
+			#}
930
+		}
931
+
932
+		// If current row is a translation select its parent
933
+		if (isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
934
+			&& isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
935
+		) {
936
+			if (isset($row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']])
937
+				&& $row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']] > 0
938
+			) {
939
+				$queryBuilder = $this->getQueryBuilder();
940
+				$row = $queryBuilder
941
+					->select($tableName . '.*')
942
+					->from($tableName)
943
+					->andWhere(
944
+						$tableName . '.uid=' . (int)$row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']],
945
+						$tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . ' = 0'
946
+					)
947
+					->execute()
948
+					->fetch();
949
+			}
950
+		}
951
+
952
+		// Retrieve the original uid; Used for Workspaces!
953
+		if (ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isBackend()) {
954
+			$pageRepository->versionOL($tableName, $row, true, true);
955
+		} else {
956
+			\TYPO3\CMS\Backend\Utility\BackendUtility::workspaceOL($tableName, $row);
957
+		}
958
+		if ($pageRepository->versioningPreview && isset($row['_ORIG_uid'])) {
959
+			$row['uid'] = $row['_ORIG_uid'];
960
+		}
961
+
962
+		// Special case for table "pages"
963
+		if ($tableName == 'pages') {
964
+			$row = $pageRepository->getPageOverlay($row, $querySettings->getLanguageUid());
965
+		} elseif (isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
966
+			&& $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] !== ''
967
+		) {
968
+			if (in_array($row[$GLOBALS['TCA'][$tableName]['ctrl']['languageField']], array(-1, 0))) {
969
+				$overlayMode = $languageMode === 'strict' ? 'hideNonTranslated' : '';
970
+				$row = $pageRepository->getRecordOverlay($tableName, $row, $querySettings->getLanguageUid(), $overlayMode);
971
+			}
972
+		}
973
+
974
+		return $row;
975
+	}
976
+
977
+	/**
978
+	 * Return a resolved table name given a possible table name alias.
979
+	 *
980
+	 * @param string $tableNameOrAlias
981
+	 * @return string
982
+	 */
983
+	protected function resolveTableNameAlias($tableNameOrAlias)
984
+	{
985
+		$resolvedTableName = $tableNameOrAlias;
986
+		if (!empty($this->tableNameAliases['aliases'][$tableNameOrAlias])) {
987
+			$resolvedTableName = $this->tableNameAliases['aliases'][$tableNameOrAlias];
988
+		}
989
+		return $resolvedTableName;
990
+	}
991
+
992
+	/**
993
+	 * Generate a unique table name alias for the given table name.
994
+	 *
995
+	 * @param string $tableName
996
+	 * @return string
997
+	 */
998
+	protected function generateAlias($tableName)
999
+	{
1000
+
1001
+		if (!isset($this->tableNameAliases['aliasIncrement'][$tableName])) {
1002
+			$this->tableNameAliases['aliasIncrement'][$tableName] = 0;
1003
+		}
1004
+
1005
+		$numberOfAliases = $this->tableNameAliases['aliasIncrement'][$tableName];
1006
+		$tableNameAlias = $tableName . $numberOfAliases;
1007
+
1008
+		$this->tableNameAliases['aliasIncrement'][$tableName]++;
1009
+		$this->tableNameAliases['aliases'][$tableNameAlias] = $tableName;
1010
+
1011
+		return $tableNameAlias;
1012
+	}
1013
+
1014
+	/**
1015
+	 * Replace the table names by its table name alias within the given statement.
1016
+	 *
1017
+	 * @param string $tableName
1018
+	 * @param string $tableNameAlias
1019
+	 * @param string $statement
1020
+	 * @return string
1021
+	 */
1022
+	protected function replaceTableNameByAlias($tableName, $tableNameAlias, $statement)
1023
+	{
1024
+		if ($statement && $tableName !== $tableNameAlias) {
1025
+			$statement = str_replace($tableName, $tableNameAlias, $statement);
1026
+		}
1027
+		return $statement;
1028
+	}
1029
+
1030
+	/**
1031
+	 * Returns an instance of the current Backend User.
1032
+	 *
1033
+	 * @return BackendUserAuthentication
1034
+	 */
1035
+	protected function getBackendUser()
1036
+	{
1037
+		return $GLOBALS['BE_USER'];
1038
+	}
1039
+
1040
+	/**
1041
+	 * Tell whether a Backend User is logged in.
1042
+	 *
1043
+	 * @return bool
1044
+	 */
1045
+	protected function isBackendUserLogged()
1046
+	{
1047
+		return is_object($GLOBALS['BE_USER']);
1048
+	}
1049
+
1050
+	/**
1051
+	 * @return PageRepository|object
1052
+	 */
1053
+	protected function getPageRepository()
1054
+	{
1055
+		if (!$this->pageRepository instanceof PageRepository) {
1056
+			if (ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend() && is_object($GLOBALS['TSFE'])) {
1057
+				$this->pageRepository = $GLOBALS['TSFE']->sys_page;
1058
+			} else {
1059
+				$this->pageRepository = GeneralUtility::makeInstance(PageRepository::class);
1060
+			}
1061
+		}
1062
+
1063
+		return $this->pageRepository;
1064
+	}
1065
+
1066
+	/**
1067
+	 * @return FieldPathResolver|object
1068
+	 */
1069
+	protected function getFieldPathResolver()
1070
+	{
1071
+		return GeneralUtility::makeInstance(FieldPathResolver::class);
1072
+	}
1073
+
1074
+	/**
1075
+	 * @return object|Connection
1076
+	 */
1077
+	protected function getConnection(): Connection
1078
+	{
1079
+		/** @var ConnectionPool $connectionPool */
1080
+		return GeneralUtility::makeInstance(ConnectionPool::class)
1081
+			->getConnectionForTable($this->getTableName());
1082
+	}
1083
+
1084
+	/**
1085
+	 * @return object|QueryBuilder
1086
+	 */
1087
+	protected function getQueryBuilder(): QueryBuilder
1088
+	{
1089
+		/** @var ConnectionPool $connectionPool */
1090
+		$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
1091
+		return $connectionPool->getQueryBuilderForTable($this->getTableName());
1092
+	}
1093
+
1094
+	/**
1095
+	 * @return string
1096
+	 */
1097
+	public function getTableName(): string
1098
+	{
1099
+		return $this->query->getSource()->getNodeTypeName(); // getSelectorName()
1100
+	}
1101 1101
 
1102 1102
 }
Please login to merge, or discard this patch.
Classes/Service/BackendUserPreferenceService.php 1 patch
Indentation   +46 added lines, -46 removed lines patch added patch discarded remove patch
@@ -16,54 +16,54 @@
 block discarded – undo
16 16
 class BackendUserPreferenceService
17 17
 {
18 18
 
19
-    /**
20
-     * Returns a class instance
21
-     *
22
-     * @return \Fab\Vidi\Service\BackendUserPreferenceService|object
23
-     */
24
-    static public function getInstance()
25
-    {
26
-        return GeneralUtility::makeInstance(\Fab\Vidi\Service\BackendUserPreferenceService::class);
27
-    }
19
+	/**
20
+	 * Returns a class instance
21
+	 *
22
+	 * @return \Fab\Vidi\Service\BackendUserPreferenceService|object
23
+	 */
24
+	static public function getInstance()
25
+	{
26
+		return GeneralUtility::makeInstance(\Fab\Vidi\Service\BackendUserPreferenceService::class);
27
+	}
28 28
 
29
-    /**
30
-     * Returns a configuration key for the current BE User.
31
-     *
32
-     * @param string $key
33
-     * @return mixed
34
-     */
35
-    public function get($key)
36
-    {
37
-        $result = '';
38
-        if ($this->getBackendUser() && !empty($this->getBackendUser()->uc[$key])) {
39
-            $result = $this->getBackendUser()->uc[$key];
29
+	/**
30
+	 * Returns a configuration key for the current BE User.
31
+	 *
32
+	 * @param string $key
33
+	 * @return mixed
34
+	 */
35
+	public function get($key)
36
+	{
37
+		$result = '';
38
+		if ($this->getBackendUser() && !empty($this->getBackendUser()->uc[$key])) {
39
+			$result = $this->getBackendUser()->uc[$key];
40 40
 
41
-        }
42
-        return $result;
43
-    }
41
+		}
42
+		return $result;
43
+	}
44 44
 
45
-    /**
46
-     * Set a configuration for the current BE User.
47
-     *
48
-     * @param string $key
49
-     * @param mixed $value
50
-     * @return void
51
-     */
52
-    public function set($key, $value)
53
-    {
54
-        if ($this->getBackendUser()) {
55
-            $this->getBackendUser()->uc[$key] = $value;
56
-            $this->getBackendUser()->writeUC();
57
-        }
58
-    }
45
+	/**
46
+	 * Set a configuration for the current BE User.
47
+	 *
48
+	 * @param string $key
49
+	 * @param mixed $value
50
+	 * @return void
51
+	 */
52
+	public function set($key, $value)
53
+	{
54
+		if ($this->getBackendUser()) {
55
+			$this->getBackendUser()->uc[$key] = $value;
56
+			$this->getBackendUser()->writeUC();
57
+		}
58
+	}
59 59
 
60
-    /**
61
-     * Returns an instance of the current Backend User.
62
-     *
63
-     * @return BackendUserAuthentication
64
-     */
65
-    protected function getBackendUser()
66
-    {
67
-        return $GLOBALS['BE_USER'];
68
-    }
60
+	/**
61
+	 * Returns an instance of the current Backend User.
62
+	 *
63
+	 * @return BackendUserAuthentication
64
+	 */
65
+	protected function getBackendUser()
66
+	{
67
+		return $GLOBALS['BE_USER'];
68
+	}
69 69
 }
Please login to merge, or discard this patch.
ext_emconf.php 1 patch
Indentation   +23 added lines, -23 removed lines patch added patch discarded remove patch
@@ -1,27 +1,27 @@
 block discarded – undo
1 1
 <?php
2 2
 
3 3
 $EM_CONF[$_EXTKEY] = [
4
-    'title' => 'Versatile and Interactive Display - List Component',
5
-    'description' => 'Generic listing of records with versatile ways of interacting with the data, e.g. advanced filter, inline editing, mass editing, ... Veni, vidi, vici!',
6
-    'category' => 'module',
7
-    'author' => 'Fabien Udriot',
8
-    'author_email' => '[email protected]',
9
-    'state' => 'stable',
10
-    'version' => '6.0.0-dev',
11
-    'autoload' => [
12
-        'psr-4' => ['Fab\\Vidi\\' => 'Classes']
13
-    ],
14
-    'constraints' =>
15
-        [
16
-            'depends' =>
17
-                [
18
-                    'typo3' => '10.5.0-10.5.99',
19
-                ],
20
-            'conflicts' =>
21
-                [
22
-                ],
23
-            'suggests' =>
24
-                [
25
-                ],
26
-        ]
4
+	'title' => 'Versatile and Interactive Display - List Component',
5
+	'description' => 'Generic listing of records with versatile ways of interacting with the data, e.g. advanced filter, inline editing, mass editing, ... Veni, vidi, vici!',
6
+	'category' => 'module',
7
+	'author' => 'Fabien Udriot',
8
+	'author_email' => '[email protected]',
9
+	'state' => 'stable',
10
+	'version' => '6.0.0-dev',
11
+	'autoload' => [
12
+		'psr-4' => ['Fab\\Vidi\\' => 'Classes']
13
+	],
14
+	'constraints' =>
15
+		[
16
+			'depends' =>
17
+				[
18
+					'typo3' => '10.5.0-10.5.99',
19
+				],
20
+			'conflicts' =>
21
+				[
22
+				],
23
+			'suggests' =>
24
+				[
25
+				],
26
+		]
27 27
 ];
28 28
\ No newline at end of file
Please login to merge, or discard this patch.
Classes/Controller/ContentController.php 1 patch
Indentation   +745 added lines, -745 removed lines patch added patch discarded remove patch
@@ -44,750 +44,750 @@
 block discarded – undo
44 44
  */
45 45
 class ContentController extends ActionController
46 46
 {
47
-    /**
48
-     * Initialize every action.
49
-     */
50
-    public function initializeAction()
51
-    {
52
-        $pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
53
-        $pageRenderer->addInlineLanguageLabelFile('EXT:vidi/Resources/Private/Language/locallang.xlf');
54
-
55
-        // Configure property mapping to retrieve the file object.
56
-        if ($this->arguments->hasArgument('columns')) {
57
-
58
-            /** @var CsvToArrayConverter $typeConverter */
59
-            $typeConverter = GeneralUtility::makeInstance(CsvToArrayConverter::class);
60
-
61
-            $propertyMappingConfiguration = $this->arguments->getArgument('columns')->getPropertyMappingConfiguration();
62
-            $propertyMappingConfiguration->setTypeConverter($typeConverter);
63
-        }
64
-    }
65
-
66
-    /**
67
-     * List action for this controller.
68
-     *
69
-     * @return void
70
-     */
71
-    public function indexAction(): ResponseInterface
72
-    {
73
-        $dataType = $this->getModuleLoader()->getDataType();
74
-        $selections = $this->selectionRepository->findByDataTypeForCurrentBackendUser($dataType);
75
-        $this->view->assign('selections', $selections);
76
-
77
-        $columns = Tca::grid()->getFields();
78
-        $this->view->assign('columns', $columns);
79
-        $this->view->assign('numberOfColumns', count($columns));
80
-        return $this->htmlResponse();
81
-    }
82
-
83
-    /**
84
-     * List Row action for this controller. Output a json list of contents
85
-     *
86
-     * @param array $columns corresponds to columns to be rendered.
87
-     * @param array $matches
88
-     * @Validate("Fab\Vidi\Domain\Validator\ColumnsValidator", param="columns")
89
-     * @Validate("Fab\Vidi\Domain\Validator\MatchesValidator", param="matches")
90
-     * @return void
91
-     */
92
-    public function listAction(array $columns = [], $matches = []): ResponseInterface
93
-    {
94
-        // Initialize some objects related to the query.
95
-        $matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
96
-        $order = OrderObjectFactory::getInstance()->getOrder();
97
-        $pager = PagerObjectFactory::getInstance()->getPager();
98
-
99
-        // Fetch objects via the Content Service.
100
-        $contentService = $this->getContentService()->findBy($matcher, $order, $pager->getLimit(), $pager->getOffset());
101
-        $pager->setCount($contentService->getNumberOfObjects());
102
-
103
-        // Assign values.
104
-        $this->view->assign('columns', $columns);
105
-        $this->view->assign('objects', $contentService->getObjects());
106
-        $this->view->assign('numberOfObjects', $contentService->getNumberOfObjects());
107
-        $this->view->assign('pager', $pager);
108
-
109
-        $this->view->assign('response', $this->responseFactory->createResponse());
110
-        return $this->htmlResponse();
111
-    }
112
-
113
-    /**
114
-     * Retrieve Content objects first according to matching criteria and then "update" them.
115
-     * Important to notice the field name can contains a path, e.g. metadata.title and therefore must be analysed.
116
-     *
117
-     * Possible values for $matches:
118
-     * -----------------------------
119
-     *
120
-     * $matches = array(uid => 1), will be taken as $query->equals
121
-     * $matches = array(uid => 1,2,3), will be taken as $query->in
122
-     * $matches = array(field_name1 => bar, field_name2 => bax), will be separated by AND.
123
-     *
124
-     * Possible values for $content:
125
-     * -----------------------------
126
-     *
127
-     * $content = array(field_name => bar)
128
-     * $content = array(field_name => array(value1, value2)) <-- will be CSV converted by "value1,value2"
129
-     *
130
-     * @param string $fieldNameAndPath
131
-     * @param array $content
132
-     * @param array $matches
133
-     * @param string $savingBehavior
134
-     * @param int $language
135
-     * @param array $columns
136
-     * @throws InvalidKeyInArrayException
137
-     */
138
-    public function updateAction($fieldNameAndPath, array $content, array $matches = [], $savingBehavior = SavingBehavior::REPLACE, $language = 0, $columns = [])
139
-    {
140
-
141
-        // Instantiate the Matcher object according different rules.
142
-        $matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
143
-        $order = OrderObjectFactory::getInstance()->getOrder();
144
-
145
-        // Fetch objects via the Content Service.
146
-        $contentService = $this->getContentService()->findBy($matcher, $order);
147
-
148
-        // Get the real field that is going to be updated.
149
-        $updatedFieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath);
150
-
151
-        // Get result object for storing data along the processing.
152
-        $result = $this->getJsonResult();
153
-        $result->setNumberOfObjects($contentService->getNumberOfObjects());
154
-
155
-        foreach ($contentService->getObjects() as $index => $object) {
156
-
157
-            $identifier = $this->getContentObjectResolver()->getValue($object, $fieldNameAndPath, 'uid', $language);
158
-
159
-            // It could be the identifier is not found because the translation
160
-            // of the record does not yet exist when mass-editing
161
-            if ((int)$identifier <= 0) {
162
-                continue;
163
-            }
164
-
165
-            $dataType = $this->getContentObjectResolver()->getDataType($object, $fieldNameAndPath);
166
-
167
-            $signalResult = $this->emitProcessContentDataSignal($object, $fieldNameAndPath, $content, $index + 1, $savingBehavior, $language);
168
-            $contentData = $signalResult->getContentData();
169
-
170
-            // Add identifier to content data, required by TCEMain.
171
-            $contentData['uid'] = $identifier;
172
-
173
-            /** @var Content $dataObject */
174
-            $dataObject = GeneralUtility::makeInstance(Content::class, $dataType, $contentData);
175
-
176
-            // Properly update object.
177
-            ContentRepositoryFactory::getInstance($dataType)->update($dataObject);
178
-
179
-            // Get the possible error messages and store them.
180
-            $errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
181
-            $result->addErrorMessages($errorMessages);
182
-
183
-            // We only want to see the detail result if there is one object updated.
184
-            // Required for inline editing + it will display some useful info on the GUI in the flash messages.
185
-            if ($contentService->getNumberOfObjects() === 1) {
186
-
187
-                // Fetch the updated object from repository.
188
-                $updatedObject = ContentRepositoryFactory::getInstance()->findByUid($object->getUid());
189
-
190
-                // Re-fetch the updated result.
191
-                $updatedResult = $this->getContentObjectResolver()->getValue($updatedObject, $fieldNameAndPath, $updatedFieldName, $language);
192
-                if (is_array($updatedResult)) {
193
-                    $_updatedResult = []; // reset result set.
194
-
195
-                    /** @var Content $contentObject */
196
-                    foreach ($updatedResult as $contentObject) {
197
-                        $labelField = Tca::table($contentObject)->getLabelField();
198
-                        $values = array(
199
-                            'uid' => $contentObject->getUid(),
200
-                            'name' => $contentObject[$labelField],
201
-                        );
202
-                        $_updatedResult[] = $values;
203
-                    }
204
-
205
-                    $updatedResult = $_updatedResult;
206
-                }
207
-
208
-                $labelField = Tca::table($object)->getLabelField();
209
-
210
-                $processedObjectData = array(
211
-                    'uid' => $object->getUid(),
212
-                    'name' => $object[$labelField],
213
-                    'updatedField' => $fieldNameAndPath,
214
-                    'updatedValue' => $updatedResult,
215
-                );
216
-                $result->setProcessedObject($processedObjectData);
217
-
218
-                if (!empty($columns)) {
219
-                    /** @var Row $row */
220
-                    $row = GeneralUtility::makeInstance(Row::class, $columns);
221
-                    $result->setRow($row->render($updatedObject));
222
-                }
223
-            }
224
-        }
225
-
226
-        $response = $this->responseFactory->createResponse()
227
-            ->withHeader('Content-Type', 'application/json; charset=utf-8');
228
-        $response->getBody()->write(json_encode($result));
229
-        return $response;
230
-    }
231
-
232
-    /**
233
-     * Set the sorting of a record giving the previous object.
234
-     *
235
-     * @param array $matches
236
-     * @param int $previousIdentifier
237
-     */
238
-    public function sortAction(array $matches = [], $previousIdentifier = null)
239
-    {
240
-
241
-        $matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
242
-
243
-        // Fetch objects via the Content Service.
244
-        $contentService = $this->getContentService()->findBy($matcher);
245
-
246
-        // Compute the label field name of the table.
247
-        $tableTitleField = Tca::table()->getLabelField();
248
-
249
-        // Get result object for storing data along the processing.
250
-        $result = $this->getJsonResult();
251
-        $result->setNumberOfObjects($contentService->getNumberOfObjects());
252
-
253
-        foreach ($contentService->getObjects() as $object) {
254
-
255
-            // Store the first object, so that the "action" message can be more explicit when deleting only one record.
256
-            if ($contentService->getNumberOfObjects() === 1) {
257
-                $tableTitleValue = $object[$tableTitleField];
258
-                $processedObjectData = array(
259
-                    'uid' => $object->getUid(),
260
-                    'name' => $tableTitleValue,
261
-                );
262
-                $result->setProcessedObject($processedObjectData);
263
-            }
264
-
265
-            // The $target corresponds to the pid to move the records to.
266
-            // It can also be a negative value in case of sorting. The negative value would be the uid of its predecessor.
267
-            $target = is_null($previousIdentifier) ? $object->getPid() : (-(int)$previousIdentifier);
268
-
269
-            // Work out the object.
270
-            ContentRepositoryFactory::getInstance()->move($object, $target);
271
-
272
-            // Get the possible error messages and store them.
273
-            $errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
274
-            $result->addErrorMessages($errorMessages);
275
-        }
276
-
277
-        $response = $this->responseFactory->createResponse()
278
-            ->withHeader('Content-Type', 'application/json; charset=utf-8');
279
-        $response->getBody()->write(json_encode($result));
280
-        return $response;
281
-    }
282
-
283
-    /**
284
-     * Returns an editing form for a given field name of a Content object.
285
-     * Argument $fieldNameAndPath corresponds to the field name to be edited.
286
-     * Important to notice it can contains a path, e.g. metadata.title and therefore must be analysed.
287
-     *
288
-     * Possible values for $matches, refer to method "updateAction".
289
-     *
290
-     * @param string $fieldNameAndPath
291
-     * @param array $matches
292
-     * @param bool $hasRecursiveSelection
293
-     * @throws \Exception
294
-     */
295
-    public function editAction($fieldNameAndPath, array $matches = [], $hasRecursiveSelection = false): ResponseInterface
296
-    {
297
-
298
-        // Instantiate the Matcher object according different rules.
299
-        $matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
300
-
301
-        // Fetch objects via the Content Service.
302
-        $contentService = $this->getContentService()->findBy($matcher);
303
-
304
-        $dataType = $this->getFieldPathResolver()->getDataType($fieldNameAndPath);
305
-        $fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath);
306
-
307
-        $fieldType = Tca::table($dataType)->field($fieldName)->getType();
308
-        $this->view->assign('fieldType', ucfirst($fieldType));
309
-        $this->view->assign('dataType', $dataType);
310
-        $this->view->assign('fieldName', $fieldName);
311
-        $this->view->assign('matches', $matches);
312
-        $this->view->assign('fieldNameAndPath', $fieldNameAndPath);
313
-        $this->view->assign('numberOfObjects', $contentService->getNumberOfObjects());
314
-        $this->view->assign('hasRecursiveSelection', $hasRecursiveSelection);
315
-        $this->view->assign('editWholeSelection', empty($matches['uid'])); // necessary??
316
-
317
-        // Fetch content and its relations.
318
-        if ($fieldType === FieldType::MULTISELECT) {
319
-
320
-            $object = ContentRepositoryFactory::getInstance()->findOneBy($matcher);
321
-            $identifier = $this->getContentObjectResolver()->getValue($object, $fieldNameAndPath, 'uid');
322
-            $dataType = $this->getContentObjectResolver()->getDataType($object, $fieldNameAndPath);
323
-
324
-            $content = ContentRepositoryFactory::getInstance($dataType)->findByUid($identifier);
325
-
326
-            // Makes sure the object was retrieved. Security!
327
-            if (!$content) {
328
-                $message = sprintf('I could not retrieved content object of type "%s" with identifier %s.', $dataType, $identifier);
329
-                throw new \Exception($message, 1402350182);
330
-            }
331
-
332
-            $relatedDataType = Tca::table($dataType)->field($fieldName)->getForeignTable();
333
-
334
-            // Initialize the matcher object.
335
-            /** @var Matcher $matcher */
336
-            $matcher = GeneralUtility::makeInstance(Matcher::class, [], $relatedDataType);
337
-
338
-            // Default ordering for related data type.
339
-            $defaultOrderings = Tca::table($relatedDataType)->getDefaultOrderings();
340
-            /** @var Order $order */
341
-            $defaultOrder = GeneralUtility::makeInstance(Order::class, $defaultOrderings);
342
-
343
-            // Fetch related contents
344
-            $relatedContents = ContentRepositoryFactory::getInstance($relatedDataType)->findBy($matcher, $defaultOrder);
345
-
346
-            if (Tca::table($dataType)->field($fieldName)->isRenderModeTree()) {
347
-
348
-                $fieldConfiguration = Tca::table($dataType)->field($fieldName)->getConfiguration();
349
-                $parentField = $fieldConfiguration['treeConfig']['parentField'];
350
-
351
-                $flatTree = [];
352
-                foreach ($relatedContents as $node) {
353
-                    $flatTree[$node->getUid()] = array(
354
-                        'item' => $node,
355
-                        'parent' => $node[$parentField] ? $node[$parentField]['uid'] : null,
356
-                    );
357
-                }
358
-
359
-                $tree = [];
360
-
361
-                // If leaves are selected without its parents selected, those are shown as parent
362
-                foreach ($flatTree as $id => &$flatNode) {
363
-                    if (!isset($flatTree[$flatNode['parent']])) {
364
-                        $flatNode['parent'] = null;
365
-                    }
366
-                }
367
-
368
-                foreach ($flatTree as $id => &$node) {
369
-                    if ($node['parent'] === null) {
370
-                        $tree[$id] = &$node;
371
-                    } else {
372
-                        $flatTree[$node['parent']]['children'][$id] = &$node;
373
-                    }
374
-                }
375
-
376
-                $relatedContents = $tree;
377
-            }
378
-
379
-            $this->view->assign('content', $content);
380
-            $this->view->assign('relatedContents', $relatedContents);
381
-            $this->view->assign('relatedDataType', $relatedDataType);
382
-            $this->view->assign('relatedContentTitle', Tca::table($relatedDataType)->getTitle());
383
-            $this->view->assign(
384
-                'renderMode',
385
-                Tca::table($dataType)->field($fieldName)->isRenderModeTree() ? FieldType::TREE : null
386
-            );
387
-        }
388
-        return $this->htmlResponse();
389
-    }
390
-
391
-    /**
392
-     * Retrieve Content objects first according to matching criteria and then "delete" them.
393
-     *
394
-     * Possible values for $matches, refer to method "updateAction".
395
-     *
396
-     * @param array $matches
397
-     */
398
-    public function deleteAction(array $matches = [])
399
-    {
400
-
401
-        $matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
402
-
403
-        // Fetch objects via the Content Service.
404
-        $contentService = $this->getContentService()->findBy($matcher);
405
-
406
-        // Compute the label field name of the table.
407
-        $tableTitleField = Tca::table()->getLabelField();
408
-
409
-        // Get result object for storing data along the processing.
410
-        $result = $this->getJsonResult();
411
-        $result->setNumberOfObjects($contentService->getNumberOfObjects());
412
-
413
-        foreach ($contentService->getObjects() as $object) {
414
-
415
-            // Store the first object, so that the delete message can be more explicit when deleting only one record.
416
-            if ($contentService->getNumberOfObjects() === 1) {
417
-                $tableTitleValue = $object[$tableTitleField];
418
-                $processedObjectData = array(
419
-                    'uid' => $object->getUid(),
420
-                    'name' => $tableTitleValue,
421
-                );
422
-                $result->setProcessedObject($processedObjectData);
423
-            }
424
-
425
-            // Properly delete object.
426
-            ContentRepositoryFactory::getInstance()->remove($object);
427
-
428
-            // Get the possible error messages and store them.
429
-            $errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
430
-            $result->addErrorMessages($errorMessages);
431
-        }
432
-
433
-        $response = $this->responseFactory->createResponse()
434
-            ->withHeader('Content-Type', 'application/json; charset=utf-8');
435
-        $response->getBody()->write(json_encode($result));
436
-        return $response;
437
-    }
438
-
439
-    /**
440
-     * Retrieve Content objects first according to matching criteria and then "copy" them.
441
-     *
442
-     * Possible values for $matches, refer to method "updateAction".
443
-     *
444
-     * @param string $target
445
-     * @param array $matches
446
-     * @throws \Exception
447
-     * @return string
448
-     */
449
-    public function copyAction($target, array $matches = [])
450
-    {
451
-        // @todo
452
-        throw new \Exception('Not yet implemented', 1410192546);
453
-    }
454
-
455
-    /**
456
-     * Retrieve Content objects from the Clipboard then "copy" them according to the target.
457
-     *
458
-     * @param string $target
459
-     * @throws \Exception
460
-     */
461
-    public function copyClipboardAction($target)
462
-    {
463
-
464
-        // Retrieve matcher object from clipboard.
465
-        $matcher = $this->getClipboardService()->getMatcher();
466
-
467
-        // Fetch objects via the Content Service.
468
-        $contentService = $this->getContentService()->findBy($matcher);
469
-
470
-        // Compute the label field name of the table.
471
-        $tableTitleField = Tca::table()->getLabelField();
472
-
473
-        // Get result object for storing data along the processing.
474
-        $result = $this->getJsonResult();
475
-        $result->setNumberOfObjects($contentService->getNumberOfObjects());
476
-
477
-        foreach ($contentService->getObjects() as $object) {
478
-
479
-            // Store the first object, so that the "action" message can be more explicit when deleting only one record.
480
-            if ($contentService->getNumberOfObjects() === 1) {
481
-                $tableTitleValue = $object[$tableTitleField];
482
-                $processedObjectData = array(
483
-                    'uid' => $object->getUid(),
484
-                    'name' => $tableTitleValue,
485
-                );
486
-                $result->setProcessedObject($processedObjectData);
487
-            }
488
-
489
-            // Work out the object.
490
-            ContentRepositoryFactory::getInstance()->copy($object, $target);
491
-
492
-            // Get the possible error messages and store them.
493
-            $errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
494
-            $result->addErrorMessages($errorMessages);
495
-        }
496
-
497
-        // Flush Clipboard if told so.
498
-        if (GeneralUtility::_GP('flushClipboard')) {
499
-            $this->getClipboardService()->flush();
500
-        }
501
-
502
-        $response = $this->responseFactory->createResponse()
503
-            ->withHeader('Content-Type', 'application/json; charset=utf-8');
504
-        $response->getBody()->write(json_encode($result));
505
-        return $response;
506
-    }
507
-
508
-    /**
509
-     * Retrieve Content objects first according to matching criteria and then "move" them.
510
-     *
511
-     * Possible values for $matches, refer to method "updateAction".
512
-     *
513
-     * @param string $target
514
-     * @param array $matches
515
-     */
516
-    public function moveAction($target, array $matches = [])
517
-    {
518
-
519
-        $matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
520
-
521
-        // Fetch objects via the Content Service.
522
-        $contentService = $this->getContentService()->findBy($matcher);
523
-
524
-        // Compute the label field name of the table.
525
-        $tableTitleField = Tca::table()->getLabelField();
526
-
527
-        // Get result object for storing data along the processing.
528
-        $result = $this->getJsonResult();
529
-        $result->setNumberOfObjects($contentService->getNumberOfObjects());
530
-
531
-        foreach ($contentService->getObjects() as $object) {
532
-
533
-            // Store the first object, so that the "action" message can be more explicit when deleting only one record.
534
-            if ($contentService->getNumberOfObjects() === 1) {
535
-                $tableTitleValue = $object[$tableTitleField];
536
-                $processedObjectData = array(
537
-                    'uid' => $object->getUid(),
538
-                    'name' => $tableTitleValue,
539
-                );
540
-                $result->setProcessedObject($processedObjectData);
541
-            }
542
-
543
-            // Work out the object.
544
-            ContentRepositoryFactory::getInstance()->move($object, $target);
545
-
546
-            // Get the possible error messages and store them.
547
-            $errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
548
-            $result->addErrorMessages($errorMessages);
549
-        }
550
-
551
-        $response = $this->responseFactory->createResponse()
552
-            ->withHeader('Content-Type', 'application/json; charset=utf-8');
553
-        $response->getBody()->write(json_encode($result));
554
-        return $response;
555
-    }
556
-
557
-    /**
558
-     * Retrieve Content objects from the Clipboard then "move" them according to the target.
559
-     *
560
-     * @param string $target
561
-     */
562
-    public function moveClipboardAction($target)
563
-    {
564
-
565
-        // Retrieve matcher object from clipboard.
566
-        $matcher = $this->getClipboardService()->getMatcher();
567
-
568
-        // Fetch objects via the Content Service.
569
-        $contentService = $this->getContentService()->findBy($matcher);
570
-
571
-        // Compute the label field name of the table.
572
-        $tableTitleField = Tca::table()->getLabelField();
573
-
574
-        // Get result object for storing data along the processing.
575
-        $result = $this->getJsonResult();
576
-        $result->setNumberOfObjects($contentService->getNumberOfObjects());
577
-
578
-        foreach ($contentService->getObjects() as $object) {
579
-
580
-            // Store the first object, so that the "action" message can be more explicit when deleting only one record.
581
-            if ($contentService->getNumberOfObjects() === 1) {
582
-                $tableTitleValue = $object[$tableTitleField];
583
-                $processedObjectData = array(
584
-                    'uid' => $object->getUid(),
585
-                    'name' => $tableTitleValue,
586
-                );
587
-                $result->setProcessedObject($processedObjectData);
588
-            }
589
-
590
-            // Work out the object.
591
-            ContentRepositoryFactory::getInstance()->move($object, $target);
592
-
593
-            // Get the possible error messages and store them.
594
-            $errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
595
-            $result->addErrorMessages($errorMessages);
596
-        }
597
-
598
-        // Flush Clipboard if told so.
599
-        if (GeneralUtility::_GP('flushClipboard')) {
600
-            $this->getClipboardService()->flush();
601
-        }
602
-
603
-        $response = $this->responseFactory->createResponse()
604
-            ->withHeader('Content-Type', 'application/json; charset=utf-8');
605
-        $response->getBody()->write(json_encode($result));
606
-        return $response;
607
-    }
608
-
609
-    /**
610
-     * Retrieve Content objects first according to matching criteria and then "localize" them.
611
-     *
612
-     * Possible values for $matches, refer to method "updateAction".
613
-     *
614
-     * @param string $fieldNameAndPath
615
-     * @param array $matches
616
-     * @param int $language
617
-     * @throws \Exception
618
-     */
619
-    public function localizeAction($fieldNameAndPath, array $matches = [], $language = 0)
620
-    {
621
-
622
-        $matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
623
-
624
-        // Fetch objects via the Content Service.
625
-        $contentService = $this->getContentService()->findBy($matcher);
626
-
627
-        // Get result object for storing data along the processing.
628
-        $result = $this->getJsonResult();
629
-        $result->setNumberOfObjects($contentService->getNumberOfObjects());
630
-
631
-        foreach ($contentService->getObjects() as $object) {
632
-
633
-            $identifier = $this->getContentObjectResolver()->getValue($object, $fieldNameAndPath, 'uid');
634
-            $dataType = $this->getContentObjectResolver()->getDataType($object, $fieldNameAndPath);
635
-
636
-            // Fetch the source object to be localized.
637
-            /** @var Content $content */
638
-            $content = ContentRepositoryFactory::getInstance($dataType)->findByIdentifier($identifier);
639
-
640
-            // Makes sure the object was retrieved. Security!
641
-            if (!$content) {
642
-                $message = sprintf('Something went wrong when retrieving content "%s" with identifier "%s".', $dataType, $identifier);
643
-                throw new \Exception($message, 1412343097);
644
-            }
645
-
646
-            // Handover the localization to the Repository.
647
-            ContentRepositoryFactory::getInstance($dataType)->localize($content, $language);
648
-
649
-            // Get the possible error messages and store them.
650
-            $errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
651
-
652
-            // Redirect to TCEForm so that the BE User can do its job!
653
-            if ($contentService->getNumberOfObjects() === 1) {
654
-
655
-                if (!empty($errorMessages)) {
656
-                    $message = sprintf('Something went wrong when localizing content "%s" with identifier "%s". <br/>%s',
657
-                        $dataType,
658
-                        $identifier,
659
-                        implode('<br/>', $errorMessages)
660
-                    );
661
-                    throw new \Exception($message, 1412343098);
662
-                }
663
-
664
-                $localizedContent = $this->getLanguageService()->getLocalizedContent($content, $language);
665
-                if (empty($localizedContent)) {
666
-                    $message = sprintf('Oups! I could not retrieve localized content of type "%s" with identifier "%s"',
667
-                        $content->getDataType(),
668
-                        $content->getUid()
669
-                    );
670
-                    throw new \Exception($message, 1412343099);
671
-                }
672
-
673
-                /** @var EditUri $uri */
674
-                $uriRenderer = GeneralUtility::makeInstance(EditUri::class);
675
-                $uri = $uriRenderer->render($localizedContent);
676
-                HttpUtility::redirect($uri);
677
-                break; // no need to further continue
678
-            }
679
-
680
-            $result->addErrorMessages($errorMessages);
681
-        }
682
-
683
-        $response = $this->responseFactory->createResponse()
684
-            ->withHeader('Content-Type', 'application/json; charset=utf-8');
685
-        $response->getBody()->write(json_encode($result));
686
-        return $response;
687
-    }
688
-
689
-    /**
690
-     * Get the Vidi Module Loader.
691
-     *
692
-     * @return ContentService
693
-     */
694
-    protected function getContentService()
695
-    {
696
-        return GeneralUtility::makeInstance(ContentService::class);
697
-    }
698
-
699
-    /**
700
-     * @return ContentObjectResolver
701
-     */
702
-    protected function getContentObjectResolver()
703
-    {
704
-        return GeneralUtility::makeInstance(ContentObjectResolver::class);
705
-    }
706
-
707
-    /**
708
-     * @return FieldPathResolver
709
-     */
710
-    protected function getFieldPathResolver()
711
-    {
712
-        return GeneralUtility::makeInstance(FieldPathResolver::class);
713
-    }
714
-
715
-    /**
716
-     * @return JsonResult|object
717
-     */
718
-    protected function getJsonResult()
719
-    {
720
-        return GeneralUtility::makeInstance(JsonResult::class);
721
-    }
722
-
723
-    /**
724
-     * Signal that is called for post-processing content data send to the server for update.
725
-     *
726
-     * @param Content $contentObject
727
-     * @param $fieldNameAndPath
728
-     * @param $contentData
729
-     * @param $counter
730
-     * @param $savingBehavior
731
-     * @param $language
732
-     * @return ProcessContentDataSignalArguments
733
-     */
734
-    protected function emitProcessContentDataSignal(Content $contentObject, $fieldNameAndPath, $contentData, $counter, $savingBehavior, $language)
735
-    {
736
-
737
-        /** @var ProcessContentDataSignalArguments $signalArguments */
738
-        $signalArguments = GeneralUtility::makeInstance(ProcessContentDataSignalArguments::class);
739
-        $signalArguments->setContentObject($contentObject)
740
-            ->setFieldNameAndPath($fieldNameAndPath)
741
-            ->setContentData($contentData)
742
-            ->setCounter($counter)
743
-            ->setSavingBehavior($savingBehavior)
744
-            ->setLanguage($language);
745
-
746
-        $signalResult = $this->getSignalSlotDispatcher()->dispatch('Fab\Vidi\Controller\Backend\ContentController', 'processContentData', array($signalArguments));
747
-        return $signalResult[0];
748
-    }
749
-
750
-    /**
751
-     * Get the SignalSlot dispatcher.
752
-     *
753
-     * @return Dispatcher
754
-     */
755
-    protected function getSignalSlotDispatcher()
756
-    {
757
-        return GeneralUtility::makeInstance(Dispatcher::class);
758
-    }
759
-
760
-    /**
761
-     * Get the Clipboard service.
762
-     *
763
-     * @return ClipboardService
764
-     */
765
-    protected function getClipboardService()
766
-    {
767
-        return GeneralUtility::makeInstance(ClipboardService::class);
768
-    }
769
-
770
-    /**
771
-     * @return LanguageService
772
-     */
773
-    protected function getLanguageService()
774
-    {
775
-        return GeneralUtility::makeInstance(LanguageService::class);
776
-    }
777
-
778
-    /**
779
-     * Get the Vidi Module Loader.
780
-     *
781
-     * @return ModuleLoader
782
-     */
783
-    protected function getModuleLoader()
784
-    {
785
-        return GeneralUtility::makeInstance(ModuleLoader::class);
786
-    }
787
-
788
-    public function injectSelectionRepository(SelectionRepository $selectionRepository): void
789
-    {
790
-        $this->selectionRepository = $selectionRepository;
791
-    }
47
+	/**
48
+	 * Initialize every action.
49
+	 */
50
+	public function initializeAction()
51
+	{
52
+		$pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
53
+		$pageRenderer->addInlineLanguageLabelFile('EXT:vidi/Resources/Private/Language/locallang.xlf');
54
+
55
+		// Configure property mapping to retrieve the file object.
56
+		if ($this->arguments->hasArgument('columns')) {
57
+
58
+			/** @var CsvToArrayConverter $typeConverter */
59
+			$typeConverter = GeneralUtility::makeInstance(CsvToArrayConverter::class);
60
+
61
+			$propertyMappingConfiguration = $this->arguments->getArgument('columns')->getPropertyMappingConfiguration();
62
+			$propertyMappingConfiguration->setTypeConverter($typeConverter);
63
+		}
64
+	}
65
+
66
+	/**
67
+	 * List action for this controller.
68
+	 *
69
+	 * @return void
70
+	 */
71
+	public function indexAction(): ResponseInterface
72
+	{
73
+		$dataType = $this->getModuleLoader()->getDataType();
74
+		$selections = $this->selectionRepository->findByDataTypeForCurrentBackendUser($dataType);
75
+		$this->view->assign('selections', $selections);
76
+
77
+		$columns = Tca::grid()->getFields();
78
+		$this->view->assign('columns', $columns);
79
+		$this->view->assign('numberOfColumns', count($columns));
80
+		return $this->htmlResponse();
81
+	}
82
+
83
+	/**
84
+	 * List Row action for this controller. Output a json list of contents
85
+	 *
86
+	 * @param array $columns corresponds to columns to be rendered.
87
+	 * @param array $matches
88
+	 * @Validate("Fab\Vidi\Domain\Validator\ColumnsValidator", param="columns")
89
+	 * @Validate("Fab\Vidi\Domain\Validator\MatchesValidator", param="matches")
90
+	 * @return void
91
+	 */
92
+	public function listAction(array $columns = [], $matches = []): ResponseInterface
93
+	{
94
+		// Initialize some objects related to the query.
95
+		$matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
96
+		$order = OrderObjectFactory::getInstance()->getOrder();
97
+		$pager = PagerObjectFactory::getInstance()->getPager();
98
+
99
+		// Fetch objects via the Content Service.
100
+		$contentService = $this->getContentService()->findBy($matcher, $order, $pager->getLimit(), $pager->getOffset());
101
+		$pager->setCount($contentService->getNumberOfObjects());
102
+
103
+		// Assign values.
104
+		$this->view->assign('columns', $columns);
105
+		$this->view->assign('objects', $contentService->getObjects());
106
+		$this->view->assign('numberOfObjects', $contentService->getNumberOfObjects());
107
+		$this->view->assign('pager', $pager);
108
+
109
+		$this->view->assign('response', $this->responseFactory->createResponse());
110
+		return $this->htmlResponse();
111
+	}
112
+
113
+	/**
114
+	 * Retrieve Content objects first according to matching criteria and then "update" them.
115
+	 * Important to notice the field name can contains a path, e.g. metadata.title and therefore must be analysed.
116
+	 *
117
+	 * Possible values for $matches:
118
+	 * -----------------------------
119
+	 *
120
+	 * $matches = array(uid => 1), will be taken as $query->equals
121
+	 * $matches = array(uid => 1,2,3), will be taken as $query->in
122
+	 * $matches = array(field_name1 => bar, field_name2 => bax), will be separated by AND.
123
+	 *
124
+	 * Possible values for $content:
125
+	 * -----------------------------
126
+	 *
127
+	 * $content = array(field_name => bar)
128
+	 * $content = array(field_name => array(value1, value2)) <-- will be CSV converted by "value1,value2"
129
+	 *
130
+	 * @param string $fieldNameAndPath
131
+	 * @param array $content
132
+	 * @param array $matches
133
+	 * @param string $savingBehavior
134
+	 * @param int $language
135
+	 * @param array $columns
136
+	 * @throws InvalidKeyInArrayException
137
+	 */
138
+	public function updateAction($fieldNameAndPath, array $content, array $matches = [], $savingBehavior = SavingBehavior::REPLACE, $language = 0, $columns = [])
139
+	{
140
+
141
+		// Instantiate the Matcher object according different rules.
142
+		$matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
143
+		$order = OrderObjectFactory::getInstance()->getOrder();
144
+
145
+		// Fetch objects via the Content Service.
146
+		$contentService = $this->getContentService()->findBy($matcher, $order);
147
+
148
+		// Get the real field that is going to be updated.
149
+		$updatedFieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath);
150
+
151
+		// Get result object for storing data along the processing.
152
+		$result = $this->getJsonResult();
153
+		$result->setNumberOfObjects($contentService->getNumberOfObjects());
154
+
155
+		foreach ($contentService->getObjects() as $index => $object) {
156
+
157
+			$identifier = $this->getContentObjectResolver()->getValue($object, $fieldNameAndPath, 'uid', $language);
158
+
159
+			// It could be the identifier is not found because the translation
160
+			// of the record does not yet exist when mass-editing
161
+			if ((int)$identifier <= 0) {
162
+				continue;
163
+			}
164
+
165
+			$dataType = $this->getContentObjectResolver()->getDataType($object, $fieldNameAndPath);
166
+
167
+			$signalResult = $this->emitProcessContentDataSignal($object, $fieldNameAndPath, $content, $index + 1, $savingBehavior, $language);
168
+			$contentData = $signalResult->getContentData();
169
+
170
+			// Add identifier to content data, required by TCEMain.
171
+			$contentData['uid'] = $identifier;
172
+
173
+			/** @var Content $dataObject */
174
+			$dataObject = GeneralUtility::makeInstance(Content::class, $dataType, $contentData);
175
+
176
+			// Properly update object.
177
+			ContentRepositoryFactory::getInstance($dataType)->update($dataObject);
178
+
179
+			// Get the possible error messages and store them.
180
+			$errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
181
+			$result->addErrorMessages($errorMessages);
182
+
183
+			// We only want to see the detail result if there is one object updated.
184
+			// Required for inline editing + it will display some useful info on the GUI in the flash messages.
185
+			if ($contentService->getNumberOfObjects() === 1) {
186
+
187
+				// Fetch the updated object from repository.
188
+				$updatedObject = ContentRepositoryFactory::getInstance()->findByUid($object->getUid());
189
+
190
+				// Re-fetch the updated result.
191
+				$updatedResult = $this->getContentObjectResolver()->getValue($updatedObject, $fieldNameAndPath, $updatedFieldName, $language);
192
+				if (is_array($updatedResult)) {
193
+					$_updatedResult = []; // reset result set.
194
+
195
+					/** @var Content $contentObject */
196
+					foreach ($updatedResult as $contentObject) {
197
+						$labelField = Tca::table($contentObject)->getLabelField();
198
+						$values = array(
199
+							'uid' => $contentObject->getUid(),
200
+							'name' => $contentObject[$labelField],
201
+						);
202
+						$_updatedResult[] = $values;
203
+					}
204
+
205
+					$updatedResult = $_updatedResult;
206
+				}
207
+
208
+				$labelField = Tca::table($object)->getLabelField();
209
+
210
+				$processedObjectData = array(
211
+					'uid' => $object->getUid(),
212
+					'name' => $object[$labelField],
213
+					'updatedField' => $fieldNameAndPath,
214
+					'updatedValue' => $updatedResult,
215
+				);
216
+				$result->setProcessedObject($processedObjectData);
217
+
218
+				if (!empty($columns)) {
219
+					/** @var Row $row */
220
+					$row = GeneralUtility::makeInstance(Row::class, $columns);
221
+					$result->setRow($row->render($updatedObject));
222
+				}
223
+			}
224
+		}
225
+
226
+		$response = $this->responseFactory->createResponse()
227
+			->withHeader('Content-Type', 'application/json; charset=utf-8');
228
+		$response->getBody()->write(json_encode($result));
229
+		return $response;
230
+	}
231
+
232
+	/**
233
+	 * Set the sorting of a record giving the previous object.
234
+	 *
235
+	 * @param array $matches
236
+	 * @param int $previousIdentifier
237
+	 */
238
+	public function sortAction(array $matches = [], $previousIdentifier = null)
239
+	{
240
+
241
+		$matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
242
+
243
+		// Fetch objects via the Content Service.
244
+		$contentService = $this->getContentService()->findBy($matcher);
245
+
246
+		// Compute the label field name of the table.
247
+		$tableTitleField = Tca::table()->getLabelField();
248
+
249
+		// Get result object for storing data along the processing.
250
+		$result = $this->getJsonResult();
251
+		$result->setNumberOfObjects($contentService->getNumberOfObjects());
252
+
253
+		foreach ($contentService->getObjects() as $object) {
254
+
255
+			// Store the first object, so that the "action" message can be more explicit when deleting only one record.
256
+			if ($contentService->getNumberOfObjects() === 1) {
257
+				$tableTitleValue = $object[$tableTitleField];
258
+				$processedObjectData = array(
259
+					'uid' => $object->getUid(),
260
+					'name' => $tableTitleValue,
261
+				);
262
+				$result->setProcessedObject($processedObjectData);
263
+			}
264
+
265
+			// The $target corresponds to the pid to move the records to.
266
+			// It can also be a negative value in case of sorting. The negative value would be the uid of its predecessor.
267
+			$target = is_null($previousIdentifier) ? $object->getPid() : (-(int)$previousIdentifier);
268
+
269
+			// Work out the object.
270
+			ContentRepositoryFactory::getInstance()->move($object, $target);
271
+
272
+			// Get the possible error messages and store them.
273
+			$errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
274
+			$result->addErrorMessages($errorMessages);
275
+		}
276
+
277
+		$response = $this->responseFactory->createResponse()
278
+			->withHeader('Content-Type', 'application/json; charset=utf-8');
279
+		$response->getBody()->write(json_encode($result));
280
+		return $response;
281
+	}
282
+
283
+	/**
284
+	 * Returns an editing form for a given field name of a Content object.
285
+	 * Argument $fieldNameAndPath corresponds to the field name to be edited.
286
+	 * Important to notice it can contains a path, e.g. metadata.title and therefore must be analysed.
287
+	 *
288
+	 * Possible values for $matches, refer to method "updateAction".
289
+	 *
290
+	 * @param string $fieldNameAndPath
291
+	 * @param array $matches
292
+	 * @param bool $hasRecursiveSelection
293
+	 * @throws \Exception
294
+	 */
295
+	public function editAction($fieldNameAndPath, array $matches = [], $hasRecursiveSelection = false): ResponseInterface
296
+	{
297
+
298
+		// Instantiate the Matcher object according different rules.
299
+		$matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
300
+
301
+		// Fetch objects via the Content Service.
302
+		$contentService = $this->getContentService()->findBy($matcher);
303
+
304
+		$dataType = $this->getFieldPathResolver()->getDataType($fieldNameAndPath);
305
+		$fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath);
306
+
307
+		$fieldType = Tca::table($dataType)->field($fieldName)->getType();
308
+		$this->view->assign('fieldType', ucfirst($fieldType));
309
+		$this->view->assign('dataType', $dataType);
310
+		$this->view->assign('fieldName', $fieldName);
311
+		$this->view->assign('matches', $matches);
312
+		$this->view->assign('fieldNameAndPath', $fieldNameAndPath);
313
+		$this->view->assign('numberOfObjects', $contentService->getNumberOfObjects());
314
+		$this->view->assign('hasRecursiveSelection', $hasRecursiveSelection);
315
+		$this->view->assign('editWholeSelection', empty($matches['uid'])); // necessary??
316
+
317
+		// Fetch content and its relations.
318
+		if ($fieldType === FieldType::MULTISELECT) {
319
+
320
+			$object = ContentRepositoryFactory::getInstance()->findOneBy($matcher);
321
+			$identifier = $this->getContentObjectResolver()->getValue($object, $fieldNameAndPath, 'uid');
322
+			$dataType = $this->getContentObjectResolver()->getDataType($object, $fieldNameAndPath);
323
+
324
+			$content = ContentRepositoryFactory::getInstance($dataType)->findByUid($identifier);
325
+
326
+			// Makes sure the object was retrieved. Security!
327
+			if (!$content) {
328
+				$message = sprintf('I could not retrieved content object of type "%s" with identifier %s.', $dataType, $identifier);
329
+				throw new \Exception($message, 1402350182);
330
+			}
331
+
332
+			$relatedDataType = Tca::table($dataType)->field($fieldName)->getForeignTable();
333
+
334
+			// Initialize the matcher object.
335
+			/** @var Matcher $matcher */
336
+			$matcher = GeneralUtility::makeInstance(Matcher::class, [], $relatedDataType);
337
+
338
+			// Default ordering for related data type.
339
+			$defaultOrderings = Tca::table($relatedDataType)->getDefaultOrderings();
340
+			/** @var Order $order */
341
+			$defaultOrder = GeneralUtility::makeInstance(Order::class, $defaultOrderings);
342
+
343
+			// Fetch related contents
344
+			$relatedContents = ContentRepositoryFactory::getInstance($relatedDataType)->findBy($matcher, $defaultOrder);
345
+
346
+			if (Tca::table($dataType)->field($fieldName)->isRenderModeTree()) {
347
+
348
+				$fieldConfiguration = Tca::table($dataType)->field($fieldName)->getConfiguration();
349
+				$parentField = $fieldConfiguration['treeConfig']['parentField'];
350
+
351
+				$flatTree = [];
352
+				foreach ($relatedContents as $node) {
353
+					$flatTree[$node->getUid()] = array(
354
+						'item' => $node,
355
+						'parent' => $node[$parentField] ? $node[$parentField]['uid'] : null,
356
+					);
357
+				}
358
+
359
+				$tree = [];
360
+
361
+				// If leaves are selected without its parents selected, those are shown as parent
362
+				foreach ($flatTree as $id => &$flatNode) {
363
+					if (!isset($flatTree[$flatNode['parent']])) {
364
+						$flatNode['parent'] = null;
365
+					}
366
+				}
367
+
368
+				foreach ($flatTree as $id => &$node) {
369
+					if ($node['parent'] === null) {
370
+						$tree[$id] = &$node;
371
+					} else {
372
+						$flatTree[$node['parent']]['children'][$id] = &$node;
373
+					}
374
+				}
375
+
376
+				$relatedContents = $tree;
377
+			}
378
+
379
+			$this->view->assign('content', $content);
380
+			$this->view->assign('relatedContents', $relatedContents);
381
+			$this->view->assign('relatedDataType', $relatedDataType);
382
+			$this->view->assign('relatedContentTitle', Tca::table($relatedDataType)->getTitle());
383
+			$this->view->assign(
384
+				'renderMode',
385
+				Tca::table($dataType)->field($fieldName)->isRenderModeTree() ? FieldType::TREE : null
386
+			);
387
+		}
388
+		return $this->htmlResponse();
389
+	}
390
+
391
+	/**
392
+	 * Retrieve Content objects first according to matching criteria and then "delete" them.
393
+	 *
394
+	 * Possible values for $matches, refer to method "updateAction".
395
+	 *
396
+	 * @param array $matches
397
+	 */
398
+	public function deleteAction(array $matches = [])
399
+	{
400
+
401
+		$matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
402
+
403
+		// Fetch objects via the Content Service.
404
+		$contentService = $this->getContentService()->findBy($matcher);
405
+
406
+		// Compute the label field name of the table.
407
+		$tableTitleField = Tca::table()->getLabelField();
408
+
409
+		// Get result object for storing data along the processing.
410
+		$result = $this->getJsonResult();
411
+		$result->setNumberOfObjects($contentService->getNumberOfObjects());
412
+
413
+		foreach ($contentService->getObjects() as $object) {
414
+
415
+			// Store the first object, so that the delete message can be more explicit when deleting only one record.
416
+			if ($contentService->getNumberOfObjects() === 1) {
417
+				$tableTitleValue = $object[$tableTitleField];
418
+				$processedObjectData = array(
419
+					'uid' => $object->getUid(),
420
+					'name' => $tableTitleValue,
421
+				);
422
+				$result->setProcessedObject($processedObjectData);
423
+			}
424
+
425
+			// Properly delete object.
426
+			ContentRepositoryFactory::getInstance()->remove($object);
427
+
428
+			// Get the possible error messages and store them.
429
+			$errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
430
+			$result->addErrorMessages($errorMessages);
431
+		}
432
+
433
+		$response = $this->responseFactory->createResponse()
434
+			->withHeader('Content-Type', 'application/json; charset=utf-8');
435
+		$response->getBody()->write(json_encode($result));
436
+		return $response;
437
+	}
438
+
439
+	/**
440
+	 * Retrieve Content objects first according to matching criteria and then "copy" them.
441
+	 *
442
+	 * Possible values for $matches, refer to method "updateAction".
443
+	 *
444
+	 * @param string $target
445
+	 * @param array $matches
446
+	 * @throws \Exception
447
+	 * @return string
448
+	 */
449
+	public function copyAction($target, array $matches = [])
450
+	{
451
+		// @todo
452
+		throw new \Exception('Not yet implemented', 1410192546);
453
+	}
454
+
455
+	/**
456
+	 * Retrieve Content objects from the Clipboard then "copy" them according to the target.
457
+	 *
458
+	 * @param string $target
459
+	 * @throws \Exception
460
+	 */
461
+	public function copyClipboardAction($target)
462
+	{
463
+
464
+		// Retrieve matcher object from clipboard.
465
+		$matcher = $this->getClipboardService()->getMatcher();
466
+
467
+		// Fetch objects via the Content Service.
468
+		$contentService = $this->getContentService()->findBy($matcher);
469
+
470
+		// Compute the label field name of the table.
471
+		$tableTitleField = Tca::table()->getLabelField();
472
+
473
+		// Get result object for storing data along the processing.
474
+		$result = $this->getJsonResult();
475
+		$result->setNumberOfObjects($contentService->getNumberOfObjects());
476
+
477
+		foreach ($contentService->getObjects() as $object) {
478
+
479
+			// Store the first object, so that the "action" message can be more explicit when deleting only one record.
480
+			if ($contentService->getNumberOfObjects() === 1) {
481
+				$tableTitleValue = $object[$tableTitleField];
482
+				$processedObjectData = array(
483
+					'uid' => $object->getUid(),
484
+					'name' => $tableTitleValue,
485
+				);
486
+				$result->setProcessedObject($processedObjectData);
487
+			}
488
+
489
+			// Work out the object.
490
+			ContentRepositoryFactory::getInstance()->copy($object, $target);
491
+
492
+			// Get the possible error messages and store them.
493
+			$errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
494
+			$result->addErrorMessages($errorMessages);
495
+		}
496
+
497
+		// Flush Clipboard if told so.
498
+		if (GeneralUtility::_GP('flushClipboard')) {
499
+			$this->getClipboardService()->flush();
500
+		}
501
+
502
+		$response = $this->responseFactory->createResponse()
503
+			->withHeader('Content-Type', 'application/json; charset=utf-8');
504
+		$response->getBody()->write(json_encode($result));
505
+		return $response;
506
+	}
507
+
508
+	/**
509
+	 * Retrieve Content objects first according to matching criteria and then "move" them.
510
+	 *
511
+	 * Possible values for $matches, refer to method "updateAction".
512
+	 *
513
+	 * @param string $target
514
+	 * @param array $matches
515
+	 */
516
+	public function moveAction($target, array $matches = [])
517
+	{
518
+
519
+		$matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
520
+
521
+		// Fetch objects via the Content Service.
522
+		$contentService = $this->getContentService()->findBy($matcher);
523
+
524
+		// Compute the label field name of the table.
525
+		$tableTitleField = Tca::table()->getLabelField();
526
+
527
+		// Get result object for storing data along the processing.
528
+		$result = $this->getJsonResult();
529
+		$result->setNumberOfObjects($contentService->getNumberOfObjects());
530
+
531
+		foreach ($contentService->getObjects() as $object) {
532
+
533
+			// Store the first object, so that the "action" message can be more explicit when deleting only one record.
534
+			if ($contentService->getNumberOfObjects() === 1) {
535
+				$tableTitleValue = $object[$tableTitleField];
536
+				$processedObjectData = array(
537
+					'uid' => $object->getUid(),
538
+					'name' => $tableTitleValue,
539
+				);
540
+				$result->setProcessedObject($processedObjectData);
541
+			}
542
+
543
+			// Work out the object.
544
+			ContentRepositoryFactory::getInstance()->move($object, $target);
545
+
546
+			// Get the possible error messages and store them.
547
+			$errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
548
+			$result->addErrorMessages($errorMessages);
549
+		}
550
+
551
+		$response = $this->responseFactory->createResponse()
552
+			->withHeader('Content-Type', 'application/json; charset=utf-8');
553
+		$response->getBody()->write(json_encode($result));
554
+		return $response;
555
+	}
556
+
557
+	/**
558
+	 * Retrieve Content objects from the Clipboard then "move" them according to the target.
559
+	 *
560
+	 * @param string $target
561
+	 */
562
+	public function moveClipboardAction($target)
563
+	{
564
+
565
+		// Retrieve matcher object from clipboard.
566
+		$matcher = $this->getClipboardService()->getMatcher();
567
+
568
+		// Fetch objects via the Content Service.
569
+		$contentService = $this->getContentService()->findBy($matcher);
570
+
571
+		// Compute the label field name of the table.
572
+		$tableTitleField = Tca::table()->getLabelField();
573
+
574
+		// Get result object for storing data along the processing.
575
+		$result = $this->getJsonResult();
576
+		$result->setNumberOfObjects($contentService->getNumberOfObjects());
577
+
578
+		foreach ($contentService->getObjects() as $object) {
579
+
580
+			// Store the first object, so that the "action" message can be more explicit when deleting only one record.
581
+			if ($contentService->getNumberOfObjects() === 1) {
582
+				$tableTitleValue = $object[$tableTitleField];
583
+				$processedObjectData = array(
584
+					'uid' => $object->getUid(),
585
+					'name' => $tableTitleValue,
586
+				);
587
+				$result->setProcessedObject($processedObjectData);
588
+			}
589
+
590
+			// Work out the object.
591
+			ContentRepositoryFactory::getInstance()->move($object, $target);
592
+
593
+			// Get the possible error messages and store them.
594
+			$errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
595
+			$result->addErrorMessages($errorMessages);
596
+		}
597
+
598
+		// Flush Clipboard if told so.
599
+		if (GeneralUtility::_GP('flushClipboard')) {
600
+			$this->getClipboardService()->flush();
601
+		}
602
+
603
+		$response = $this->responseFactory->createResponse()
604
+			->withHeader('Content-Type', 'application/json; charset=utf-8');
605
+		$response->getBody()->write(json_encode($result));
606
+		return $response;
607
+	}
608
+
609
+	/**
610
+	 * Retrieve Content objects first according to matching criteria and then "localize" them.
611
+	 *
612
+	 * Possible values for $matches, refer to method "updateAction".
613
+	 *
614
+	 * @param string $fieldNameAndPath
615
+	 * @param array $matches
616
+	 * @param int $language
617
+	 * @throws \Exception
618
+	 */
619
+	public function localizeAction($fieldNameAndPath, array $matches = [], $language = 0)
620
+	{
621
+
622
+		$matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
623
+
624
+		// Fetch objects via the Content Service.
625
+		$contentService = $this->getContentService()->findBy($matcher);
626
+
627
+		// Get result object for storing data along the processing.
628
+		$result = $this->getJsonResult();
629
+		$result->setNumberOfObjects($contentService->getNumberOfObjects());
630
+
631
+		foreach ($contentService->getObjects() as $object) {
632
+
633
+			$identifier = $this->getContentObjectResolver()->getValue($object, $fieldNameAndPath, 'uid');
634
+			$dataType = $this->getContentObjectResolver()->getDataType($object, $fieldNameAndPath);
635
+
636
+			// Fetch the source object to be localized.
637
+			/** @var Content $content */
638
+			$content = ContentRepositoryFactory::getInstance($dataType)->findByIdentifier($identifier);
639
+
640
+			// Makes sure the object was retrieved. Security!
641
+			if (!$content) {
642
+				$message = sprintf('Something went wrong when retrieving content "%s" with identifier "%s".', $dataType, $identifier);
643
+				throw new \Exception($message, 1412343097);
644
+			}
645
+
646
+			// Handover the localization to the Repository.
647
+			ContentRepositoryFactory::getInstance($dataType)->localize($content, $language);
648
+
649
+			// Get the possible error messages and store them.
650
+			$errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
651
+
652
+			// Redirect to TCEForm so that the BE User can do its job!
653
+			if ($contentService->getNumberOfObjects() === 1) {
654
+
655
+				if (!empty($errorMessages)) {
656
+					$message = sprintf('Something went wrong when localizing content "%s" with identifier "%s". <br/>%s',
657
+						$dataType,
658
+						$identifier,
659
+						implode('<br/>', $errorMessages)
660
+					);
661
+					throw new \Exception($message, 1412343098);
662
+				}
663
+
664
+				$localizedContent = $this->getLanguageService()->getLocalizedContent($content, $language);
665
+				if (empty($localizedContent)) {
666
+					$message = sprintf('Oups! I could not retrieve localized content of type "%s" with identifier "%s"',
667
+						$content->getDataType(),
668
+						$content->getUid()
669
+					);
670
+					throw new \Exception($message, 1412343099);
671
+				}
672
+
673
+				/** @var EditUri $uri */
674
+				$uriRenderer = GeneralUtility::makeInstance(EditUri::class);
675
+				$uri = $uriRenderer->render($localizedContent);
676
+				HttpUtility::redirect($uri);
677
+				break; // no need to further continue
678
+			}
679
+
680
+			$result->addErrorMessages($errorMessages);
681
+		}
682
+
683
+		$response = $this->responseFactory->createResponse()
684
+			->withHeader('Content-Type', 'application/json; charset=utf-8');
685
+		$response->getBody()->write(json_encode($result));
686
+		return $response;
687
+	}
688
+
689
+	/**
690
+	 * Get the Vidi Module Loader.
691
+	 *
692
+	 * @return ContentService
693
+	 */
694
+	protected function getContentService()
695
+	{
696
+		return GeneralUtility::makeInstance(ContentService::class);
697
+	}
698
+
699
+	/**
700
+	 * @return ContentObjectResolver
701
+	 */
702
+	protected function getContentObjectResolver()
703
+	{
704
+		return GeneralUtility::makeInstance(ContentObjectResolver::class);
705
+	}
706
+
707
+	/**
708
+	 * @return FieldPathResolver
709
+	 */
710
+	protected function getFieldPathResolver()
711
+	{
712
+		return GeneralUtility::makeInstance(FieldPathResolver::class);
713
+	}
714
+
715
+	/**
716
+	 * @return JsonResult|object
717
+	 */
718
+	protected function getJsonResult()
719
+	{
720
+		return GeneralUtility::makeInstance(JsonResult::class);
721
+	}
722
+
723
+	/**
724
+	 * Signal that is called for post-processing content data send to the server for update.
725
+	 *
726
+	 * @param Content $contentObject
727
+	 * @param $fieldNameAndPath
728
+	 * @param $contentData
729
+	 * @param $counter
730
+	 * @param $savingBehavior
731
+	 * @param $language
732
+	 * @return ProcessContentDataSignalArguments
733
+	 */
734
+	protected function emitProcessContentDataSignal(Content $contentObject, $fieldNameAndPath, $contentData, $counter, $savingBehavior, $language)
735
+	{
736
+
737
+		/** @var ProcessContentDataSignalArguments $signalArguments */
738
+		$signalArguments = GeneralUtility::makeInstance(ProcessContentDataSignalArguments::class);
739
+		$signalArguments->setContentObject($contentObject)
740
+			->setFieldNameAndPath($fieldNameAndPath)
741
+			->setContentData($contentData)
742
+			->setCounter($counter)
743
+			->setSavingBehavior($savingBehavior)
744
+			->setLanguage($language);
745
+
746
+		$signalResult = $this->getSignalSlotDispatcher()->dispatch('Fab\Vidi\Controller\Backend\ContentController', 'processContentData', array($signalArguments));
747
+		return $signalResult[0];
748
+	}
749
+
750
+	/**
751
+	 * Get the SignalSlot dispatcher.
752
+	 *
753
+	 * @return Dispatcher
754
+	 */
755
+	protected function getSignalSlotDispatcher()
756
+	{
757
+		return GeneralUtility::makeInstance(Dispatcher::class);
758
+	}
759
+
760
+	/**
761
+	 * Get the Clipboard service.
762
+	 *
763
+	 * @return ClipboardService
764
+	 */
765
+	protected function getClipboardService()
766
+	{
767
+		return GeneralUtility::makeInstance(ClipboardService::class);
768
+	}
769
+
770
+	/**
771
+	 * @return LanguageService
772
+	 */
773
+	protected function getLanguageService()
774
+	{
775
+		return GeneralUtility::makeInstance(LanguageService::class);
776
+	}
777
+
778
+	/**
779
+	 * Get the Vidi Module Loader.
780
+	 *
781
+	 * @return ModuleLoader
782
+	 */
783
+	protected function getModuleLoader()
784
+	{
785
+		return GeneralUtility::makeInstance(ModuleLoader::class);
786
+	}
787
+
788
+	public function injectSelectionRepository(SelectionRepository $selectionRepository): void
789
+	{
790
+		$this->selectionRepository = $selectionRepository;
791
+	}
792 792
 
793 793
 }
Please login to merge, or discard this patch.