Completed
Pull Request — master (#107)
by
unknown
03:28
created
Classes/Controller/Backend/ContentController.php 3 patches
Doc Comments   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -740,11 +740,11 @@
 block discarded – undo
740 740
      * Signal that is called for post-processing content data send to the server for update.
741 741
      *
742 742
      * @param Content $contentObject
743
-     * @param $fieldNameAndPath
743
+     * @param string $fieldNameAndPath
744 744
      * @param $contentData
745
-     * @param $counter
746
-     * @param $savingBehavior
747
-     * @param $language
745
+     * @param integer $counter
746
+     * @param string $savingBehavior
747
+     * @param integer $language
748 748
      * @return ProcessContentDataSignalArguments
749 749
      * @signal
750 750
      */
Please login to merge, or discard this patch.
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -16,7 +16,6 @@
 block discarded – undo
16 16
 
17 17
 use Fab\Vidi\Tca\FieldType;
18 18
 use Fab\Vidi\View\Grid\Row;
19
-use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
20 19
 use TYPO3\CMS\Core\Utility\GeneralUtility;
21 20
 use TYPO3\CMS\Core\Utility\HttpUtility;
22 21
 use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
Please login to merge, or discard this patch.
Indentation   +764 added lines, -764 removed lines patch added patch discarded remove patch
@@ -37,769 +37,769 @@
 block discarded – undo
37 37
 class ContentController extends ActionController
38 38
 {
39 39
 
40
-    /**
41
-     * @var \TYPO3\CMS\Core\Page\PageRenderer
42
-     * @inject
43
-     */
44
-    protected $pageRenderer;
45
-
46
-    /**
47
-     * @var \Fab\Vidi\Domain\Repository\SelectionRepository
48
-     * @inject
49
-     */
50
-    protected $selectionRepository;
51
-
52
-    /**
53
-     * Initialize every action.
54
-     */
55
-    public function initializeAction()
56
-    {
57
-        $this->pageRenderer->addInlineLanguageLabelFile('EXT:vidi/Resources/Private/Language/locallang.xlf');
58
-
59
-        // Configure property mapping to retrieve the file object.
60
-        if ($this->arguments->hasArgument('columns')) {
61
-
62
-            /** @var \Fab\Vidi\TypeConverter\CsvToArrayConverter $typeConverter */
63
-            $typeConverter = $this->objectManager->get('Fab\Vidi\TypeConverter\CsvToArrayConverter');
64
-
65
-            $propertyMappingConfiguration = $this->arguments->getArgument('columns')->getPropertyMappingConfiguration();
66
-            $propertyMappingConfiguration->setTypeConverter($typeConverter);
67
-        }
68
-    }
69
-
70
-    /**
71
-     * List action for this controller.
72
-     *
73
-     * @return void
74
-     */
75
-    public function indexAction()
76
-    {
77
-        $dataType = $this->getModuleLoader()->getDataType();
78
-        $selections = $this->selectionRepository->findByDataTypeForCurrentBackendUser($dataType);
79
-        $this->view->assign('selections', $selections);
80
-
81
-        $columns = Tca::grid()->getFields();
82
-        $this->view->assign('columns', $columns);
83
-        $this->view->assign('numberOfColumns', count($columns));
84
-    }
85
-
86
-    /**
87
-     * List Row action for this controller. Output a json list of contents
88
-     *
89
-     * @param array $columns corresponds to columns to be rendered.
90
-     * @param array $matches
91
-     * @validate $columns Fab\Vidi\Domain\Validator\ColumnsValidator
92
-     * @validate $matches Fab\Vidi\Domain\Validator\MatchesValidator
93
-     * @return void
94
-     */
95
-    public function listAction(array $columns = array(), $matches = array())
96
-    {
97
-        // Initialize some objects related to the query.
98
-        $matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
99
-        $order = OrderObjectFactory::getInstance()->getOrder();
100
-        $pager = PagerObjectFactory::getInstance()->getPager();
101
-
102
-        // Fetch objects via the Content Service.
103
-        $contentService = $this->getContentService()->findBy($matcher, $order, $pager->getLimit(), $pager->getOffset());
104
-        $pager->setCount($contentService->getNumberOfObjects());
105
-
106
-        // Assign values.
107
-        $this->view->assign('columns', $columns);
108
-        $this->view->assign('objects', $contentService->getObjects());
109
-        $this->view->assign('numberOfObjects', $contentService->getNumberOfObjects());
110
-        $this->view->assign('pager', $pager);
111
-        $this->view->assign('response', $this->response);
112
-    }
113
-
114
-    /**
115
-     * Retrieve Content objects first according to matching criteria and then "update" them.
116
-     * Important to notice the field name can contains a path, e.g. metadata.title and therefore must be analysed.
117
-     *
118
-     * Possible values for $matches:
119
-     * -----------------------------
120
-     *
121
-     * $matches = array(uid => 1), will be taken as $query->equals
122
-     * $matches = array(uid => 1,2,3), will be taken as $query->in
123
-     * $matches = array(field_name1 => bar, field_name2 => bax), will be separated by AND.
124
-     *
125
-     * Possible values for $content:
126
-     * -----------------------------
127
-     *
128
-     * $content = array(field_name => bar)
129
-     * $content = array(field_name => array(value1, value2)) <-- will be CSV converted by "value1,value2"
130
-     *
131
-     * @param string $fieldNameAndPath
132
-     * @param array $content
133
-     * @param array $matches
134
-     * @param string $savingBehavior
135
-     * @param int $language
136
-     * @param array $columns
137
-     * @return string
138
-     * @throws \Fab\Vidi\Exception\InvalidKeyInArrayException
139
-     */
140
-    public function updateAction($fieldNameAndPath, array $content, array $matches = array(), $savingBehavior = SavingBehavior::REPLACE, $language = 0, $columns = array())
141
-    {
142
-
143
-        // Instantiate the Matcher object according different rules.
144
-        $matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
145
-        $order = OrderObjectFactory::getInstance()->getOrder();
146
-
147
-        // Fetch objects via the Content Service.
148
-        $contentService = $this->getContentService()->findBy($matcher, $order);
149
-
150
-        // Get the real field that is going to be updated.
151
-        $updatedFieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath);
152
-
153
-        // Get result object for storing data along the processing.
154
-        $result = $this->getJsonResult();
155
-        $result->setNumberOfObjects($contentService->getNumberOfObjects());
156
-
157
-        foreach ($contentService->getObjects() as $index => $object) {
158
-
159
-            $identifier = $this->getContentObjectResolver()->getValue($object, $fieldNameAndPath, 'uid', $language);
160
-
161
-            // It could be the identifier is not found because the translation
162
-            // of the record does not yet exist when mass-editing
163
-            if ((int)$identifier <= 0) {
164
-                continue;
165
-            }
166
-
167
-            $dataType = $this->getContentObjectResolver()->getDataType($object, $fieldNameAndPath);
168
-
169
-            $signalResult = $this->emitProcessContentDataSignal($object, $fieldNameAndPath, $content, $index + 1, $savingBehavior, $language);
170
-            $contentData = $signalResult->getContentData();
171
-
172
-            // Add identifier to content data, required by TCEMain.
173
-            $contentData['uid'] = $identifier;
174
-
175
-            /** @var Content $dataObject */
176
-            $dataObject = GeneralUtility::makeInstance('Fab\Vidi\Domain\Model\Content', $dataType, $contentData);
177
-
178
-            // Properly update object.
179
-            ContentRepositoryFactory::getInstance($dataType)->update($dataObject);
180
-
181
-            // Get the possible error messages and store them.
182
-            $errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
183
-            $result->addErrorMessages($errorMessages);
184
-
185
-            // We only want to see the detail result if there is one object updated.
186
-            // Required for inline editing + it will display some useful info on the GUI in the flash messages.
187
-            if ($contentService->getNumberOfObjects() === 1) {
188
-
189
-                // Fetch the updated object from repository.
190
-                $updatedObject = ContentRepositoryFactory::getInstance()->findByUid($object->getUid());
191
-
192
-                // Re-fetch the updated result.
193
-                $updatedResult = $this->getContentObjectResolver()->getValue($updatedObject, $fieldNameAndPath, $updatedFieldName, $language);
194
-                if (is_array($updatedResult)) {
195
-                    $_updatedResult = array(); // reset result set.
196
-
197
-                    /** @var Content $contentObject */
198
-                    foreach ($updatedResult as $contentObject) {
199
-                        $labelField = Tca::table($contentObject)->getLabelField();
200
-                        $values = array(
201
-                            'uid' => $contentObject->getUid(),
202
-                            'name' => $contentObject[$labelField],
203
-                        );
204
-                        $_updatedResult[] = $values;
205
-                    }
206
-
207
-                    $updatedResult = $_updatedResult;
208
-                }
209
-
210
-                $labelField = Tca::table($object)->getLabelField();
211
-
212
-                $processedObjectData = array(
213
-                    'uid' => $object->getUid(),
214
-                    'name' => $object[$labelField],
215
-                    'updatedField' => $fieldNameAndPath,
216
-                    'updatedValue' => $updatedResult,
217
-                );
218
-                $result->setProcessedObject($processedObjectData);
219
-
220
-                if (!empty($columns)) {
221
-                    /** @var Row $row */
222
-                    $row = GeneralUtility::makeInstance('Fab\Vidi\View\Grid\Row', $columns);
223
-                    $result->setRow($row->render($updatedObject));
224
-                }
225
-            }
226
-        }
227
-
228
-        // Set the result and render the JSON view.
229
-        $this->getJsonView()->setResult($result);
230
-        return $this->getJsonView()->render();
231
-    }
232
-
233
-    /**
234
-     * Set the sorting of a record giving the previous object.
235
-     *
236
-     * @param array $matches
237
-     * @param int $previousIdentifier
238
-     * @return string
239
-     */
240
-    public function sortAction(array $matches = array(), $previousIdentifier = NULL)
241
-    {
242
-
243
-        $matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
244
-
245
-        // Fetch objects via the Content Service.
246
-        $contentService = $this->getContentService()->findBy($matcher);
247
-
248
-        // Compute the label field name of the table.
249
-        $tableTitleField = Tca::table()->getLabelField();
250
-
251
-        // Get result object for storing data along the processing.
252
-        $result = $this->getJsonResult();
253
-        $result->setNumberOfObjects($contentService->getNumberOfObjects());
254
-
255
-        foreach ($contentService->getObjects() as $object) {
256
-
257
-            // Store the first object, so that the "action" message can be more explicit when deleting only one record.
258
-            if ($contentService->getNumberOfObjects() === 1) {
259
-                $tableTitleValue = $object[$tableTitleField];
260
-                $processedObjectData = array(
261
-                    'uid' => $object->getUid(),
262
-                    'name' => $tableTitleValue,
263
-                );
264
-                $result->setProcessedObject($processedObjectData);
265
-            }
266
-
267
-            // The $target corresponds to the pid to move the records to.
268
-            // It can also be a negative value in case of sorting. The negative value would be the uid of its predecessor.
269
-            $target = is_null($previousIdentifier) ? $object->getPid() : (-(int)$previousIdentifier);
270
-
271
-            // Work out the object.
272
-            ContentRepositoryFactory::getInstance()->move($object, $target);
273
-
274
-            // Get the possible error messages and store them.
275
-            $errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
276
-            $result->addErrorMessages($errorMessages);
277
-        }
278
-
279
-        // Set the result and render the JSON view.
280
-        $this->getJsonView()->setResult($result);
281
-        return $this->getJsonView()->render();
282
-    }
283
-
284
-    /**
285
-     * Returns an editing form for a given field name of a Content object.
286
-     * Argument $fieldNameAndPath corresponds to the field name to be edited.
287
-     * Important to notice it can contains a path, e.g. metadata.title and therefore must be analysed.
288
-     *
289
-     * Possible values for $matches, refer to method "updateAction".
290
-     *
291
-     * @param string $fieldNameAndPath
292
-     * @param array $matches
293
-     * @param bool $hasRecursiveSelection
294
-     * @throws \Exception
295
-     */
296
-    public function editAction($fieldNameAndPath, array $matches = array(), $hasRecursiveSelection = FALSE)
297
-    {
298
-
299
-        // Instantiate the Matcher object according different rules.
300
-        $matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
301
-
302
-        // Fetch objects via the Content Service.
303
-        $contentService = $this->getContentService()->findBy($matcher);
304
-
305
-        $dataType = $this->getFieldPathResolver()->getDataType($fieldNameAndPath);
306
-        $fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath);
307
-
308
-        $fieldType = Tca::table($dataType)->field($fieldName)->getType();
309
-        $this->view->assign('fieldType', ucfirst($fieldType));
310
-        $this->view->assign('dataType', $dataType);
311
-        $this->view->assign('fieldName', $fieldName);
312
-        $this->view->assign('matches', $matches);
313
-        $this->view->assign('fieldNameAndPath', $fieldNameAndPath);
314
-        $this->view->assign('numberOfObjects', $contentService->getNumberOfObjects());
315
-        $this->view->assign('hasRecursiveSelection', $hasRecursiveSelection);
316
-        $this->view->assign('editWholeSelection', empty($matches['uid'])); // necessary??
317
-
318
-        // Fetch content and its relations.
319
-        if ($fieldType === FieldType::MULTISELECT) {
320
-
321
-            $object = ContentRepositoryFactory::getInstance()->findOneBy($matcher);
322
-            $identifier = $this->getContentObjectResolver()->getValue($object, $fieldNameAndPath, 'uid');
323
-            $dataType = $this->getContentObjectResolver()->getDataType($object, $fieldNameAndPath);
324
-
325
-            $content = ContentRepositoryFactory::getInstance($dataType)->findByUid($identifier);
326
-
327
-            // Makes sure the object was retrieved. Security!
328
-            if (!$content) {
329
-                $message = sprintf('I could not retrieved content object of type "%s" with identifier %s.', $dataType, $identifier);
330
-                throw new \Exception($message, 1402350182);
331
-            }
332
-
333
-            $relatedDataType = Tca::table($dataType)->field($fieldName)->getForeignTable();
334
-
335
-            // Initialize the matcher object.
336
-            /** @var \Fab\Vidi\Persistence\Matcher $matcher */
337
-            $matcher = GeneralUtility::makeInstance('Fab\Vidi\Persistence\Matcher', array(), $relatedDataType);
338
-
339
-            // Default ordering for related data type.
340
-            $defaultOrderings = Tca::table($relatedDataType)->getDefaultOrderings();
341
-            /** @var \Fab\Vidi\Persistence\Order $order */
342
-            $defaultOrder = GeneralUtility::makeInstance('Fab\Vidi\Persistence\Order', $defaultOrderings);
343
-
344
-            // Fetch related contents
345
-            $relatedContents = ContentRepositoryFactory::getInstance($relatedDataType)->findBy($matcher, $defaultOrder);
346
-
347
-            if (Tca::table($dataType)->field($fieldName)->isRenderModeTree()) {
348
-
349
-                $fieldConfiguration = Tca::table($dataType)->field($fieldName)->getConfiguration();
350
-                $parentField = $fieldConfiguration['treeConfig']['parentField'];
351
-
352
-                $flatTree = array();
353
-                foreach ($relatedContents as $node) {
354
-                    $flatTree[$node->getUid()] = array(
355
-                        'item' => $node,
356
-                        'parent' => $node[$parentField] ? $node[$parentField]['uid'] : NULL,
357
-                    );
358
-                }
359
-
360
-                $tree = array();
361
-
362
-                // If leaves are selected without its parents selected, those are shown as parent
363
-                foreach ($flatTree as $id => &$flatNode) {
364
-                    if (!isset($flatTree[$flatNode['parent']])) {
365
-                        $flatNode['parent'] = NULL;
366
-                    }
367
-                }
368
-
369
-                foreach ($flatTree as $id => &$node) {
370
-                    if ($node['parent'] === NULL) {
371
-                        $tree[$id] = &$node;
372
-                    } else {
373
-                        $flatTree[$node['parent']]['children'][$id] = &$node;
374
-                    }
375
-                }
376
-
377
-                $relatedContents = $tree;
378
-            }
379
-
380
-            $this->view->assign('content', $content);
381
-            $this->view->assign('relatedContents', $relatedContents);
382
-            $this->view->assign('relatedDataType', $relatedDataType);
383
-            $this->view->assign('relatedContentTitle', Tca::table($relatedDataType)->getTitle());
384
-            $this->view->assign(
385
-                'renderMode',
386
-                Tca::table($dataType)->field($fieldName)->isRenderModeTree() ? FieldType::TREE : NULL
387
-            );
388
-        }
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
-     * @return string
398
-     */
399
-    public function deleteAction(array $matches = array())
400
-    {
401
-
402
-        $matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
403
-
404
-        // Fetch objects via the Content Service.
405
-        $contentService = $this->getContentService()->findBy($matcher);
406
-
407
-        // Compute the label field name of the table.
408
-        $tableTitleField = Tca::table()->getLabelField();
409
-
410
-        // Get result object for storing data along the processing.
411
-        $result = $this->getJsonResult();
412
-        $result->setNumberOfObjects($contentService->getNumberOfObjects());
413
-
414
-        foreach ($contentService->getObjects() as $object) {
415
-
416
-            // Store the first object, so that the delete message can be more explicit when deleting only one record.
417
-            if ($contentService->getNumberOfObjects() === 1) {
418
-                $tableTitleValue = $object[$tableTitleField];
419
-                $processedObjectData = array(
420
-                    'uid' => $object->getUid(),
421
-                    'name' => $tableTitleValue,
422
-                );
423
-                $result->setProcessedObject($processedObjectData);
424
-            }
425
-
426
-            // Properly delete object.
427
-            ContentRepositoryFactory::getInstance()->remove($object);
428
-
429
-            // Get the possible error messages and store them.
430
-            $errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
431
-            $result->addErrorMessages($errorMessages);
432
-        }
433
-
434
-        // Set the result and render the JSON view.
435
-        $this->getJsonView()->setResult($result);
436
-        return $this->getJsonView()->render();
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 = array())
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
-     * @return string
461
-     */
462
-    public function copyClipboardAction($target)
463
-    {
464
-
465
-        // Retrieve matcher object from clipboard.
466
-        $matcher = $this->getClipboardService()->getMatcher();
467
-
468
-        // Fetch objects via the Content Service.
469
-        $contentService = $this->getContentService()->findBy($matcher);
470
-
471
-        // Compute the label field name of the table.
472
-        $tableTitleField = Tca::table()->getLabelField();
473
-
474
-        // Get result object for storing data along the processing.
475
-        $result = $this->getJsonResult();
476
-        $result->setNumberOfObjects($contentService->getNumberOfObjects());
477
-
478
-        foreach ($contentService->getObjects() as $object) {
479
-
480
-            // Store the first object, so that the "action" message can be more explicit when deleting only one record.
481
-            if ($contentService->getNumberOfObjects() === 1) {
482
-                $tableTitleValue = $object[$tableTitleField];
483
-                $processedObjectData = array(
484
-                    'uid' => $object->getUid(),
485
-                    'name' => $tableTitleValue,
486
-                );
487
-                $result->setProcessedObject($processedObjectData);
488
-            }
489
-
490
-            // Work out the object.
491
-            ContentRepositoryFactory::getInstance()->copy($object, $target);
492
-
493
-            // Get the possible error messages and store them.
494
-            $errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
495
-            $result->addErrorMessages($errorMessages);
496
-        }
497
-
498
-        // Flush Clipboard if told so.
499
-        if (GeneralUtility::_GP('flushClipboard')) {
500
-            $this->getClipboardService()->flush();
501
-        }
502
-
503
-        // Set the result and render the JSON view.
504
-        $this->getJsonView()->setResult($result);
505
-        return $this->getJsonView()->render();
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
-     * @return string
516
-     */
517
-    public function moveAction($target, array $matches = array())
518
-    {
519
-
520
-        $matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
521
-
522
-        // Fetch objects via the Content Service.
523
-        $contentService = $this->getContentService()->findBy($matcher);
524
-
525
-        // Compute the label field name of the table.
526
-        $tableTitleField = Tca::table()->getLabelField();
527
-
528
-        // Get result object for storing data along the processing.
529
-        $result = $this->getJsonResult();
530
-        $result->setNumberOfObjects($contentService->getNumberOfObjects());
531
-
532
-        foreach ($contentService->getObjects() as $object) {
533
-
534
-            // Store the first object, so that the "action" message can be more explicit when deleting only one record.
535
-            if ($contentService->getNumberOfObjects() === 1) {
536
-                $tableTitleValue = $object[$tableTitleField];
537
-                $processedObjectData = array(
538
-                    'uid' => $object->getUid(),
539
-                    'name' => $tableTitleValue,
540
-                );
541
-                $result->setProcessedObject($processedObjectData);
542
-            }
543
-
544
-            // Work out the object.
545
-            ContentRepositoryFactory::getInstance()->move($object, $target);
546
-
547
-            // Get the possible error messages and store them.
548
-            $errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
549
-            $result->addErrorMessages($errorMessages);
550
-        }
551
-
552
-        // Set the result and render the JSON view.
553
-        $this->getJsonView()->setResult($result);
554
-        return $this->getJsonView()->render();
555
-    }
556
-
557
-    /**
558
-     * Retrieve Content objects from the Clipboard then "move" them according to the target.
559
-     *
560
-     * @param string $target
561
-     * @return string
562
-     */
563
-    public function moveClipboardAction($target)
564
-    {
565
-
566
-        // Retrieve matcher object from clipboard.
567
-        $matcher = $this->getClipboardService()->getMatcher();
568
-
569
-        // Fetch objects via the Content Service.
570
-        $contentService = $this->getContentService()->findBy($matcher);
571
-
572
-        // Compute the label field name of the table.
573
-        $tableTitleField = Tca::table()->getLabelField();
574
-
575
-        // Get result object for storing data along the processing.
576
-        $result = $this->getJsonResult();
577
-        $result->setNumberOfObjects($contentService->getNumberOfObjects());
578
-
579
-        foreach ($contentService->getObjects() as $object) {
580
-
581
-            // Store the first object, so that the "action" message can be more explicit when deleting only one record.
582
-            if ($contentService->getNumberOfObjects() === 1) {
583
-                $tableTitleValue = $object[$tableTitleField];
584
-                $processedObjectData = array(
585
-                    'uid' => $object->getUid(),
586
-                    'name' => $tableTitleValue,
587
-                );
588
-                $result->setProcessedObject($processedObjectData);
589
-            }
590
-
591
-            // Work out the object.
592
-            ContentRepositoryFactory::getInstance()->move($object, $target);
593
-
594
-            // Get the possible error messages and store them.
595
-            $errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
596
-            $result->addErrorMessages($errorMessages);
597
-        }
598
-
599
-        // Flush Clipboard if told so.
600
-        if (GeneralUtility::_GP('flushClipboard')) {
601
-            $this->getClipboardService()->flush();
602
-        }
603
-
604
-        // Set the result and render the JSON view.
605
-        $this->getJsonView()->setResult($result);
606
-        return $this->getJsonView()->render();
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
-     * @return string
618
-     * @throws \Exception
619
-     */
620
-    public function localizeAction($fieldNameAndPath, array $matches = array(), $language = 0)
621
-    {
622
-
623
-        $matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
624
-
625
-        // Fetch objects via the Content Service.
626
-        $contentService = $this->getContentService()->findBy($matcher);
627
-
628
-        // Get result object for storing data along the processing.
629
-        $result = $this->getJsonResult();
630
-        $result->setNumberOfObjects($contentService->getNumberOfObjects());
631
-
632
-        foreach ($contentService->getObjects() as $object) {
633
-
634
-            $identifier = $this->getContentObjectResolver()->getValue($object, $fieldNameAndPath, 'uid');
635
-            $dataType = $this->getContentObjectResolver()->getDataType($object, $fieldNameAndPath);
636
-
637
-            // Fetch the source object to be localized.
638
-            /** @var Content $content */
639
-            $content = ContentRepositoryFactory::getInstance($dataType)->findByIdentifier($identifier);
640
-
641
-            // Makes sure the object was retrieved. Security!
642
-            if (!$content) {
643
-                $message = sprintf('Something went wrong when retrieving content "%s" with identifier "%s".', $dataType, $identifier);
644
-                throw new \Exception($message, 1412343097);
645
-            }
646
-
647
-            // Handover the localization to the Repository.
648
-            ContentRepositoryFactory::getInstance($dataType)->localize($content, $language);
649
-
650
-            // Get the possible error messages and store them.
651
-            $errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
652
-
653
-            // Redirect to TCEForm so that the BE User can do its job!
654
-            if ($contentService->getNumberOfObjects() === 1) {
655
-
656
-                if (!empty($errorMessages)) {
657
-                    $message = sprintf('Something went wrong when localizing content "%s" with identifier "%s". <br/>%s',
658
-                        $dataType,
659
-                        $identifier,
660
-                        implode('<br/>', $errorMessages)
661
-                    );
662
-                    throw new \Exception($message, 1412343098);
663
-                }
664
-
665
-                $localizedContent = $this->getLanguageService()->getLocalizedContent($content, $language);
666
-                if (empty($localizedContent)) {
667
-                    $message = sprintf('Oups! I could not retrieve localized content of type "%s" with identifier "%s"',
668
-                        $content->getDataType(),
669
-                        $content->getUid()
670
-                    );
671
-                    throw new \Exception($message, 1412343099);
672
-                }
673
-
674
-                /** @var \Fab\Vidi\View\Uri\EditUri $uri */
675
-                $uriRenderer = GeneralUtility::makeInstance('Fab\Vidi\View\Uri\EditUri');
676
-                $uri = $uriRenderer->render($localizedContent);
677
-                HttpUtility::redirect($uri);
678
-                break; // no need to further continue
679
-            }
680
-
681
-            $result->addErrorMessages($errorMessages);
682
-        }
683
-
684
-        // Set the result and render the JSON view.
685
-        $this->getJsonView()->setResult($result);
686
-        return $this->getJsonView()->render();
687
-    }
688
-
689
-    /**
690
-     * Get the Vidi Module Loader.
691
-     *
692
-     * @return \Fab\Vidi\Service\ContentService
693
-     */
694
-    protected function getContentService()
695
-    {
696
-        return GeneralUtility::makeInstance('Fab\Vidi\Service\ContentService');
697
-    }
698
-
699
-    /**
700
-     * @return \Fab\Vidi\Resolver\ContentObjectResolver
701
-     */
702
-    protected function getContentObjectResolver()
703
-    {
704
-        return GeneralUtility::makeInstance('Fab\Vidi\Resolver\ContentObjectResolver');
705
-    }
706
-
707
-    /**
708
-     * @return \Fab\Vidi\Resolver\FieldPathResolver
709
-     */
710
-    protected function getFieldPathResolver()
711
-    {
712
-        return GeneralUtility::makeInstance('Fab\Vidi\Resolver\FieldPathResolver');
713
-    }
714
-
715
-    /**
716
-     * Return a special view for handling JSON
717
-     * Goal is to have this view injected but require more configuration.
718
-     *
719
-     * @return JsonView
720
-     */
721
-    protected function getJsonView()
722
-    {
723
-        if (!$this->view instanceof JsonView) {
724
-            /** @var JsonView $view */
725
-            $this->view = $this->objectManager->get('Fab\Vidi\Mvc\JsonView');
726
-            $this->view->setResponse($this->response);
727
-        }
728
-        return $this->view;
729
-    }
730
-
731
-    /**
732
-     * @return JsonResult
733
-     */
734
-    protected function getJsonResult()
735
-    {
736
-        return GeneralUtility::makeInstance('Fab\Vidi\Mvc\JsonResult');
737
-    }
738
-
739
-    /**
740
-     * Signal that is called for post-processing content data send to the server for update.
741
-     *
742
-     * @param Content $contentObject
743
-     * @param $fieldNameAndPath
744
-     * @param $contentData
745
-     * @param $counter
746
-     * @param $savingBehavior
747
-     * @param $language
748
-     * @return ProcessContentDataSignalArguments
749
-     * @signal
750
-     */
751
-    protected function emitProcessContentDataSignal(Content $contentObject, $fieldNameAndPath, $contentData, $counter, $savingBehavior, $language)
752
-    {
753
-
754
-        /** @var \Fab\Vidi\Signal\ProcessContentDataSignalArguments $signalArguments */
755
-        $signalArguments = GeneralUtility::makeInstance('Fab\Vidi\Signal\ProcessContentDataSignalArguments');
756
-        $signalArguments->setContentObject($contentObject)
757
-            ->setFieldNameAndPath($fieldNameAndPath)
758
-            ->setContentData($contentData)
759
-            ->setCounter($counter)
760
-            ->setSavingBehavior($savingBehavior)
761
-            ->setLanguage($language);
762
-
763
-        $signalResult = $this->getSignalSlotDispatcher()->dispatch('Fab\Vidi\Controller\Backend\ContentController', 'processContentData', array($signalArguments));
764
-        return $signalResult[0];
765
-    }
766
-
767
-    /**
768
-     * Get the SignalSlot dispatcher.
769
-     *
770
-     * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
771
-     */
772
-    protected function getSignalSlotDispatcher()
773
-    {
774
-        return $this->objectManager->get('TYPO3\\CMS\\Extbase\\SignalSlot\\Dispatcher');
775
-    }
776
-
777
-    /**
778
-     * Get the Clipboard service.
779
-     *
780
-     * @return \Fab\Vidi\Service\ClipboardService
781
-     */
782
-    protected function getClipboardService()
783
-    {
784
-        return GeneralUtility::makeInstance('Fab\Vidi\Service\ClipboardService');
785
-    }
786
-
787
-    /**
788
-     * @return \Fab\Vidi\Language\LanguageService
789
-     */
790
-    protected function getLanguageService()
791
-    {
792
-        return GeneralUtility::makeInstance('Fab\Vidi\Language\LanguageService');
793
-    }
794
-
795
-    /**
796
-     * Get the Vidi Module Loader.
797
-     *
798
-     * @return \Fab\Vidi\Module\ModuleLoader
799
-     */
800
-    protected function getModuleLoader()
801
-    {
802
-        return GeneralUtility::makeInstance('Fab\Vidi\Module\ModuleLoader');
803
-    }
40
+	/**
41
+	 * @var \TYPO3\CMS\Core\Page\PageRenderer
42
+	 * @inject
43
+	 */
44
+	protected $pageRenderer;
45
+
46
+	/**
47
+	 * @var \Fab\Vidi\Domain\Repository\SelectionRepository
48
+	 * @inject
49
+	 */
50
+	protected $selectionRepository;
51
+
52
+	/**
53
+	 * Initialize every action.
54
+	 */
55
+	public function initializeAction()
56
+	{
57
+		$this->pageRenderer->addInlineLanguageLabelFile('EXT:vidi/Resources/Private/Language/locallang.xlf');
58
+
59
+		// Configure property mapping to retrieve the file object.
60
+		if ($this->arguments->hasArgument('columns')) {
61
+
62
+			/** @var \Fab\Vidi\TypeConverter\CsvToArrayConverter $typeConverter */
63
+			$typeConverter = $this->objectManager->get('Fab\Vidi\TypeConverter\CsvToArrayConverter');
64
+
65
+			$propertyMappingConfiguration = $this->arguments->getArgument('columns')->getPropertyMappingConfiguration();
66
+			$propertyMappingConfiguration->setTypeConverter($typeConverter);
67
+		}
68
+	}
69
+
70
+	/**
71
+	 * List action for this controller.
72
+	 *
73
+	 * @return void
74
+	 */
75
+	public function indexAction()
76
+	{
77
+		$dataType = $this->getModuleLoader()->getDataType();
78
+		$selections = $this->selectionRepository->findByDataTypeForCurrentBackendUser($dataType);
79
+		$this->view->assign('selections', $selections);
80
+
81
+		$columns = Tca::grid()->getFields();
82
+		$this->view->assign('columns', $columns);
83
+		$this->view->assign('numberOfColumns', count($columns));
84
+	}
85
+
86
+	/**
87
+	 * List Row action for this controller. Output a json list of contents
88
+	 *
89
+	 * @param array $columns corresponds to columns to be rendered.
90
+	 * @param array $matches
91
+	 * @validate $columns Fab\Vidi\Domain\Validator\ColumnsValidator
92
+	 * @validate $matches Fab\Vidi\Domain\Validator\MatchesValidator
93
+	 * @return void
94
+	 */
95
+	public function listAction(array $columns = array(), $matches = array())
96
+	{
97
+		// Initialize some objects related to the query.
98
+		$matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
99
+		$order = OrderObjectFactory::getInstance()->getOrder();
100
+		$pager = PagerObjectFactory::getInstance()->getPager();
101
+
102
+		// Fetch objects via the Content Service.
103
+		$contentService = $this->getContentService()->findBy($matcher, $order, $pager->getLimit(), $pager->getOffset());
104
+		$pager->setCount($contentService->getNumberOfObjects());
105
+
106
+		// Assign values.
107
+		$this->view->assign('columns', $columns);
108
+		$this->view->assign('objects', $contentService->getObjects());
109
+		$this->view->assign('numberOfObjects', $contentService->getNumberOfObjects());
110
+		$this->view->assign('pager', $pager);
111
+		$this->view->assign('response', $this->response);
112
+	}
113
+
114
+	/**
115
+	 * Retrieve Content objects first according to matching criteria and then "update" them.
116
+	 * Important to notice the field name can contains a path, e.g. metadata.title and therefore must be analysed.
117
+	 *
118
+	 * Possible values for $matches:
119
+	 * -----------------------------
120
+	 *
121
+	 * $matches = array(uid => 1), will be taken as $query->equals
122
+	 * $matches = array(uid => 1,2,3), will be taken as $query->in
123
+	 * $matches = array(field_name1 => bar, field_name2 => bax), will be separated by AND.
124
+	 *
125
+	 * Possible values for $content:
126
+	 * -----------------------------
127
+	 *
128
+	 * $content = array(field_name => bar)
129
+	 * $content = array(field_name => array(value1, value2)) <-- will be CSV converted by "value1,value2"
130
+	 *
131
+	 * @param string $fieldNameAndPath
132
+	 * @param array $content
133
+	 * @param array $matches
134
+	 * @param string $savingBehavior
135
+	 * @param int $language
136
+	 * @param array $columns
137
+	 * @return string
138
+	 * @throws \Fab\Vidi\Exception\InvalidKeyInArrayException
139
+	 */
140
+	public function updateAction($fieldNameAndPath, array $content, array $matches = array(), $savingBehavior = SavingBehavior::REPLACE, $language = 0, $columns = array())
141
+	{
142
+
143
+		// Instantiate the Matcher object according different rules.
144
+		$matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
145
+		$order = OrderObjectFactory::getInstance()->getOrder();
146
+
147
+		// Fetch objects via the Content Service.
148
+		$contentService = $this->getContentService()->findBy($matcher, $order);
149
+
150
+		// Get the real field that is going to be updated.
151
+		$updatedFieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath);
152
+
153
+		// Get result object for storing data along the processing.
154
+		$result = $this->getJsonResult();
155
+		$result->setNumberOfObjects($contentService->getNumberOfObjects());
156
+
157
+		foreach ($contentService->getObjects() as $index => $object) {
158
+
159
+			$identifier = $this->getContentObjectResolver()->getValue($object, $fieldNameAndPath, 'uid', $language);
160
+
161
+			// It could be the identifier is not found because the translation
162
+			// of the record does not yet exist when mass-editing
163
+			if ((int)$identifier <= 0) {
164
+				continue;
165
+			}
166
+
167
+			$dataType = $this->getContentObjectResolver()->getDataType($object, $fieldNameAndPath);
168
+
169
+			$signalResult = $this->emitProcessContentDataSignal($object, $fieldNameAndPath, $content, $index + 1, $savingBehavior, $language);
170
+			$contentData = $signalResult->getContentData();
171
+
172
+			// Add identifier to content data, required by TCEMain.
173
+			$contentData['uid'] = $identifier;
174
+
175
+			/** @var Content $dataObject */
176
+			$dataObject = GeneralUtility::makeInstance('Fab\Vidi\Domain\Model\Content', $dataType, $contentData);
177
+
178
+			// Properly update object.
179
+			ContentRepositoryFactory::getInstance($dataType)->update($dataObject);
180
+
181
+			// Get the possible error messages and store them.
182
+			$errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
183
+			$result->addErrorMessages($errorMessages);
184
+
185
+			// We only want to see the detail result if there is one object updated.
186
+			// Required for inline editing + it will display some useful info on the GUI in the flash messages.
187
+			if ($contentService->getNumberOfObjects() === 1) {
188
+
189
+				// Fetch the updated object from repository.
190
+				$updatedObject = ContentRepositoryFactory::getInstance()->findByUid($object->getUid());
191
+
192
+				// Re-fetch the updated result.
193
+				$updatedResult = $this->getContentObjectResolver()->getValue($updatedObject, $fieldNameAndPath, $updatedFieldName, $language);
194
+				if (is_array($updatedResult)) {
195
+					$_updatedResult = array(); // reset result set.
196
+
197
+					/** @var Content $contentObject */
198
+					foreach ($updatedResult as $contentObject) {
199
+						$labelField = Tca::table($contentObject)->getLabelField();
200
+						$values = array(
201
+							'uid' => $contentObject->getUid(),
202
+							'name' => $contentObject[$labelField],
203
+						);
204
+						$_updatedResult[] = $values;
205
+					}
206
+
207
+					$updatedResult = $_updatedResult;
208
+				}
209
+
210
+				$labelField = Tca::table($object)->getLabelField();
211
+
212
+				$processedObjectData = array(
213
+					'uid' => $object->getUid(),
214
+					'name' => $object[$labelField],
215
+					'updatedField' => $fieldNameAndPath,
216
+					'updatedValue' => $updatedResult,
217
+				);
218
+				$result->setProcessedObject($processedObjectData);
219
+
220
+				if (!empty($columns)) {
221
+					/** @var Row $row */
222
+					$row = GeneralUtility::makeInstance('Fab\Vidi\View\Grid\Row', $columns);
223
+					$result->setRow($row->render($updatedObject));
224
+				}
225
+			}
226
+		}
227
+
228
+		// Set the result and render the JSON view.
229
+		$this->getJsonView()->setResult($result);
230
+		return $this->getJsonView()->render();
231
+	}
232
+
233
+	/**
234
+	 * Set the sorting of a record giving the previous object.
235
+	 *
236
+	 * @param array $matches
237
+	 * @param int $previousIdentifier
238
+	 * @return string
239
+	 */
240
+	public function sortAction(array $matches = array(), $previousIdentifier = NULL)
241
+	{
242
+
243
+		$matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
244
+
245
+		// Fetch objects via the Content Service.
246
+		$contentService = $this->getContentService()->findBy($matcher);
247
+
248
+		// Compute the label field name of the table.
249
+		$tableTitleField = Tca::table()->getLabelField();
250
+
251
+		// Get result object for storing data along the processing.
252
+		$result = $this->getJsonResult();
253
+		$result->setNumberOfObjects($contentService->getNumberOfObjects());
254
+
255
+		foreach ($contentService->getObjects() as $object) {
256
+
257
+			// Store the first object, so that the "action" message can be more explicit when deleting only one record.
258
+			if ($contentService->getNumberOfObjects() === 1) {
259
+				$tableTitleValue = $object[$tableTitleField];
260
+				$processedObjectData = array(
261
+					'uid' => $object->getUid(),
262
+					'name' => $tableTitleValue,
263
+				);
264
+				$result->setProcessedObject($processedObjectData);
265
+			}
266
+
267
+			// The $target corresponds to the pid to move the records to.
268
+			// It can also be a negative value in case of sorting. The negative value would be the uid of its predecessor.
269
+			$target = is_null($previousIdentifier) ? $object->getPid() : (-(int)$previousIdentifier);
270
+
271
+			// Work out the object.
272
+			ContentRepositoryFactory::getInstance()->move($object, $target);
273
+
274
+			// Get the possible error messages and store them.
275
+			$errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
276
+			$result->addErrorMessages($errorMessages);
277
+		}
278
+
279
+		// Set the result and render the JSON view.
280
+		$this->getJsonView()->setResult($result);
281
+		return $this->getJsonView()->render();
282
+	}
283
+
284
+	/**
285
+	 * Returns an editing form for a given field name of a Content object.
286
+	 * Argument $fieldNameAndPath corresponds to the field name to be edited.
287
+	 * Important to notice it can contains a path, e.g. metadata.title and therefore must be analysed.
288
+	 *
289
+	 * Possible values for $matches, refer to method "updateAction".
290
+	 *
291
+	 * @param string $fieldNameAndPath
292
+	 * @param array $matches
293
+	 * @param bool $hasRecursiveSelection
294
+	 * @throws \Exception
295
+	 */
296
+	public function editAction($fieldNameAndPath, array $matches = array(), $hasRecursiveSelection = FALSE)
297
+	{
298
+
299
+		// Instantiate the Matcher object according different rules.
300
+		$matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
301
+
302
+		// Fetch objects via the Content Service.
303
+		$contentService = $this->getContentService()->findBy($matcher);
304
+
305
+		$dataType = $this->getFieldPathResolver()->getDataType($fieldNameAndPath);
306
+		$fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath);
307
+
308
+		$fieldType = Tca::table($dataType)->field($fieldName)->getType();
309
+		$this->view->assign('fieldType', ucfirst($fieldType));
310
+		$this->view->assign('dataType', $dataType);
311
+		$this->view->assign('fieldName', $fieldName);
312
+		$this->view->assign('matches', $matches);
313
+		$this->view->assign('fieldNameAndPath', $fieldNameAndPath);
314
+		$this->view->assign('numberOfObjects', $contentService->getNumberOfObjects());
315
+		$this->view->assign('hasRecursiveSelection', $hasRecursiveSelection);
316
+		$this->view->assign('editWholeSelection', empty($matches['uid'])); // necessary??
317
+
318
+		// Fetch content and its relations.
319
+		if ($fieldType === FieldType::MULTISELECT) {
320
+
321
+			$object = ContentRepositoryFactory::getInstance()->findOneBy($matcher);
322
+			$identifier = $this->getContentObjectResolver()->getValue($object, $fieldNameAndPath, 'uid');
323
+			$dataType = $this->getContentObjectResolver()->getDataType($object, $fieldNameAndPath);
324
+
325
+			$content = ContentRepositoryFactory::getInstance($dataType)->findByUid($identifier);
326
+
327
+			// Makes sure the object was retrieved. Security!
328
+			if (!$content) {
329
+				$message = sprintf('I could not retrieved content object of type "%s" with identifier %s.', $dataType, $identifier);
330
+				throw new \Exception($message, 1402350182);
331
+			}
332
+
333
+			$relatedDataType = Tca::table($dataType)->field($fieldName)->getForeignTable();
334
+
335
+			// Initialize the matcher object.
336
+			/** @var \Fab\Vidi\Persistence\Matcher $matcher */
337
+			$matcher = GeneralUtility::makeInstance('Fab\Vidi\Persistence\Matcher', array(), $relatedDataType);
338
+
339
+			// Default ordering for related data type.
340
+			$defaultOrderings = Tca::table($relatedDataType)->getDefaultOrderings();
341
+			/** @var \Fab\Vidi\Persistence\Order $order */
342
+			$defaultOrder = GeneralUtility::makeInstance('Fab\Vidi\Persistence\Order', $defaultOrderings);
343
+
344
+			// Fetch related contents
345
+			$relatedContents = ContentRepositoryFactory::getInstance($relatedDataType)->findBy($matcher, $defaultOrder);
346
+
347
+			if (Tca::table($dataType)->field($fieldName)->isRenderModeTree()) {
348
+
349
+				$fieldConfiguration = Tca::table($dataType)->field($fieldName)->getConfiguration();
350
+				$parentField = $fieldConfiguration['treeConfig']['parentField'];
351
+
352
+				$flatTree = array();
353
+				foreach ($relatedContents as $node) {
354
+					$flatTree[$node->getUid()] = array(
355
+						'item' => $node,
356
+						'parent' => $node[$parentField] ? $node[$parentField]['uid'] : NULL,
357
+					);
358
+				}
359
+
360
+				$tree = array();
361
+
362
+				// If leaves are selected without its parents selected, those are shown as parent
363
+				foreach ($flatTree as $id => &$flatNode) {
364
+					if (!isset($flatTree[$flatNode['parent']])) {
365
+						$flatNode['parent'] = NULL;
366
+					}
367
+				}
368
+
369
+				foreach ($flatTree as $id => &$node) {
370
+					if ($node['parent'] === NULL) {
371
+						$tree[$id] = &$node;
372
+					} else {
373
+						$flatTree[$node['parent']]['children'][$id] = &$node;
374
+					}
375
+				}
376
+
377
+				$relatedContents = $tree;
378
+			}
379
+
380
+			$this->view->assign('content', $content);
381
+			$this->view->assign('relatedContents', $relatedContents);
382
+			$this->view->assign('relatedDataType', $relatedDataType);
383
+			$this->view->assign('relatedContentTitle', Tca::table($relatedDataType)->getTitle());
384
+			$this->view->assign(
385
+				'renderMode',
386
+				Tca::table($dataType)->field($fieldName)->isRenderModeTree() ? FieldType::TREE : NULL
387
+			);
388
+		}
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
+	 * @return string
398
+	 */
399
+	public function deleteAction(array $matches = array())
400
+	{
401
+
402
+		$matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
403
+
404
+		// Fetch objects via the Content Service.
405
+		$contentService = $this->getContentService()->findBy($matcher);
406
+
407
+		// Compute the label field name of the table.
408
+		$tableTitleField = Tca::table()->getLabelField();
409
+
410
+		// Get result object for storing data along the processing.
411
+		$result = $this->getJsonResult();
412
+		$result->setNumberOfObjects($contentService->getNumberOfObjects());
413
+
414
+		foreach ($contentService->getObjects() as $object) {
415
+
416
+			// Store the first object, so that the delete message can be more explicit when deleting only one record.
417
+			if ($contentService->getNumberOfObjects() === 1) {
418
+				$tableTitleValue = $object[$tableTitleField];
419
+				$processedObjectData = array(
420
+					'uid' => $object->getUid(),
421
+					'name' => $tableTitleValue,
422
+				);
423
+				$result->setProcessedObject($processedObjectData);
424
+			}
425
+
426
+			// Properly delete object.
427
+			ContentRepositoryFactory::getInstance()->remove($object);
428
+
429
+			// Get the possible error messages and store them.
430
+			$errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
431
+			$result->addErrorMessages($errorMessages);
432
+		}
433
+
434
+		// Set the result and render the JSON view.
435
+		$this->getJsonView()->setResult($result);
436
+		return $this->getJsonView()->render();
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 = array())
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
+	 * @return string
461
+	 */
462
+	public function copyClipboardAction($target)
463
+	{
464
+
465
+		// Retrieve matcher object from clipboard.
466
+		$matcher = $this->getClipboardService()->getMatcher();
467
+
468
+		// Fetch objects via the Content Service.
469
+		$contentService = $this->getContentService()->findBy($matcher);
470
+
471
+		// Compute the label field name of the table.
472
+		$tableTitleField = Tca::table()->getLabelField();
473
+
474
+		// Get result object for storing data along the processing.
475
+		$result = $this->getJsonResult();
476
+		$result->setNumberOfObjects($contentService->getNumberOfObjects());
477
+
478
+		foreach ($contentService->getObjects() as $object) {
479
+
480
+			// Store the first object, so that the "action" message can be more explicit when deleting only one record.
481
+			if ($contentService->getNumberOfObjects() === 1) {
482
+				$tableTitleValue = $object[$tableTitleField];
483
+				$processedObjectData = array(
484
+					'uid' => $object->getUid(),
485
+					'name' => $tableTitleValue,
486
+				);
487
+				$result->setProcessedObject($processedObjectData);
488
+			}
489
+
490
+			// Work out the object.
491
+			ContentRepositoryFactory::getInstance()->copy($object, $target);
492
+
493
+			// Get the possible error messages and store them.
494
+			$errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
495
+			$result->addErrorMessages($errorMessages);
496
+		}
497
+
498
+		// Flush Clipboard if told so.
499
+		if (GeneralUtility::_GP('flushClipboard')) {
500
+			$this->getClipboardService()->flush();
501
+		}
502
+
503
+		// Set the result and render the JSON view.
504
+		$this->getJsonView()->setResult($result);
505
+		return $this->getJsonView()->render();
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
+	 * @return string
516
+	 */
517
+	public function moveAction($target, array $matches = array())
518
+	{
519
+
520
+		$matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
521
+
522
+		// Fetch objects via the Content Service.
523
+		$contentService = $this->getContentService()->findBy($matcher);
524
+
525
+		// Compute the label field name of the table.
526
+		$tableTitleField = Tca::table()->getLabelField();
527
+
528
+		// Get result object for storing data along the processing.
529
+		$result = $this->getJsonResult();
530
+		$result->setNumberOfObjects($contentService->getNumberOfObjects());
531
+
532
+		foreach ($contentService->getObjects() as $object) {
533
+
534
+			// Store the first object, so that the "action" message can be more explicit when deleting only one record.
535
+			if ($contentService->getNumberOfObjects() === 1) {
536
+				$tableTitleValue = $object[$tableTitleField];
537
+				$processedObjectData = array(
538
+					'uid' => $object->getUid(),
539
+					'name' => $tableTitleValue,
540
+				);
541
+				$result->setProcessedObject($processedObjectData);
542
+			}
543
+
544
+			// Work out the object.
545
+			ContentRepositoryFactory::getInstance()->move($object, $target);
546
+
547
+			// Get the possible error messages and store them.
548
+			$errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
549
+			$result->addErrorMessages($errorMessages);
550
+		}
551
+
552
+		// Set the result and render the JSON view.
553
+		$this->getJsonView()->setResult($result);
554
+		return $this->getJsonView()->render();
555
+	}
556
+
557
+	/**
558
+	 * Retrieve Content objects from the Clipboard then "move" them according to the target.
559
+	 *
560
+	 * @param string $target
561
+	 * @return string
562
+	 */
563
+	public function moveClipboardAction($target)
564
+	{
565
+
566
+		// Retrieve matcher object from clipboard.
567
+		$matcher = $this->getClipboardService()->getMatcher();
568
+
569
+		// Fetch objects via the Content Service.
570
+		$contentService = $this->getContentService()->findBy($matcher);
571
+
572
+		// Compute the label field name of the table.
573
+		$tableTitleField = Tca::table()->getLabelField();
574
+
575
+		// Get result object for storing data along the processing.
576
+		$result = $this->getJsonResult();
577
+		$result->setNumberOfObjects($contentService->getNumberOfObjects());
578
+
579
+		foreach ($contentService->getObjects() as $object) {
580
+
581
+			// Store the first object, so that the "action" message can be more explicit when deleting only one record.
582
+			if ($contentService->getNumberOfObjects() === 1) {
583
+				$tableTitleValue = $object[$tableTitleField];
584
+				$processedObjectData = array(
585
+					'uid' => $object->getUid(),
586
+					'name' => $tableTitleValue,
587
+				);
588
+				$result->setProcessedObject($processedObjectData);
589
+			}
590
+
591
+			// Work out the object.
592
+			ContentRepositoryFactory::getInstance()->move($object, $target);
593
+
594
+			// Get the possible error messages and store them.
595
+			$errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
596
+			$result->addErrorMessages($errorMessages);
597
+		}
598
+
599
+		// Flush Clipboard if told so.
600
+		if (GeneralUtility::_GP('flushClipboard')) {
601
+			$this->getClipboardService()->flush();
602
+		}
603
+
604
+		// Set the result and render the JSON view.
605
+		$this->getJsonView()->setResult($result);
606
+		return $this->getJsonView()->render();
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
+	 * @return string
618
+	 * @throws \Exception
619
+	 */
620
+	public function localizeAction($fieldNameAndPath, array $matches = array(), $language = 0)
621
+	{
622
+
623
+		$matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
624
+
625
+		// Fetch objects via the Content Service.
626
+		$contentService = $this->getContentService()->findBy($matcher);
627
+
628
+		// Get result object for storing data along the processing.
629
+		$result = $this->getJsonResult();
630
+		$result->setNumberOfObjects($contentService->getNumberOfObjects());
631
+
632
+		foreach ($contentService->getObjects() as $object) {
633
+
634
+			$identifier = $this->getContentObjectResolver()->getValue($object, $fieldNameAndPath, 'uid');
635
+			$dataType = $this->getContentObjectResolver()->getDataType($object, $fieldNameAndPath);
636
+
637
+			// Fetch the source object to be localized.
638
+			/** @var Content $content */
639
+			$content = ContentRepositoryFactory::getInstance($dataType)->findByIdentifier($identifier);
640
+
641
+			// Makes sure the object was retrieved. Security!
642
+			if (!$content) {
643
+				$message = sprintf('Something went wrong when retrieving content "%s" with identifier "%s".', $dataType, $identifier);
644
+				throw new \Exception($message, 1412343097);
645
+			}
646
+
647
+			// Handover the localization to the Repository.
648
+			ContentRepositoryFactory::getInstance($dataType)->localize($content, $language);
649
+
650
+			// Get the possible error messages and store them.
651
+			$errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
652
+
653
+			// Redirect to TCEForm so that the BE User can do its job!
654
+			if ($contentService->getNumberOfObjects() === 1) {
655
+
656
+				if (!empty($errorMessages)) {
657
+					$message = sprintf('Something went wrong when localizing content "%s" with identifier "%s". <br/>%s',
658
+						$dataType,
659
+						$identifier,
660
+						implode('<br/>', $errorMessages)
661
+					);
662
+					throw new \Exception($message, 1412343098);
663
+				}
664
+
665
+				$localizedContent = $this->getLanguageService()->getLocalizedContent($content, $language);
666
+				if (empty($localizedContent)) {
667
+					$message = sprintf('Oups! I could not retrieve localized content of type "%s" with identifier "%s"',
668
+						$content->getDataType(),
669
+						$content->getUid()
670
+					);
671
+					throw new \Exception($message, 1412343099);
672
+				}
673
+
674
+				/** @var \Fab\Vidi\View\Uri\EditUri $uri */
675
+				$uriRenderer = GeneralUtility::makeInstance('Fab\Vidi\View\Uri\EditUri');
676
+				$uri = $uriRenderer->render($localizedContent);
677
+				HttpUtility::redirect($uri);
678
+				break; // no need to further continue
679
+			}
680
+
681
+			$result->addErrorMessages($errorMessages);
682
+		}
683
+
684
+		// Set the result and render the JSON view.
685
+		$this->getJsonView()->setResult($result);
686
+		return $this->getJsonView()->render();
687
+	}
688
+
689
+	/**
690
+	 * Get the Vidi Module Loader.
691
+	 *
692
+	 * @return \Fab\Vidi\Service\ContentService
693
+	 */
694
+	protected function getContentService()
695
+	{
696
+		return GeneralUtility::makeInstance('Fab\Vidi\Service\ContentService');
697
+	}
698
+
699
+	/**
700
+	 * @return \Fab\Vidi\Resolver\ContentObjectResolver
701
+	 */
702
+	protected function getContentObjectResolver()
703
+	{
704
+		return GeneralUtility::makeInstance('Fab\Vidi\Resolver\ContentObjectResolver');
705
+	}
706
+
707
+	/**
708
+	 * @return \Fab\Vidi\Resolver\FieldPathResolver
709
+	 */
710
+	protected function getFieldPathResolver()
711
+	{
712
+		return GeneralUtility::makeInstance('Fab\Vidi\Resolver\FieldPathResolver');
713
+	}
714
+
715
+	/**
716
+	 * Return a special view for handling JSON
717
+	 * Goal is to have this view injected but require more configuration.
718
+	 *
719
+	 * @return JsonView
720
+	 */
721
+	protected function getJsonView()
722
+	{
723
+		if (!$this->view instanceof JsonView) {
724
+			/** @var JsonView $view */
725
+			$this->view = $this->objectManager->get('Fab\Vidi\Mvc\JsonView');
726
+			$this->view->setResponse($this->response);
727
+		}
728
+		return $this->view;
729
+	}
730
+
731
+	/**
732
+	 * @return JsonResult
733
+	 */
734
+	protected function getJsonResult()
735
+	{
736
+		return GeneralUtility::makeInstance('Fab\Vidi\Mvc\JsonResult');
737
+	}
738
+
739
+	/**
740
+	 * Signal that is called for post-processing content data send to the server for update.
741
+	 *
742
+	 * @param Content $contentObject
743
+	 * @param $fieldNameAndPath
744
+	 * @param $contentData
745
+	 * @param $counter
746
+	 * @param $savingBehavior
747
+	 * @param $language
748
+	 * @return ProcessContentDataSignalArguments
749
+	 * @signal
750
+	 */
751
+	protected function emitProcessContentDataSignal(Content $contentObject, $fieldNameAndPath, $contentData, $counter, $savingBehavior, $language)
752
+	{
753
+
754
+		/** @var \Fab\Vidi\Signal\ProcessContentDataSignalArguments $signalArguments */
755
+		$signalArguments = GeneralUtility::makeInstance('Fab\Vidi\Signal\ProcessContentDataSignalArguments');
756
+		$signalArguments->setContentObject($contentObject)
757
+			->setFieldNameAndPath($fieldNameAndPath)
758
+			->setContentData($contentData)
759
+			->setCounter($counter)
760
+			->setSavingBehavior($savingBehavior)
761
+			->setLanguage($language);
762
+
763
+		$signalResult = $this->getSignalSlotDispatcher()->dispatch('Fab\Vidi\Controller\Backend\ContentController', 'processContentData', array($signalArguments));
764
+		return $signalResult[0];
765
+	}
766
+
767
+	/**
768
+	 * Get the SignalSlot dispatcher.
769
+	 *
770
+	 * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
771
+	 */
772
+	protected function getSignalSlotDispatcher()
773
+	{
774
+		return $this->objectManager->get('TYPO3\\CMS\\Extbase\\SignalSlot\\Dispatcher');
775
+	}
776
+
777
+	/**
778
+	 * Get the Clipboard service.
779
+	 *
780
+	 * @return \Fab\Vidi\Service\ClipboardService
781
+	 */
782
+	protected function getClipboardService()
783
+	{
784
+		return GeneralUtility::makeInstance('Fab\Vidi\Service\ClipboardService');
785
+	}
786
+
787
+	/**
788
+	 * @return \Fab\Vidi\Language\LanguageService
789
+	 */
790
+	protected function getLanguageService()
791
+	{
792
+		return GeneralUtility::makeInstance('Fab\Vidi\Language\LanguageService');
793
+	}
794
+
795
+	/**
796
+	 * Get the Vidi Module Loader.
797
+	 *
798
+	 * @return \Fab\Vidi\Module\ModuleLoader
799
+	 */
800
+	protected function getModuleLoader()
801
+	{
802
+		return GeneralUtility::makeInstance('Fab\Vidi\Module\ModuleLoader');
803
+	}
804 804
 
805 805
 }
Please login to merge, or discard this patch.
Classes/DataHandler/CoreDataHandler.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -94,7 +94,7 @@
 block discarded – undo
94 94
      *
95 95
      * @param Content $content
96 96
      * @param string $target
97
-     * @return bool
97
+     * @return boolean|null
98 98
      */
99 99
     public function processCopy(Content $content, $target)
100 100
     {
Please login to merge, or discard this patch.
Indentation   +135 added lines, -135 removed lines patch added patch discarded remove patch
@@ -24,140 +24,140 @@
 block discarded – undo
24 24
 class CoreDataHandler extends AbstractDataHandler
25 25
 {
26 26
 
27
-    /**
28
-     * @var array
29
-     */
30
-    protected $dataHandler;
31
-
32
-    /**
33
-     * Process Content with action "update".
34
-     *
35
-     * @param Content $content
36
-     * @throws \Exception
37
-     * @return bool
38
-     */
39
-    public function processUpdate(Content $content)
40
-    {
41
-
42
-        $values = array();
43
-
44
-        // Check the field to be updated exists
45
-        foreach ($content->toArray() as $fieldName => $value) {
46
-            if (!Tca::table($content->getDataType())->hasField($fieldName)) {
47
-                $message = sprintf('It looks field "%s" does not exist for data type "%s"', $fieldName, $content->getDataType());
48
-                throw new \Exception($message, 1390668497);
49
-            }
50
-
51
-            // Flatten value if array given which is required for the DataHandler.
52
-            if (is_array($value)) {
53
-                $value = implode(',', $value);
54
-            }
55
-            $values[$fieldName] = $value;
56
-        }
57
-
58
-        $data[$content->getDataType()][$content->getUid()] = $values;
59
-
60
-        $dataHandler = $this->getDataHandler();
61
-        $dataHandler->start($data, array());
62
-        $dataHandler->process_datamap();
63
-        $this->errorMessages = $dataHandler->errorLog;
64
-
65
-        // Returns TRUE is log does not contain errors.
66
-        return empty($dataHandler->errorLog);
67
-    }
68
-
69
-    /**
70
-     * Process Content with action "remove".
71
-     *
72
-     * @param Content $content
73
-     * @return bool
74
-     */
75
-    public function processRemove(Content $content)
76
-    {
77
-
78
-        // Build command
79
-        $cmd[$content->getDataType()][$content->getUid()]['delete'] = 1;
80
-
81
-        /** @var $dataHandler \TYPO3\CMS\Core\DataHandling\DataHandler */
82
-        $dataHandler = $this->getDataHandler();
83
-        $dataHandler->start(array(), $cmd);
84
-        $dataHandler->process_datamap();
85
-        $dataHandler->process_cmdmap();
86
-        $this->errorMessages = $dataHandler->errorLog;
87
-
88
-        // Returns TRUE is log does not contain errors.
89
-        return empty($dataHandler->errorLog);
90
-    }
91
-
92
-    /**
93
-     * Process Content with action "copy".
94
-     *
95
-     * @param Content $content
96
-     * @param string $target
97
-     * @return bool
98
-     */
99
-    public function processCopy(Content $content, $target)
100
-    {
101
-        // TODO: Implement processCopy() method.
102
-    }
103
-
104
-    /**
105
-     * Process Content with action "move".
106
-     * The $target corresponds to the pid to move the records to.
107
-     * It can also be a negative value in case of sorting. The negative value would be the uid of its predecessor.
108
-     *
109
-     * @param Content $content
110
-     * @param int $target corresponds
111
-     * @return bool
112
-     */
113
-    public function processMove(Content $content, $target)
114
-    {
115
-
116
-        // Build command
117
-        $cmd[$content->getDataType()][$content->getUid()]['move'] = $target;
118
-
119
-        /** @var $dataHandler \TYPO3\CMS\Core\DataHandling\DataHandler */
120
-        $dataHandler = $this->getDataHandler();
121
-        $dataHandler->start(array(), $cmd);
122
-        $dataHandler->process_datamap();
123
-        $dataHandler->process_cmdmap();
124
-        $this->errorMessages = $dataHandler->errorLog;
125
-
126
-        // Returns TRUE is log does not contain errors.
127
-        return empty($dataHandler->errorLog);
128
-    }
129
-
130
-    /**
131
-     * Process Content with action "localize".
132
-     *
133
-     * @param Content $content
134
-     * @param int $language
135
-     * @return bool
136
-     */
137
-    public function processLocalize(Content $content, $language)
138
-    {
139
-
140
-        $command[$content->getDataType()][$content->getUid()]['localize'] = $language;
141
-
142
-        $dataHandler = $this->getDataHandler();
143
-        $dataHandler->start(array(), $command);
144
-        $dataHandler->process_datamap();
145
-        $dataHandler->process_cmdmap();
146
-        $this->errorMessages = $dataHandler->errorLog;
147
-
148
-        // Returns TRUE is log does not contain errors.
149
-        return empty($dataHandler->errorLog);
150
-    }
151
-
152
-    /**
153
-     * @return \TYPO3\CMS\Core\DataHandling\DataHandler
154
-     */
155
-    protected function getDataHandler()
156
-    {
157
-        if (!$this->dataHandler) {
158
-            $this->dataHandler = GeneralUtility::makeInstance('TYPO3\CMS\Core\DataHandling\DataHandler');
159
-        }
160
-        return $this->dataHandler;
161
-    }
27
+	/**
28
+	 * @var array
29
+	 */
30
+	protected $dataHandler;
31
+
32
+	/**
33
+	 * Process Content with action "update".
34
+	 *
35
+	 * @param Content $content
36
+	 * @throws \Exception
37
+	 * @return bool
38
+	 */
39
+	public function processUpdate(Content $content)
40
+	{
41
+
42
+		$values = array();
43
+
44
+		// Check the field to be updated exists
45
+		foreach ($content->toArray() as $fieldName => $value) {
46
+			if (!Tca::table($content->getDataType())->hasField($fieldName)) {
47
+				$message = sprintf('It looks field "%s" does not exist for data type "%s"', $fieldName, $content->getDataType());
48
+				throw new \Exception($message, 1390668497);
49
+			}
50
+
51
+			// Flatten value if array given which is required for the DataHandler.
52
+			if (is_array($value)) {
53
+				$value = implode(',', $value);
54
+			}
55
+			$values[$fieldName] = $value;
56
+		}
57
+
58
+		$data[$content->getDataType()][$content->getUid()] = $values;
59
+
60
+		$dataHandler = $this->getDataHandler();
61
+		$dataHandler->start($data, array());
62
+		$dataHandler->process_datamap();
63
+		$this->errorMessages = $dataHandler->errorLog;
64
+
65
+		// Returns TRUE is log does not contain errors.
66
+		return empty($dataHandler->errorLog);
67
+	}
68
+
69
+	/**
70
+	 * Process Content with action "remove".
71
+	 *
72
+	 * @param Content $content
73
+	 * @return bool
74
+	 */
75
+	public function processRemove(Content $content)
76
+	{
77
+
78
+		// Build command
79
+		$cmd[$content->getDataType()][$content->getUid()]['delete'] = 1;
80
+
81
+		/** @var $dataHandler \TYPO3\CMS\Core\DataHandling\DataHandler */
82
+		$dataHandler = $this->getDataHandler();
83
+		$dataHandler->start(array(), $cmd);
84
+		$dataHandler->process_datamap();
85
+		$dataHandler->process_cmdmap();
86
+		$this->errorMessages = $dataHandler->errorLog;
87
+
88
+		// Returns TRUE is log does not contain errors.
89
+		return empty($dataHandler->errorLog);
90
+	}
91
+
92
+	/**
93
+	 * Process Content with action "copy".
94
+	 *
95
+	 * @param Content $content
96
+	 * @param string $target
97
+	 * @return bool
98
+	 */
99
+	public function processCopy(Content $content, $target)
100
+	{
101
+		// TODO: Implement processCopy() method.
102
+	}
103
+
104
+	/**
105
+	 * Process Content with action "move".
106
+	 * The $target corresponds to the pid to move the records to.
107
+	 * It can also be a negative value in case of sorting. The negative value would be the uid of its predecessor.
108
+	 *
109
+	 * @param Content $content
110
+	 * @param int $target corresponds
111
+	 * @return bool
112
+	 */
113
+	public function processMove(Content $content, $target)
114
+	{
115
+
116
+		// Build command
117
+		$cmd[$content->getDataType()][$content->getUid()]['move'] = $target;
118
+
119
+		/** @var $dataHandler \TYPO3\CMS\Core\DataHandling\DataHandler */
120
+		$dataHandler = $this->getDataHandler();
121
+		$dataHandler->start(array(), $cmd);
122
+		$dataHandler->process_datamap();
123
+		$dataHandler->process_cmdmap();
124
+		$this->errorMessages = $dataHandler->errorLog;
125
+
126
+		// Returns TRUE is log does not contain errors.
127
+		return empty($dataHandler->errorLog);
128
+	}
129
+
130
+	/**
131
+	 * Process Content with action "localize".
132
+	 *
133
+	 * @param Content $content
134
+	 * @param int $language
135
+	 * @return bool
136
+	 */
137
+	public function processLocalize(Content $content, $language)
138
+	{
139
+
140
+		$command[$content->getDataType()][$content->getUid()]['localize'] = $language;
141
+
142
+		$dataHandler = $this->getDataHandler();
143
+		$dataHandler->start(array(), $command);
144
+		$dataHandler->process_datamap();
145
+		$dataHandler->process_cmdmap();
146
+		$this->errorMessages = $dataHandler->errorLog;
147
+
148
+		// Returns TRUE is log does not contain errors.
149
+		return empty($dataHandler->errorLog);
150
+	}
151
+
152
+	/**
153
+	 * @return \TYPO3\CMS\Core\DataHandling\DataHandler
154
+	 */
155
+	protected function getDataHandler()
156
+	{
157
+		if (!$this->dataHandler) {
158
+			$this->dataHandler = GeneralUtility::makeInstance('TYPO3\CMS\Core\DataHandling\DataHandler');
159
+		}
160
+		return $this->dataHandler;
161
+	}
162 162
 
163 163
 }
Please login to merge, or discard this patch.
Classes/Domain/Model/Content.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -385,7 +385,7 @@
 block discarded – undo
385 385
     /**
386 386
      * Return the properties of this object.
387 387
      *
388
-     * @return array
388
+     * @return string[]
389 389
      */
390 390
     public function toFields()
391 391
     {
Please login to merge, or discard this patch.
Indentation   +436 added lines, -436 removed lines patch added patch discarded remove patch
@@ -29,441 +29,441 @@
 block discarded – undo
29 29
 class Content implements \ArrayAccess
30 30
 {
31 31
 
32
-    /**
33
-     * @var int
34
-     */
35
-    protected $uid;
36
-
37
-    /**
38
-     * @var string
39
-     */
40
-    protected $dataType;
41
-
42
-    /**
43
-     * Constructor for a Content object.
44
-     *
45
-     * @param string $dataType will basically correspond to a table name, e.g fe_users, tt_content, ...
46
-     * @param array $contentData
47
-     * @return \Fab\Vidi\Domain\Model\Content
48
-     */
49
-    public function __construct($dataType, array $contentData = array())
50
-    {
51
-
52
-        $this->dataType = $dataType;
53
-        $this->uid = empty($contentData['uid']) ? NULL : (int)$contentData['uid'];
54
-
55
-        /** @var \Fab\Vidi\Tca\TableService $table */
56
-        $table = Tca::table($dataType);
57
-
58
-        // Initialize the array containing the allowed fields to be filled-in.
59
-        $fields = array('pid');
60
-
61
-        // If a creation time stamp has been defined for this data type.
62
-        if ($table->getTimeCreationField()) {
63
-            $fields[] = $table->getTimeCreationField();
64
-        }
65
-
66
-        // If an update time stamp has been defined for this data type.
67
-        if ($table->getTimeModificationField()) {
68
-            $fields[] = $table->getTimeModificationField();
69
-        }
70
-
71
-        // Merge the other fields allowed for this data type.
72
-        $fields = array_merge($fields, $table->getFields());
73
-
74
-        // Fetch excluded fields from the grid.
75
-        if ($this->isBackendMode()) {
76
-            $fields = $this->filterForConfiguration($fields);
77
-            $fields = $this->filterForBackendUser($fields);
78
-        }
79
-
80
-        // Get column to be displayed
81
-        foreach ($fields as $fieldName) {
82
-            if (array_key_exists($fieldName, $contentData)) {
83
-                $propertyName = Field::name($fieldName)->of($dataType)->toPropertyName();
84
-                $this->$propertyName = $contentData[$fieldName];
85
-            }
86
-        }
87
-    }
88
-
89
-    /**
90
-     * Dispatches magic methods (findBy[Property]())
91
-     *
92
-     * @param string $methodName The name of the magic method
93
-     * @param string $arguments The arguments of the magic method
94
-     * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnsupportedMethodException
95
-     * @return mixed
96
-     * @api
97
-     */
98
-    public function __call($methodName, $arguments)
99
-    {
100
-        $value = NULL;
101
-        if (substr($methodName, 0, 3) === 'get' && strlen($methodName) > 4) {
102
-            $propertyName = strtolower(substr(substr($methodName, 3), 0, 1)) . substr(substr($methodName, 3), 1);
103
-
104
-            $fieldName = Property::name($propertyName)->of($this)->toFieldName();
105
-            $field = Tca::table($this->dataType)->field($fieldName);
106
-
107
-            $value = $this->$propertyName;
108
-
109
-            // TRUE means it is a relation and it is not yet resolved.
110
-            if ($this->hasRelation($propertyName) && is_scalar($this->$propertyName)) {
111
-                $value = $this->resolveRelation($propertyName);
112
-            } elseif ($field->getType() === FieldType::RADIO || $field->getType() === FieldType::SELECT) {
113
-
114
-                // Attempt to convert the value into a label for radio and select fields.
115
-                $label = Tca::table($this->getDataType())->field($fieldName)->getLabelForItem($value);
116
-                if ($label) {
117
-                    $value = $label;
118
-                }
119
-            }
120
-
121
-        } elseif (substr($methodName, 0, 3) === 'set' && strlen($methodName) > 4 && isset($arguments[0])) {
122
-            $propertyName = strtolower(substr(substr($methodName, 3), 0, 1)) . substr(substr($methodName, 3), 1);
123
-            $this->$propertyName = $arguments[0];
124
-        }
125
-        return $value;
126
-    }
127
-
128
-    /**
129
-     * Tell whether the property has a relation.
130
-     *
131
-     * @param string $propertyName
132
-     * @return bool
133
-     */
134
-    protected function hasRelation($propertyName)
135
-    {
136
-        $fieldName = Property::name($propertyName)->of($this)->toFieldName();
137
-        return Tca::table($this->dataType)->field($fieldName)->hasRelation();
138
-    }
139
-
140
-    /**
141
-     * Try to "resolve" the property whether it has a relation.
142
-     * If the property has not relation it simply returns the same value.
143
-     *
144
-     * @throws \RuntimeException
145
-     * @param string $propertyName
146
-     * @return mixed
147
-     */
148
-    protected function resolveRelation($propertyName)
149
-    {
150
-
151
-        // Convert property name to field name and get the foreign data type.
152
-        $fieldName = Property::name($propertyName)->of($this)->toFieldName();
153
-        $foreignDataType = Tca::table($this->dataType)->field($fieldName)->relationDataType();
154
-
155
-        // Get the foreign repository instance form the factory
156
-        /** @var \Fab\Vidi\Domain\Repository\ContentRepository $foreignContentRepository */
157
-        $foreignContentRepository = ContentRepositoryFactory::getInstance($foreignDataType, $fieldName);
158
-
159
-        if (Tca::table($this->dataType)->field($fieldName)->hasRelationWithCommaSeparatedValues()) {
160
-
161
-            // Fetch values from repository
162
-            $values = GeneralUtility::trimExplode(',', $this->$propertyName);
163
-            $this->$propertyName = $foreignContentRepository->findIn('uid', $values);
164
-        } elseif (Tca::table($this->dataType)->field($fieldName)->hasMany()) {
165
-            // Include relation many-to-many and one-to-many
166
-            // Tca::table($this->dataType)->field($fieldName)->hasRelationOneToMany()
167
-            // Tca::table($this->dataType)->field($fieldName)->hasRelationManyToMany()
168
-
169
-            $foreignFieldName = Tca::table($this->dataType)->field($fieldName)->getForeignField();
170
-            if (empty($foreignFieldName)) {
171
-                $message = sprintf('Missing "foreign_field" key for field "%s" in table "%s".',
172
-                    $fieldName,
173
-                    $this->dataType
174
-                );
175
-                throw new \RuntimeException($message, 1376149186);
176
-            }
177
-
178
-            // Fetch values from repository.
179
-            $foreignPropertyName = Field::name($foreignFieldName)->of($this)->toPropertyName();
180
-            $findByProperty = 'findBy' . ucfirst($foreignPropertyName);
181
-
182
-            // Date picker (type == group) are special fields because property path must contain the table name
183
-            // to determine the relation type. Example for sys_category, property path will look like "items.sys_file"
184
-            $propertyValue = $this->uid;
185
-            if (Tca::table($foreignDataType)->field($foreignFieldName)->isGroup()) {
186
-                $propertyValue = $this->dataType . '.' . $this->uid;
187
-            }
188
-
189
-            $this->$propertyName = $foreignContentRepository->$findByProperty($propertyValue);
190
-
191
-        } elseif (Tca::table($this->dataType)->field($fieldName)->hasOne()) {
192
-
193
-            $fieldConfiguration = Tca::table($this->dataType)->field($fieldName)->getConfiguration();
194
-
195
-            // First case, we are on the "good side" of the relation, just query the repository
196
-            if (empty($fieldConfiguration['foreign_field'])) {
197
-                $this->$propertyName = $foreignContentRepository->findByUid($this->$propertyName);
198
-            } else {
199
-                // Second case, we are the "bad side" of the relation, query the foreign repository
200
-                // e.g. in case of one-to-one relation.
201
-
202
-                // We must query the opposite side to get the identifier of the foreign object.
203
-                $foreignDataType = Tca::table($this->dataType)->field($fieldName)->getForeignTable();
204
-                $foreignField = Tca::table($this->dataType)->field($fieldName)->getForeignField();
205
-                $foreignContentRepository = ContentRepositoryFactory::getInstance($foreignDataType);
206
-                $find = 'findOneBy' . GeneralUtility::underscoredToUpperCamelCase($foreignField);
207
-
208
-                /** @var Content $foreignObject */
209
-                $this->$propertyName = $foreignContentRepository->$find($this->getUid());
210
-            }
211
-
212
-        }
213
-        return $this->$propertyName;
214
-    }
215
-
216
-    /**
217
-     * @return int
218
-     */
219
-    public function getUid()
220
-    {
221
-        return $this->uid;
222
-    }
223
-
224
-    /**
225
-     * @return string
226
-     */
227
-    public function getDataType()
228
-    {
229
-        return $this->dataType;
230
-    }
231
-
232
-    /**
233
-     * Whether a offset exists
234
-     *
235
-     * @link http://php.net/manual/en/arrayaccess.offsetexists.php
236
-     * @param mixed $offset
237
-     * @return boolean true on success or false on failure.
238
-     */
239
-    public function offsetExists($offset)
240
-    {
241
-        $offset = Field::name($offset)->of($this)->toPropertyName();
242
-        return isset($this->$offset);
243
-    }
244
-
245
-    /**
246
-     * Offset to retrieve
247
-     *
248
-     * @link http://php.net/manual/en/arrayaccess.offsetget.php
249
-     * @param mixed $offset
250
-     * @return mixed Can return all value types.
251
-     */
252
-    public function offsetGet($offset)
253
-    {
254
-        $offset = Field::name($offset)->of($this)->toPropertyName();
255
-        $getter = 'get' . ucfirst($offset);
256
-        return $this->$getter();
257
-    }
258
-
259
-    /**
260
-     * Offset to set
261
-     *
262
-     * @link http://php.net/manual/en/arrayaccess.offsetset.php
263
-     * @param mixed $offset
264
-     * @param mixed $value
265
-     * @return $this
266
-     */
267
-    public function offsetSet($offset, $value)
268
-    {
269
-        $offset = Field::name($offset)->of($this)->toPropertyName();
270
-        $setter = 'set' . ucfirst($offset);
271
-        $this->$setter($value);
272
-        return $this;
273
-    }
274
-
275
-    /**
276
-     * Offset to unset
277
-     *
278
-     * @link http://php.net/manual/en/arrayaccess.offsetunset.php
279
-     * @param mixed $offset
280
-     * @throws NotImplementedException
281
-     * @return void
282
-     */
283
-    public function offsetUnset($offset)
284
-    {
285
-        $message = 'Un-setting value for Array object is not supported';
286
-        throw new NotImplementedException($message, 1376132306);
287
-    }
288
-
289
-    /**
290
-     * Convert this to array
291
-     *
292
-     * @return array
293
-     */
294
-    public function toArray()
295
-    {
296
-        $result['uid'] = $this->uid;
297
-        $propertiesAndValues = json_decode(json_encode($this), TRUE);
298
-
299
-        foreach ($propertiesAndValues as $propertyName => $value) {
300
-            $fieldName = Property::name($propertyName)->of($this)->toFieldName();
301
-            $result[$fieldName] = $value;
302
-        }
303
-
304
-        return $result;
305
-    }
306
-
307
-    /**
308
-     * Convert this object to an array containing the resolved values.
309
-     *
310
-     * @param bool $resolveRelations
311
-     * @return array
312
-     * @throws \Exception
313
-     */
314
-    public function toValues($resolveRelations = TRUE)
315
-    {
316
-        $result['uid'] = $this->uid;
317
-        $propertiesAndValues = json_decode(json_encode($this), TRUE);
318
-
319
-        foreach ($propertiesAndValues as $propertyName => $value) {
320
-            $fieldName = Property::name($propertyName)->of($this)->toFieldName();
321
-
322
-            $result[$fieldName] = $value;
323
-            if ($resolveRelations) {
324
-                $field = Tca::table($this->dataType)->field($fieldName);
325
-
326
-                $resolvedValue = '';
327
-                if ($field->getType() === FieldType::FILE) {
328
-
329
-                    if ($field->hasMany()) {
330
-                        $files = FileReferenceService::getInstance()->findReferencedBy($propertyName, $this);
331
-
332
-                        $resolvedValue = array();
333
-                        foreach ($files as $file) {
334
-                            $resolvedValue[] = $file->getIdentifier();
335
-                        }
336
-                    } else {
337
-                        $files = FileReferenceService::getInstance()->findReferencedBy($propertyName, $this);
338
-                        if (!empty($files)) {
339
-                            $resolvedValue = current($files)->getIdentifier();
340
-                        }
341
-                    }
342
-
343
-                    // Reset value
344
-                    $result[$fieldName] = $resolvedValue;
345
-
346
-                } elseif (Tca::table($this->dataType)->field($fieldName)->hasRelation()) {
347
-                    $objects = $this[$fieldName];
348
-                    if (is_array($objects)) {
349
-                        $resolvedValue = array();
350
-                        foreach ($objects as $object) {
351
-                            /** @var $object Content */
352
-                            $labelField = Tca::table($object->getDataType())->getLabelField();
353
-                            $resolvedValue[] = $object[$labelField];
354
-                        }
355
-                    } elseif ($objects instanceof Content) {
356
-                        $labelField = Tca::table($objects->getDataType())->getLabelField();
357
-                        $resolvedValue = $objects[$labelField];
358
-                    }
359
-
360
-                    // Reset value
361
-                    $result[$fieldName] = $resolvedValue;
362
-                }
363
-            }
364
-        }
365
-
366
-        return $result;
367
-    }
368
-
369
-    /**
370
-     * Return the properties of this object.
371
-     *
372
-     * @return array
373
-     */
374
-    public function toProperties()
375
-    {
376
-        $result[] = 'uid';
377
-        $propertiesAndValues = json_decode(json_encode($this), TRUE);
378
-
379
-        foreach ($propertiesAndValues as $propertyName => $value) {
380
-            $result[] = $propertyName;
381
-        }
382
-        return $result;
383
-    }
384
-
385
-    /**
386
-     * Return the properties of this object.
387
-     *
388
-     * @return array
389
-     */
390
-    public function toFields()
391
-    {
392
-        $result[] = 'uid';
393
-        $propertiesAndValues = json_decode(json_encode($this), TRUE);
394
-
395
-        foreach ($propertiesAndValues as $propertyName => $value) {
396
-            $result[] = Property::name($propertyName)->of($this)->toFieldName();
397
-        }
398
-
399
-        return $result;
400
-    }
401
-
402
-    /**
403
-     * @return string
404
-     */
405
-    public function __toString()
406
-    {
407
-        $labelField = Tca::table($this->dataType)->getLabelField();
408
-        return $this[$labelField];
409
-    }
410
-
411
-    /**
412
-     * Remove fields according to BE User permission.
413
-     *
414
-     * @param $fields
415
-     * @return array
416
-     * @throws \Exception
417
-     */
418
-    protected function filterForBackendUser($fields)
419
-    {
420
-        if (!$this->getBackendUser()->isAdmin()) {
421
-            foreach ($fields as $key => $fieldName) {
422
-                if (Tca::table($this->dataType)->hasField($fieldName) && !Tca::table($this->dataType)->field($fieldName)->hasAccess()) {
423
-                    unset($fields[$key]);
424
-                }
425
-            }
426
-        }
427
-        return $fields;
428
-    }
429
-
430
-    /**
431
-     * Remove fields according to Grid configuration.
432
-     *
433
-     * @param $fields
434
-     * @return array
435
-     */
436
-    protected function filterForConfiguration($fields)
437
-    {
438
-
439
-        $excludedFields = Tca::grid($this->dataType)->getExcludedFields();
440
-        foreach ($fields as $key => $field) {
441
-            if (in_array($field, $excludedFields)) {
442
-                unset($fields[$key]);
443
-            }
444
-        }
445
-
446
-        return $fields;
447
-    }
448
-
449
-    /**
450
-     * Returns an instance of the current Backend User.
451
-     *
452
-     * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
453
-     */
454
-    protected function getBackendUser()
455
-    {
456
-        return $GLOBALS['BE_USER'];
457
-    }
458
-
459
-    /**
460
-     * Returns whether the current mode is Backend
461
-     *
462
-     * @return bool
463
-     */
464
-    protected function isBackendMode()
465
-    {
466
-        return TYPO3_MODE == 'BE';
467
-    }
32
+	/**
33
+	 * @var int
34
+	 */
35
+	protected $uid;
36
+
37
+	/**
38
+	 * @var string
39
+	 */
40
+	protected $dataType;
41
+
42
+	/**
43
+	 * Constructor for a Content object.
44
+	 *
45
+	 * @param string $dataType will basically correspond to a table name, e.g fe_users, tt_content, ...
46
+	 * @param array $contentData
47
+	 * @return \Fab\Vidi\Domain\Model\Content
48
+	 */
49
+	public function __construct($dataType, array $contentData = array())
50
+	{
51
+
52
+		$this->dataType = $dataType;
53
+		$this->uid = empty($contentData['uid']) ? NULL : (int)$contentData['uid'];
54
+
55
+		/** @var \Fab\Vidi\Tca\TableService $table */
56
+		$table = Tca::table($dataType);
57
+
58
+		// Initialize the array containing the allowed fields to be filled-in.
59
+		$fields = array('pid');
60
+
61
+		// If a creation time stamp has been defined for this data type.
62
+		if ($table->getTimeCreationField()) {
63
+			$fields[] = $table->getTimeCreationField();
64
+		}
65
+
66
+		// If an update time stamp has been defined for this data type.
67
+		if ($table->getTimeModificationField()) {
68
+			$fields[] = $table->getTimeModificationField();
69
+		}
70
+
71
+		// Merge the other fields allowed for this data type.
72
+		$fields = array_merge($fields, $table->getFields());
73
+
74
+		// Fetch excluded fields from the grid.
75
+		if ($this->isBackendMode()) {
76
+			$fields = $this->filterForConfiguration($fields);
77
+			$fields = $this->filterForBackendUser($fields);
78
+		}
79
+
80
+		// Get column to be displayed
81
+		foreach ($fields as $fieldName) {
82
+			if (array_key_exists($fieldName, $contentData)) {
83
+				$propertyName = Field::name($fieldName)->of($dataType)->toPropertyName();
84
+				$this->$propertyName = $contentData[$fieldName];
85
+			}
86
+		}
87
+	}
88
+
89
+	/**
90
+	 * Dispatches magic methods (findBy[Property]())
91
+	 *
92
+	 * @param string $methodName The name of the magic method
93
+	 * @param string $arguments The arguments of the magic method
94
+	 * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnsupportedMethodException
95
+	 * @return mixed
96
+	 * @api
97
+	 */
98
+	public function __call($methodName, $arguments)
99
+	{
100
+		$value = NULL;
101
+		if (substr($methodName, 0, 3) === 'get' && strlen($methodName) > 4) {
102
+			$propertyName = strtolower(substr(substr($methodName, 3), 0, 1)) . substr(substr($methodName, 3), 1);
103
+
104
+			$fieldName = Property::name($propertyName)->of($this)->toFieldName();
105
+			$field = Tca::table($this->dataType)->field($fieldName);
106
+
107
+			$value = $this->$propertyName;
108
+
109
+			// TRUE means it is a relation and it is not yet resolved.
110
+			if ($this->hasRelation($propertyName) && is_scalar($this->$propertyName)) {
111
+				$value = $this->resolveRelation($propertyName);
112
+			} elseif ($field->getType() === FieldType::RADIO || $field->getType() === FieldType::SELECT) {
113
+
114
+				// Attempt to convert the value into a label for radio and select fields.
115
+				$label = Tca::table($this->getDataType())->field($fieldName)->getLabelForItem($value);
116
+				if ($label) {
117
+					$value = $label;
118
+				}
119
+			}
120
+
121
+		} elseif (substr($methodName, 0, 3) === 'set' && strlen($methodName) > 4 && isset($arguments[0])) {
122
+			$propertyName = strtolower(substr(substr($methodName, 3), 0, 1)) . substr(substr($methodName, 3), 1);
123
+			$this->$propertyName = $arguments[0];
124
+		}
125
+		return $value;
126
+	}
127
+
128
+	/**
129
+	 * Tell whether the property has a relation.
130
+	 *
131
+	 * @param string $propertyName
132
+	 * @return bool
133
+	 */
134
+	protected function hasRelation($propertyName)
135
+	{
136
+		$fieldName = Property::name($propertyName)->of($this)->toFieldName();
137
+		return Tca::table($this->dataType)->field($fieldName)->hasRelation();
138
+	}
139
+
140
+	/**
141
+	 * Try to "resolve" the property whether it has a relation.
142
+	 * If the property has not relation it simply returns the same value.
143
+	 *
144
+	 * @throws \RuntimeException
145
+	 * @param string $propertyName
146
+	 * @return mixed
147
+	 */
148
+	protected function resolveRelation($propertyName)
149
+	{
150
+
151
+		// Convert property name to field name and get the foreign data type.
152
+		$fieldName = Property::name($propertyName)->of($this)->toFieldName();
153
+		$foreignDataType = Tca::table($this->dataType)->field($fieldName)->relationDataType();
154
+
155
+		// Get the foreign repository instance form the factory
156
+		/** @var \Fab\Vidi\Domain\Repository\ContentRepository $foreignContentRepository */
157
+		$foreignContentRepository = ContentRepositoryFactory::getInstance($foreignDataType, $fieldName);
158
+
159
+		if (Tca::table($this->dataType)->field($fieldName)->hasRelationWithCommaSeparatedValues()) {
160
+
161
+			// Fetch values from repository
162
+			$values = GeneralUtility::trimExplode(',', $this->$propertyName);
163
+			$this->$propertyName = $foreignContentRepository->findIn('uid', $values);
164
+		} elseif (Tca::table($this->dataType)->field($fieldName)->hasMany()) {
165
+			// Include relation many-to-many and one-to-many
166
+			// Tca::table($this->dataType)->field($fieldName)->hasRelationOneToMany()
167
+			// Tca::table($this->dataType)->field($fieldName)->hasRelationManyToMany()
168
+
169
+			$foreignFieldName = Tca::table($this->dataType)->field($fieldName)->getForeignField();
170
+			if (empty($foreignFieldName)) {
171
+				$message = sprintf('Missing "foreign_field" key for field "%s" in table "%s".',
172
+					$fieldName,
173
+					$this->dataType
174
+				);
175
+				throw new \RuntimeException($message, 1376149186);
176
+			}
177
+
178
+			// Fetch values from repository.
179
+			$foreignPropertyName = Field::name($foreignFieldName)->of($this)->toPropertyName();
180
+			$findByProperty = 'findBy' . ucfirst($foreignPropertyName);
181
+
182
+			// Date picker (type == group) are special fields because property path must contain the table name
183
+			// to determine the relation type. Example for sys_category, property path will look like "items.sys_file"
184
+			$propertyValue = $this->uid;
185
+			if (Tca::table($foreignDataType)->field($foreignFieldName)->isGroup()) {
186
+				$propertyValue = $this->dataType . '.' . $this->uid;
187
+			}
188
+
189
+			$this->$propertyName = $foreignContentRepository->$findByProperty($propertyValue);
190
+
191
+		} elseif (Tca::table($this->dataType)->field($fieldName)->hasOne()) {
192
+
193
+			$fieldConfiguration = Tca::table($this->dataType)->field($fieldName)->getConfiguration();
194
+
195
+			// First case, we are on the "good side" of the relation, just query the repository
196
+			if (empty($fieldConfiguration['foreign_field'])) {
197
+				$this->$propertyName = $foreignContentRepository->findByUid($this->$propertyName);
198
+			} else {
199
+				// Second case, we are the "bad side" of the relation, query the foreign repository
200
+				// e.g. in case of one-to-one relation.
201
+
202
+				// We must query the opposite side to get the identifier of the foreign object.
203
+				$foreignDataType = Tca::table($this->dataType)->field($fieldName)->getForeignTable();
204
+				$foreignField = Tca::table($this->dataType)->field($fieldName)->getForeignField();
205
+				$foreignContentRepository = ContentRepositoryFactory::getInstance($foreignDataType);
206
+				$find = 'findOneBy' . GeneralUtility::underscoredToUpperCamelCase($foreignField);
207
+
208
+				/** @var Content $foreignObject */
209
+				$this->$propertyName = $foreignContentRepository->$find($this->getUid());
210
+			}
211
+
212
+		}
213
+		return $this->$propertyName;
214
+	}
215
+
216
+	/**
217
+	 * @return int
218
+	 */
219
+	public function getUid()
220
+	{
221
+		return $this->uid;
222
+	}
223
+
224
+	/**
225
+	 * @return string
226
+	 */
227
+	public function getDataType()
228
+	{
229
+		return $this->dataType;
230
+	}
231
+
232
+	/**
233
+	 * Whether a offset exists
234
+	 *
235
+	 * @link http://php.net/manual/en/arrayaccess.offsetexists.php
236
+	 * @param mixed $offset
237
+	 * @return boolean true on success or false on failure.
238
+	 */
239
+	public function offsetExists($offset)
240
+	{
241
+		$offset = Field::name($offset)->of($this)->toPropertyName();
242
+		return isset($this->$offset);
243
+	}
244
+
245
+	/**
246
+	 * Offset to retrieve
247
+	 *
248
+	 * @link http://php.net/manual/en/arrayaccess.offsetget.php
249
+	 * @param mixed $offset
250
+	 * @return mixed Can return all value types.
251
+	 */
252
+	public function offsetGet($offset)
253
+	{
254
+		$offset = Field::name($offset)->of($this)->toPropertyName();
255
+		$getter = 'get' . ucfirst($offset);
256
+		return $this->$getter();
257
+	}
258
+
259
+	/**
260
+	 * Offset to set
261
+	 *
262
+	 * @link http://php.net/manual/en/arrayaccess.offsetset.php
263
+	 * @param mixed $offset
264
+	 * @param mixed $value
265
+	 * @return $this
266
+	 */
267
+	public function offsetSet($offset, $value)
268
+	{
269
+		$offset = Field::name($offset)->of($this)->toPropertyName();
270
+		$setter = 'set' . ucfirst($offset);
271
+		$this->$setter($value);
272
+		return $this;
273
+	}
274
+
275
+	/**
276
+	 * Offset to unset
277
+	 *
278
+	 * @link http://php.net/manual/en/arrayaccess.offsetunset.php
279
+	 * @param mixed $offset
280
+	 * @throws NotImplementedException
281
+	 * @return void
282
+	 */
283
+	public function offsetUnset($offset)
284
+	{
285
+		$message = 'Un-setting value for Array object is not supported';
286
+		throw new NotImplementedException($message, 1376132306);
287
+	}
288
+
289
+	/**
290
+	 * Convert this to array
291
+	 *
292
+	 * @return array
293
+	 */
294
+	public function toArray()
295
+	{
296
+		$result['uid'] = $this->uid;
297
+		$propertiesAndValues = json_decode(json_encode($this), TRUE);
298
+
299
+		foreach ($propertiesAndValues as $propertyName => $value) {
300
+			$fieldName = Property::name($propertyName)->of($this)->toFieldName();
301
+			$result[$fieldName] = $value;
302
+		}
303
+
304
+		return $result;
305
+	}
306
+
307
+	/**
308
+	 * Convert this object to an array containing the resolved values.
309
+	 *
310
+	 * @param bool $resolveRelations
311
+	 * @return array
312
+	 * @throws \Exception
313
+	 */
314
+	public function toValues($resolveRelations = TRUE)
315
+	{
316
+		$result['uid'] = $this->uid;
317
+		$propertiesAndValues = json_decode(json_encode($this), TRUE);
318
+
319
+		foreach ($propertiesAndValues as $propertyName => $value) {
320
+			$fieldName = Property::name($propertyName)->of($this)->toFieldName();
321
+
322
+			$result[$fieldName] = $value;
323
+			if ($resolveRelations) {
324
+				$field = Tca::table($this->dataType)->field($fieldName);
325
+
326
+				$resolvedValue = '';
327
+				if ($field->getType() === FieldType::FILE) {
328
+
329
+					if ($field->hasMany()) {
330
+						$files = FileReferenceService::getInstance()->findReferencedBy($propertyName, $this);
331
+
332
+						$resolvedValue = array();
333
+						foreach ($files as $file) {
334
+							$resolvedValue[] = $file->getIdentifier();
335
+						}
336
+					} else {
337
+						$files = FileReferenceService::getInstance()->findReferencedBy($propertyName, $this);
338
+						if (!empty($files)) {
339
+							$resolvedValue = current($files)->getIdentifier();
340
+						}
341
+					}
342
+
343
+					// Reset value
344
+					$result[$fieldName] = $resolvedValue;
345
+
346
+				} elseif (Tca::table($this->dataType)->field($fieldName)->hasRelation()) {
347
+					$objects = $this[$fieldName];
348
+					if (is_array($objects)) {
349
+						$resolvedValue = array();
350
+						foreach ($objects as $object) {
351
+							/** @var $object Content */
352
+							$labelField = Tca::table($object->getDataType())->getLabelField();
353
+							$resolvedValue[] = $object[$labelField];
354
+						}
355
+					} elseif ($objects instanceof Content) {
356
+						$labelField = Tca::table($objects->getDataType())->getLabelField();
357
+						$resolvedValue = $objects[$labelField];
358
+					}
359
+
360
+					// Reset value
361
+					$result[$fieldName] = $resolvedValue;
362
+				}
363
+			}
364
+		}
365
+
366
+		return $result;
367
+	}
368
+
369
+	/**
370
+	 * Return the properties of this object.
371
+	 *
372
+	 * @return array
373
+	 */
374
+	public function toProperties()
375
+	{
376
+		$result[] = 'uid';
377
+		$propertiesAndValues = json_decode(json_encode($this), TRUE);
378
+
379
+		foreach ($propertiesAndValues as $propertyName => $value) {
380
+			$result[] = $propertyName;
381
+		}
382
+		return $result;
383
+	}
384
+
385
+	/**
386
+	 * Return the properties of this object.
387
+	 *
388
+	 * @return array
389
+	 */
390
+	public function toFields()
391
+	{
392
+		$result[] = 'uid';
393
+		$propertiesAndValues = json_decode(json_encode($this), TRUE);
394
+
395
+		foreach ($propertiesAndValues as $propertyName => $value) {
396
+			$result[] = Property::name($propertyName)->of($this)->toFieldName();
397
+		}
398
+
399
+		return $result;
400
+	}
401
+
402
+	/**
403
+	 * @return string
404
+	 */
405
+	public function __toString()
406
+	{
407
+		$labelField = Tca::table($this->dataType)->getLabelField();
408
+		return $this[$labelField];
409
+	}
410
+
411
+	/**
412
+	 * Remove fields according to BE User permission.
413
+	 *
414
+	 * @param $fields
415
+	 * @return array
416
+	 * @throws \Exception
417
+	 */
418
+	protected function filterForBackendUser($fields)
419
+	{
420
+		if (!$this->getBackendUser()->isAdmin()) {
421
+			foreach ($fields as $key => $fieldName) {
422
+				if (Tca::table($this->dataType)->hasField($fieldName) && !Tca::table($this->dataType)->field($fieldName)->hasAccess()) {
423
+					unset($fields[$key]);
424
+				}
425
+			}
426
+		}
427
+		return $fields;
428
+	}
429
+
430
+	/**
431
+	 * Remove fields according to Grid configuration.
432
+	 *
433
+	 * @param $fields
434
+	 * @return array
435
+	 */
436
+	protected function filterForConfiguration($fields)
437
+	{
438
+
439
+		$excludedFields = Tca::grid($this->dataType)->getExcludedFields();
440
+		foreach ($fields as $key => $field) {
441
+			if (in_array($field, $excludedFields)) {
442
+				unset($fields[$key]);
443
+			}
444
+		}
445
+
446
+		return $fields;
447
+	}
448
+
449
+	/**
450
+	 * Returns an instance of the current Backend User.
451
+	 *
452
+	 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
453
+	 */
454
+	protected function getBackendUser()
455
+	{
456
+		return $GLOBALS['BE_USER'];
457
+	}
458
+
459
+	/**
460
+	 * Returns whether the current mode is Backend
461
+	 *
462
+	 * @return bool
463
+	 */
464
+	protected function isBackendMode()
465
+	{
466
+		return TYPO3_MODE == 'BE';
467
+	}
468 468
 
469 469
 }
Please login to merge, or discard this patch.
Classes/Domain/Repository/ContentRepository.php 2 patches
Doc Comments   +3 added lines, -2 removed lines patch added patch discarded remove patch
@@ -271,7 +271,7 @@  discard block
 block discarded – undo
271 271
      * Update a content with new information.
272 272
      *
273 273
      * @param Content $content
274
-     * @param $language
274
+     * @param integer $language
275 275
      * @return bool
276 276
      */
277 277
     public function localize($content, $language)
@@ -352,6 +352,7 @@  discard block
 block discarded – undo
352 352
      * Copy a content within this repository.
353 353
      *
354 354
      * @param Content $content
355
+     * @param string $target
355 356
      * @return bool
356 357
      */
357 358
     public function copy($content, $target)
@@ -504,7 +505,7 @@  discard block
 block discarded – undo
504 505
     /**
505 506
      * Sets the default query settings to be used in this repository
506 507
      *
507
-     * @param QuerySettingsInterface $defaultQuerySettings The query settings to be used by default
508
+     * @param \Fab\Vidi\Persistence\QuerySettings $defaultQuerySettings The query settings to be used by default
508 509
      * @throws \BadMethodCallException
509 510
      * @return void
510 511
      * @api
Please login to merge, or discard this patch.
Indentation   +819 added lines, -819 removed lines patch added patch discarded remove patch
@@ -34,824 +34,824 @@
 block discarded – undo
34 34
 class ContentRepository implements RepositoryInterface
35 35
 {
36 36
 
37
-    /**
38
-     * Tell whether it is a raw result (array) or object being returned.
39
-     *
40
-     * @var bool
41
-     */
42
-    protected $rawResult = FALSE;
43
-
44
-    /**
45
-     * The data type to be returned, e.g fe_users, fe_groups, tt_content, etc...
46
-     *
47
-     * @var string
48
-     */
49
-    protected $dataType;
50
-
51
-    /**
52
-     * The source field is useful in the context of MM relations to know who is the caller
53
-     * e.g findByItems which eventually corresponds to a field name.
54
-     *
55
-     * @var string
56
-     */
57
-    protected $sourceFieldName = '';
58
-
59
-    /**
60
-     * @var array
61
-     */
62
-    protected $errorMessages = array();
63
-
64
-    /**
65
-     * @var QuerySettingsInterface
66
-     */
67
-    protected $defaultQuerySettings;
68
-
69
-    /**
70
-     * Constructor
71
-     *
72
-     * @param string $dataType
73
-     */
74
-    public function __construct($dataType)
75
-    {
76
-        $this->dataType = $dataType;
77
-    }
78
-
79
-    /**
80
-     * Returns all objects of this repository.
81
-     *
82
-     * @return Content[]
83
-     */
84
-    public function findAll()
85
-    {
86
-        $query = $this->createQuery();
87
-        return $query->execute();
88
-    }
89
-
90
-    /**
91
-     * Returns all "distinct" values for a given property.
92
-     *
93
-     * @param string $propertyName
94
-     * @param Matcher $matcher
95
-     * @return Content[]
96
-     */
97
-    public function findDistinctValues($propertyName, Matcher $matcher = NULL)
98
-    {
99
-        $query = $this->createQuery();
100
-        $query->setDistinct($propertyName);
101
-
102
-        // Remove empty values from selection.
103
-        $constraint = $query->logicalNot($query->equals($propertyName, ''));
104
-
105
-        // Add some additional constraints from the Matcher object.
106
-        $matcherConstraint = NULL;
107
-        if (!is_null($matcher)) {
108
-            $matcherConstraint = $this->computeConstraints($query, $matcher);
109
-        }
110
-
111
-        // Assemble the final constraints or not.
112
-        if ($matcherConstraint) {
113
-            $query->logicalAnd($matcherConstraint, $constraint);
114
-            $query->matching($query->logicalAnd($matcherConstraint, $constraint));
115
-        } else {
116
-            $query->matching($constraint);
117
-        }
118
-
119
-        return $query->execute();
120
-    }
121
-
122
-    /**
123
-     * Returns all "distinct" values for a given property.
124
-     *
125
-     * @param string $propertyName
126
-     * @param Matcher $matcher
127
-     * @return int
128
-     */
129
-    public function countDistinctValues($propertyName, Matcher $matcher = NULL)
130
-    {
131
-        $query = $this->createQuery();
132
-        $query->setDistinct($propertyName);
133
-
134
-        // Remove empty values from selection.
135
-        $constraint = $query->logicalNot($query->equals($propertyName, ''));
136
-
137
-        // Add some additional constraints from the Matcher object.
138
-        $matcherConstraint = NULL;
139
-        if (!is_null($matcher)) {
140
-            $matcherConstraint = $this->computeConstraints($query, $matcher);
141
-        }
142
-
143
-        // Assemble the final constraints or not.
144
-        if ($matcherConstraint) {
145
-            $query->logicalAnd($matcherConstraint, $constraint);
146
-            $query->matching($query->logicalAnd($matcherConstraint, $constraint));
147
-        } else {
148
-            $query->matching($constraint);
149
-        }
150
-
151
-        return $query->count();
152
-    }
153
-
154
-    /**
155
-     * Finds an object matching the given identifier.
156
-     *
157
-     * @param int $uid The identifier of the object to find
158
-     * @return Content|NULL
159
-     * @api
160
-     */
161
-    public function findByUid($uid)
162
-    {
163
-        return $this->findByIdentifier($uid);
164
-    }
165
-
166
-    /**
167
-     * Finds all Contents given specified matches.
168
-     *
169
-     * @param string $propertyName
170
-     * @param array $values
171
-     * @return Content[]
172
-     */
173
-    public function findIn($propertyName, array $values)
174
-    {
175
-        $query = $this->createQuery();
176
-        $query->matching($query->in($propertyName, $values));
177
-        return $query->execute();
178
-    }
179
-
180
-    /**
181
-     * Finds all Contents given specified matches.
182
-     *
183
-     * @param Matcher $matcher
184
-     * @param Order $order The order
185
-     * @param int $limit
186
-     * @param int $offset
187
-     * @return Content[]
188
-     */
189
-    public function findBy(Matcher $matcher, Order $order = NULL, $limit = NULL, $offset = NULL)
190
-    {
191
-
192
-        $query = $this->createQuery();
193
-
194
-        if ($limit) {
195
-            $query->setLimit($limit);
196
-        }
197
-
198
-        if ($order) {
199
-            $query->setOrderings($order->getOrderings());
200
-
201
-            // Loops around the orderings adding if necessary a dummy condition
202
-            // to make sure the relations can be resolved when transforming the query to plain SQL.
203
-            foreach ($order->getOrderings() as $ordering => $direction) {
204
-                if ($this->hasForeignRelationIn($ordering)) {
205
-                    $relationalField = $this->getForeignRelationFrom($ordering);
206
-                    $matcher->like($relationalField . '.uid', '');
207
-                }
208
-            }
209
-        }
210
-
211
-        if ($offset) {
212
-            $query->setOffset($offset);
213
-        }
214
-
215
-        $constraints = $this->computeConstraints($query, $matcher);
216
-
217
-        if ($constraints) {
218
-            $query->matching($constraints);
219
-        }
220
-
221
-        return $query->execute();
222
-    }
223
-
224
-    /**
225
-     * Find one Content object given specified matches.
226
-     *
227
-     * @param Matcher $matcher
228
-     * @return Content
229
-     */
230
-    public function findOneBy(Matcher $matcher)
231
-    {
232
-
233
-        $query = $this->createQuery();
234
-
235
-        $constraints = $this->computeConstraints($query, $matcher);
236
-
237
-        if ($constraints) {
238
-            $query->matching($constraints);
239
-        }
240
-
241
-        $query->setLimit(1); // only take one!
242
-
243
-        $resultSet = $query->execute();
244
-        if ($resultSet) {
245
-            $resultSet = current($resultSet);
246
-        }
247
-        return $resultSet;
248
-    }
249
-
250
-    /**
251
-     * Count all Contents given specified matches.
252
-     *
253
-     * @param Matcher $matcher
254
-     * @return int
255
-     */
256
-    public function countBy(Matcher $matcher)
257
-    {
258
-
259
-        $query = $this->createQuery();
260
-
261
-        $constraints = $this->computeConstraints($query, $matcher);
262
-
263
-        if ($constraints) {
264
-            $query->matching($constraints);
265
-        }
266
-
267
-        return $query->count();
268
-    }
269
-
270
-    /**
271
-     * Update a content with new information.
272
-     *
273
-     * @param Content $content
274
-     * @param $language
275
-     * @return bool
276
-     */
277
-    public function localize($content, $language)
278
-    {
279
-
280
-        // Security check
281
-        $this->getContentValidator()->validate($content);
282
-        $this->getLanguageValidator()->validate($language);
283
-
284
-        $dataType = $content->getDataType();
285
-        $handler = $this->getDataHandlerFactory()->action(ProcessAction::LOCALIZE)->forType($dataType)->getDataHandler();
286
-
287
-        $handlerResult = $handler->processLocalize($content, $language);
288
-        $this->errorMessages = $handler->getErrorMessages();
289
-        return $handlerResult;
290
-    }
291
-
292
-    /**
293
-     * Update a content with new information.
294
-     *
295
-     * @param Content $content
296
-     * @return bool
297
-     */
298
-    public function update($content)
299
-    {
300
-
301
-        // Security check.
302
-        $this->getContentValidator()->validate($content);
303
-
304
-        $dataType = $content->getDataType();
305
-        $handler = $this->getDataHandlerFactory()->action(ProcessAction::UPDATE)->forType($dataType)->getDataHandler();
306
-
307
-        $handlerResult = $handler->processUpdate($content);
308
-        $this->errorMessages = $handler->getErrorMessages();
309
-        return $handlerResult;
310
-    }
311
-
312
-    /**
313
-     * Removes an object from this repository.
314
-     *
315
-     * @param Content $content
316
-     * @return boolean
317
-     */
318
-    public function remove($content)
319
-    {
320
-        $dataType = $content->getDataType();
321
-        $handler = $this->getDataHandlerFactory()->action(ProcessAction::REMOVE)->forType($dataType)->getDataHandler();
322
-
323
-        $handlerResult = $handler->processRemove($content);
324
-        $this->errorMessages = $handler->getErrorMessages();
325
-        return $handlerResult;
326
-    }
327
-
328
-    /**
329
-     * Move a content within this repository.
330
-     * The $target corresponds to the pid to move the records to.
331
-     * It can also be a negative value in case of sorting. The negative value would be the uid of its predecessor.
332
-     *
333
-     * @param Content $content
334
-     * @param string $target
335
-     * @return bool
336
-     */
337
-    public function move($content, $target)
338
-    {
339
-
340
-        // Security check.
341
-        $this->getContentValidator()->validate($content);
342
-
343
-        $dataType = $content->getDataType();
344
-        $handler = $this->getDataHandlerFactory()->action(ProcessAction::MOVE)->forType($dataType)->getDataHandler();
345
-
346
-        $handlerResult = $handler->processMove($content, $target);
347
-        $this->errorMessages = $handler->getErrorMessages();
348
-        return $handlerResult;
349
-    }
350
-
351
-    /**
352
-     * Copy a content within this repository.
353
-     *
354
-     * @param Content $content
355
-     * @return bool
356
-     */
357
-    public function copy($content, $target)
358
-    {
359
-
360
-        // Security check.
361
-        $this->getContentValidator()->validate($content);
362
-
363
-        $dataType = $content->getDataType();
364
-        $handler = $this->getDataHandlerFactory()->action(ProcessAction::COPY)->forType($dataType)->getDataHandler();
365
-
366
-        $handlerResult = $handler->processCopy($content, $target);
367
-        $this->errorMessages = $handler->getErrorMessages();
368
-        return $handlerResult;
369
-    }
370
-
371
-    /**
372
-     * Adds an object to this repository.
373
-     *
374
-     * @param object $object The object to add
375
-     * @throws \BadMethodCallException
376
-     * @return void
377
-     * @api
378
-     */
379
-    public function add($object)
380
-    {
381
-        throw new \BadMethodCallException('Repository does not support the add() method.', 1375805599);
382
-    }
383
-
384
-    /**
385
-     * Returns the total number objects of this repository.
386
-     *
387
-     * @return integer The object count
388
-     * @api
389
-     */
390
-    public function countAll()
391
-    {
392
-        $query = $this->createQuery();
393
-        return $query->count();
394
-    }
395
-
396
-    /**
397
-     * Removes all objects of this repository as if remove() was called for
398
-     * all of them.
399
-     *
400
-     * @return void
401
-     * @api
402
-     */
403
-    public function removeAll()
404
-    {
405
-        // TODO: Implement removeAll() method.
406
-    }
407
-
408
-    /**
409
-     * Finds an object matching the given identifier.
410
-     *
411
-     * @param mixed $identifier The identifier of the object to find
412
-     * @return Content|NULL
413
-     * @api
414
-     */
415
-    public function findByIdentifier($identifier)
416
-    {
417
-        $query = $this->createQuery();
418
-
419
-        $result = $query->matching($query->equals('uid', $identifier))
420
-            ->execute();
421
-
422
-        if (is_array($result)) {
423
-            $result = current($result);
424
-        }
425
-
426
-        return $result;
427
-    }
428
-
429
-    /**
430
-     * Dispatches magic methods (findBy[Property]())
431
-     *
432
-     * @param string $methodName The name of the magic method
433
-     * @param string $arguments The arguments of the magic method
434
-     * @throws UnsupportedMethodException
435
-     * @return mixed
436
-     * @api
437
-     */
438
-    public function __call($methodName, $arguments)
439
-    {
440
-        if (substr($methodName, 0, 6) === 'findBy' && strlen($methodName) > 7) {
441
-            $propertyName = strtolower(substr(substr($methodName, 6), 0, 1)) . substr(substr($methodName, 6), 1);
442
-            $result = $this->processMagicCall($propertyName, $arguments[0]);
443
-        } elseif (substr($methodName, 0, 9) === 'findOneBy' && strlen($methodName) > 10) {
444
-            $propertyName = strtolower(substr(substr($methodName, 9), 0, 1)) . substr(substr($methodName, 9), 1);
445
-            $result = $this->processMagicCall($propertyName, $arguments[0], 'one');
446
-        } elseif (substr($methodName, 0, 7) === 'countBy' && strlen($methodName) > 8) {
447
-            $propertyName = strtolower(substr(substr($methodName, 7), 0, 1)) . substr(substr($methodName, 7), 1);
448
-            $result = $this->processMagicCall($propertyName, $arguments[0], 'count');
449
-        } else {
450
-            throw new UnsupportedMethodException('The method "' . $methodName . '" is not supported by the repository.', 1360838010);
451
-        }
452
-        return $result;
453
-    }
454
-
455
-    /**
456
-     * Returns a query for objects of this repository
457
-     *
458
-     * @return Query
459
-     * @api
460
-     */
461
-    public function createQuery()
462
-    {
463
-        /** @var Query $query */
464
-        $query = $this->getObjectManager()->get('Fab\Vidi\Persistence\Query', $this->dataType);
465
-        $query->setSourceFieldName($this->sourceFieldName);
466
-
467
-        if ($this->defaultQuerySettings) {
468
-            $query->setQuerySettings($this->defaultQuerySettings);
469
-        } else {
470
-
471
-            // Initialize and pass the query settings at this level.
472
-            /** @var \Fab\Vidi\Persistence\QuerySettings $querySettings */
473
-            $querySettings = $this->getObjectManager()->get('Fab\Vidi\Persistence\QuerySettings');
474
-
475
-            // Default choice for the BE.
476
-            if ($this->isBackendMode()) {
477
-                $querySettings->setIgnoreEnableFields(TRUE);
478
-            }
479
-
480
-            $query->setQuerySettings($querySettings);
481
-        }
482
-
483
-        return $query;
484
-    }
485
-
486
-    /**
487
-     * Sets the property names to order the result by per default.
488
-     * Expected like this:
489
-     * array(
490
-     * 'foo' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING,
491
-     * 'bar' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
492
-     * )
493
-     *
494
-     * @param array $defaultOrderings The property names to order by
495
-     * @throws \BadMethodCallException
496
-     * @return void
497
-     * @api
498
-     */
499
-    public function setDefaultOrderings(array $defaultOrderings)
500
-    {
501
-        throw new \BadMethodCallException('Repository does not support the setDefaultOrderings() method.', 1375805598);
502
-    }
503
-
504
-    /**
505
-     * Sets the default query settings to be used in this repository
506
-     *
507
-     * @param QuerySettingsInterface $defaultQuerySettings The query settings to be used by default
508
-     * @throws \BadMethodCallException
509
-     * @return void
510
-     * @api
511
-     */
512
-    public function setDefaultQuerySettings(QuerySettingsInterface $defaultQuerySettings)
513
-    {
514
-        $this->defaultQuerySettings = $defaultQuerySettings;
515
-    }
516
-
517
-    /**
518
-     * @return array
519
-     */
520
-    public function getErrorMessages()
521
-    {
522
-        return $this->errorMessages;
523
-    }
524
-
525
-    /**
526
-     * @param string $sourceFieldName
527
-     * @return $this
528
-     */
529
-    public function setSourceFieldName($sourceFieldName)
530
-    {
531
-        $this->sourceFieldName = $sourceFieldName;
532
-        return $this;
533
-    }
534
-
535
-    /**
536
-     * @return string
537
-     */
538
-    public function getDataType()
539
-    {
540
-        return $this->dataType;
541
-    }
542
-
543
-    /**
544
-     * Tell whether the order has a foreign table in its expression, e.g. "metadata.title".
545
-     *
546
-     * @param string $ordering
547
-     * @return bool
548
-     */
549
-    protected function hasForeignRelationIn($ordering)
550
-    {
551
-        return strpos($ordering, '.') !== FALSE;
552
-    }
553
-
554
-    /**
555
-     * Extract the foreign relation of the ordering "metadata.title" -> "metadata"
556
-     *
557
-     * @param string $ordering
558
-     * @return string
559
-     */
560
-    protected function getForeignRelationFrom($ordering)
561
-    {
562
-        $parts = explode('.', $ordering);
563
-        return $parts[0];
564
-    }
565
-
566
-    /**
567
-     * Get the constraints
568
-     *
569
-     * @param Query $query
570
-     * @param Matcher $matcher
571
-     * @return ConstraintInterface|NULL
572
-     */
573
-    protected function computeConstraints(Query $query, Matcher $matcher)
574
-    {
575
-
576
-        $constraints = NULL;
577
-
578
-        $collectedConstraints = array();
579
-
580
-        // Search term
581
-        $constraint = $this->computeSearchTermConstraint($query, $matcher);
582
-        if ($constraint) {
583
-            $collectedConstraints[] = $constraint;
584
-        }
585
-
586
-        foreach ($matcher->getSupportedOperators() as $operator) {
587
-            $constraint = $this->computeConstraint($query, $matcher, $operator);
588
-            if ($constraint) {
589
-                $collectedConstraints[] = $constraint;
590
-            }
591
-        }
592
-
593
-        if (count($collectedConstraints) > 1) {
594
-            $logical = $matcher->getDefaultLogicalSeparator();
595
-            $constraints = $query->$logical($collectedConstraints);
596
-        } elseif (!empty($collectedConstraints)) {
597
-
598
-            // true means there is one constraint only and should become the result
599
-            $constraints = current($collectedConstraints);
600
-        }
601
-
602
-        // Trigger signal for post processing the computed constraints object.
603
-        $constraints = $this->emitPostProcessConstraintsSignal($query, $constraints);
604
-
605
-        return $constraints;
606
-    }
607
-
608
-    /**
609
-     * Computes the search constraint and returns it.
610
-     *
611
-     * @param Query $query
612
-     * @param Matcher $matcher
613
-     * @return ConstraintInterface|NULL
614
-     */
615
-    protected function computeSearchTermConstraint(Query $query, Matcher $matcher)
616
-    {
617
-
618
-        $result = NULL;
619
-
620
-        // Search term case
621
-        if ($matcher->getSearchTerm()) {
622
-
623
-            $fields = GeneralUtility::trimExplode(',', Tca::table($this->dataType)->getSearchFields(), TRUE);
624
-
625
-            $constraints = array();
626
-            $likeClause = sprintf('%%%s%%', $matcher->getSearchTerm());
627
-            foreach ($fields as $fieldNameAndPath) {
628
-                if ($this->isSuitableForLike($fieldNameAndPath, $matcher->getSearchTerm())) {
629
-
630
-                    $dataType = $this->getFieldPathResolver()->getDataType($fieldNameAndPath, $this->dataType);
631
-                    $fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath, $this->dataType);
632
-
633
-                    if (Tca::table($dataType)->hasField($fieldName) && Tca::table($dataType)->field($fieldName)->hasRelation()) {
634
-                        $foreignTable = Tca::table($dataType)->field($fieldName)->getForeignTable();
635
-                        $fieldNameAndPath = $fieldNameAndPath . '.' . Tca::table($foreignTable)->getLabelField();
636
-                    }
637
-                    $constraints[] = $query->like($fieldNameAndPath, $likeClause);
638
-                }
639
-            }
640
-            $logical = $matcher->getLogicalSeparatorForSearchTerm();
641
-            $result = $query->$logical($constraints);
642
-        }
643
-
644
-        return $result;
645
-    }
646
-
647
-    /**
648
-     * It does not make sense to have a "like" in presence of numerical field, e.g "uid".
649
-     * Tell whether the given value makes sense for a "like" clause.
650
-     *
651
-     * @param string $fieldNameAndPath
652
-     * @param string $value
653
-     * @return bool
654
-     */
655
-    protected function isSuitableForLike($fieldNameAndPath, $value)
656
-    {
657
-        $isSuitable = TRUE;
658
-
659
-        // TRUE means it is a string
660
-        if (!MathUtility::canBeInterpretedAsInteger($value)) {
661
-
662
-            $dataType = $this->getFieldPathResolver()->getDataType($fieldNameAndPath, $this->dataType);
663
-            $fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath, $this->dataType);
664
-
665
-            if (Tca::table($dataType)->field($fieldName)->isNumerical()
666
-                && !Tca::table($dataType)->field($fieldName)->hasRelation()
667
-            ) {
668
-                $isSuitable = FALSE;
669
-            }
670
-        }
671
-
672
-        return $isSuitable;
673
-    }
674
-
675
-    /**
676
-     * Computes the constraint for matches and returns it.
677
-     *
678
-     * @param Query $query
679
-     * @param Matcher $matcher
680
-     * @param string $operator
681
-     * @return ConstraintInterface|NULL
682
-     */
683
-    protected function computeConstraint(Query $query, Matcher $matcher, $operator)
684
-    {
685
-        $result = NULL;
686
-
687
-        $operatorName = ucfirst($operator);
688
-        $getCriteria = sprintf('get%sCriteria', $operatorName);
689
-        $criteria = $matcher->$getCriteria();
690
-
691
-        if (!empty($criteria)) {
692
-            $constraints = array();
693
-
694
-            foreach ($criteria as $criterion) {
695
-
696
-                $fieldNameAndPath = $criterion['fieldNameAndPath'];
697
-                $operand = $criterion['operand'];
698
-
699
-                // Compute a few variables...
700
-                // $dataType is generally equals to $this->dataType but not always... if fieldName is a path.
701
-                $dataType = $this->getFieldPathResolver()->getDataType($fieldNameAndPath, $this->dataType);
702
-                $fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath, $this->dataType);
703
-                $fieldPath = $this->getFieldPathResolver()->stripFieldName($fieldNameAndPath, $this->dataType);
704
-
705
-                if (Tca::table($dataType)->field($fieldName)->hasRelation()) {
706
-                    if (MathUtility::canBeInterpretedAsInteger($operand)) {
707
-                        $fieldNameAndPath = $fieldName . '.uid';
708
-                    } else {
709
-                        $foreignTableName = Tca::table($dataType)->field($fieldName)->getForeignTable();
710
-                        $foreignTable = Tca::table($foreignTableName);
711
-                        $fieldNameAndPath = $fieldName . '.' . $foreignTable->getLabelField();
712
-                    }
713
-
714
-                    // If different means we should restore the prepended path segment for proper SQL parser.
715
-                    // This is TRUE for a composite field, e.g items.sys_file_metadata for categories.
716
-                    if ($fieldName !== $fieldPath) {
717
-                        $fieldNameAndPath = $fieldPath . '.' . $fieldNameAndPath;
718
-                    }
719
-                }
720
-
721
-                $constraints[] = $query->$operator($fieldNameAndPath, $criterion['operand']);
722
-            }
723
-
724
-            $getLogicalSeparator = sprintf('getLogicalSeparatorFor%s', $operatorName);
725
-            $logical = $matcher->$getLogicalSeparator();
726
-            $result = $query->$logical($constraints);
727
-        }
728
-
729
-        return $result;
730
-    }
731
-
732
-    /**
733
-     * @return \TYPO3\CMS\Core\DataHandling\DataHandler
734
-     */
735
-    protected function getDataHandler()
736
-    {
737
-        if (!$this->dataHandler) {
738
-            $this->dataHandler = GeneralUtility::makeInstance('TYPO3\CMS\Core\DataHandling\DataHandler');
739
-        }
740
-        return $this->dataHandler;
741
-    }
742
-
743
-    /**
744
-     * Handle the magic call by properly creating a Query object and returning its result.
745
-     *
746
-     * @param string $propertyName
747
-     * @param string $value
748
-     * @param string $flag
749
-     * @return array
750
-     */
751
-    protected function processMagicCall($propertyName, $value, $flag = '')
752
-    {
753
-
754
-        $fieldName = Property::name($propertyName)->of($this->dataType)->toFieldName();
755
-
756
-        /** @var $matcher Matcher */
757
-        $matcher = GeneralUtility::makeInstance('Fab\Vidi\Persistence\Matcher', array(), $this->getDataType());
758
-
759
-        $table = Tca::table($this->dataType);
760
-        if ($table->field($fieldName)->isGroup()) {
761
-
762
-            $valueParts = explode('.', $value, 2);
763
-            $fieldName = $fieldName . '.' . $valueParts[0];
764
-            $value = $valueParts[1];
765
-        }
766
-
767
-        $matcher->equals($fieldName, $value);
768
-
769
-        if ($flag == 'count') {
770
-            $result = $this->countBy($matcher);
771
-        } else {
772
-            $result = $this->findBy($matcher);
773
-        }
774
-        return $flag == 'one' && !empty($result) ? reset($result) : $result;
775
-    }
776
-
777
-    /**
778
-     * @return \Fab\Vidi\DataHandler\DataHandlerFactory
779
-     */
780
-    protected function getDataHandlerFactory()
781
-    {
782
-        return GeneralUtility::makeInstance('Fab\Vidi\DataHandler\DataHandlerFactory');
783
-    }
784
-
785
-    /**
786
-     * Returns whether the current mode is Backend
787
-     *
788
-     * @return bool
789
-     */
790
-    protected function isBackendMode()
791
-    {
792
-        return TYPO3_MODE == 'BE';
793
-    }
794
-
795
-    /**
796
-     * @return \Fab\Vidi\Resolver\FieldPathResolver
797
-     */
798
-    protected function getFieldPathResolver()
799
-    {
800
-        return GeneralUtility::makeInstance('Fab\Vidi\Resolver\FieldPathResolver');
801
-    }
802
-
803
-    /**
804
-     * @return \TYPO3\CMS\Extbase\Object\ObjectManager
805
-     */
806
-    protected function getObjectManager()
807
-    {
808
-        return GeneralUtility::makeInstance('TYPO3\CMS\Extbase\Object\ObjectManager');
809
-    }
810
-
811
-    /**
812
-     * @return \Fab\Vidi\Domain\Validator\ContentValidator
813
-     */
814
-    protected function getContentValidator()
815
-    {
816
-        return GeneralUtility::makeInstance('Fab\Vidi\Domain\Validator\ContentValidator');
817
-    }
818
-
819
-    /**
820
-     * @return \Fab\Vidi\Domain\Validator\LanguageValidator
821
-     */
822
-    protected function getLanguageValidator()
823
-    {
824
-        return GeneralUtility::makeInstance('Fab\Vidi\Domain\Validator\LanguageValidator');
825
-    }
826
-
827
-    /**
828
-     * Signal that is called for post-processing the computed constraints object.
829
-     *
830
-     * @param Query $query
831
-     * @param ConstraintInterface|NULL $constraints
832
-     * @return ConstraintInterface|NULL $constraints
833
-     * @signal
834
-     */
835
-    protected function emitPostProcessConstraintsSignal(Query $query, $constraints)
836
-    {
837
-        $result = $this->getSignalSlotDispatcher()->dispatch(
838
-            'Fab\Vidi\Domain\Repository\ContentRepository',
839
-            'postProcessConstraintsObject',
840
-            array(
841
-                $query,
842
-                $constraints
843
-            )
844
-        );
845
-
846
-        return $result[1];
847
-    }
848
-
849
-    /**
850
-     * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
851
-     */
852
-    protected function getSignalSlotDispatcher()
853
-    {
854
-        return $this->getObjectManager()->get('TYPO3\\CMS\\Extbase\\SignalSlot\\Dispatcher');
855
-    }
37
+	/**
38
+	 * Tell whether it is a raw result (array) or object being returned.
39
+	 *
40
+	 * @var bool
41
+	 */
42
+	protected $rawResult = FALSE;
43
+
44
+	/**
45
+	 * The data type to be returned, e.g fe_users, fe_groups, tt_content, etc...
46
+	 *
47
+	 * @var string
48
+	 */
49
+	protected $dataType;
50
+
51
+	/**
52
+	 * The source field is useful in the context of MM relations to know who is the caller
53
+	 * e.g findByItems which eventually corresponds to a field name.
54
+	 *
55
+	 * @var string
56
+	 */
57
+	protected $sourceFieldName = '';
58
+
59
+	/**
60
+	 * @var array
61
+	 */
62
+	protected $errorMessages = array();
63
+
64
+	/**
65
+	 * @var QuerySettingsInterface
66
+	 */
67
+	protected $defaultQuerySettings;
68
+
69
+	/**
70
+	 * Constructor
71
+	 *
72
+	 * @param string $dataType
73
+	 */
74
+	public function __construct($dataType)
75
+	{
76
+		$this->dataType = $dataType;
77
+	}
78
+
79
+	/**
80
+	 * Returns all objects of this repository.
81
+	 *
82
+	 * @return Content[]
83
+	 */
84
+	public function findAll()
85
+	{
86
+		$query = $this->createQuery();
87
+		return $query->execute();
88
+	}
89
+
90
+	/**
91
+	 * Returns all "distinct" values for a given property.
92
+	 *
93
+	 * @param string $propertyName
94
+	 * @param Matcher $matcher
95
+	 * @return Content[]
96
+	 */
97
+	public function findDistinctValues($propertyName, Matcher $matcher = NULL)
98
+	{
99
+		$query = $this->createQuery();
100
+		$query->setDistinct($propertyName);
101
+
102
+		// Remove empty values from selection.
103
+		$constraint = $query->logicalNot($query->equals($propertyName, ''));
104
+
105
+		// Add some additional constraints from the Matcher object.
106
+		$matcherConstraint = NULL;
107
+		if (!is_null($matcher)) {
108
+			$matcherConstraint = $this->computeConstraints($query, $matcher);
109
+		}
110
+
111
+		// Assemble the final constraints or not.
112
+		if ($matcherConstraint) {
113
+			$query->logicalAnd($matcherConstraint, $constraint);
114
+			$query->matching($query->logicalAnd($matcherConstraint, $constraint));
115
+		} else {
116
+			$query->matching($constraint);
117
+		}
118
+
119
+		return $query->execute();
120
+	}
121
+
122
+	/**
123
+	 * Returns all "distinct" values for a given property.
124
+	 *
125
+	 * @param string $propertyName
126
+	 * @param Matcher $matcher
127
+	 * @return int
128
+	 */
129
+	public function countDistinctValues($propertyName, Matcher $matcher = NULL)
130
+	{
131
+		$query = $this->createQuery();
132
+		$query->setDistinct($propertyName);
133
+
134
+		// Remove empty values from selection.
135
+		$constraint = $query->logicalNot($query->equals($propertyName, ''));
136
+
137
+		// Add some additional constraints from the Matcher object.
138
+		$matcherConstraint = NULL;
139
+		if (!is_null($matcher)) {
140
+			$matcherConstraint = $this->computeConstraints($query, $matcher);
141
+		}
142
+
143
+		// Assemble the final constraints or not.
144
+		if ($matcherConstraint) {
145
+			$query->logicalAnd($matcherConstraint, $constraint);
146
+			$query->matching($query->logicalAnd($matcherConstraint, $constraint));
147
+		} else {
148
+			$query->matching($constraint);
149
+		}
150
+
151
+		return $query->count();
152
+	}
153
+
154
+	/**
155
+	 * Finds an object matching the given identifier.
156
+	 *
157
+	 * @param int $uid The identifier of the object to find
158
+	 * @return Content|NULL
159
+	 * @api
160
+	 */
161
+	public function findByUid($uid)
162
+	{
163
+		return $this->findByIdentifier($uid);
164
+	}
165
+
166
+	/**
167
+	 * Finds all Contents given specified matches.
168
+	 *
169
+	 * @param string $propertyName
170
+	 * @param array $values
171
+	 * @return Content[]
172
+	 */
173
+	public function findIn($propertyName, array $values)
174
+	{
175
+		$query = $this->createQuery();
176
+		$query->matching($query->in($propertyName, $values));
177
+		return $query->execute();
178
+	}
179
+
180
+	/**
181
+	 * Finds all Contents given specified matches.
182
+	 *
183
+	 * @param Matcher $matcher
184
+	 * @param Order $order The order
185
+	 * @param int $limit
186
+	 * @param int $offset
187
+	 * @return Content[]
188
+	 */
189
+	public function findBy(Matcher $matcher, Order $order = NULL, $limit = NULL, $offset = NULL)
190
+	{
191
+
192
+		$query = $this->createQuery();
193
+
194
+		if ($limit) {
195
+			$query->setLimit($limit);
196
+		}
197
+
198
+		if ($order) {
199
+			$query->setOrderings($order->getOrderings());
200
+
201
+			// Loops around the orderings adding if necessary a dummy condition
202
+			// to make sure the relations can be resolved when transforming the query to plain SQL.
203
+			foreach ($order->getOrderings() as $ordering => $direction) {
204
+				if ($this->hasForeignRelationIn($ordering)) {
205
+					$relationalField = $this->getForeignRelationFrom($ordering);
206
+					$matcher->like($relationalField . '.uid', '');
207
+				}
208
+			}
209
+		}
210
+
211
+		if ($offset) {
212
+			$query->setOffset($offset);
213
+		}
214
+
215
+		$constraints = $this->computeConstraints($query, $matcher);
216
+
217
+		if ($constraints) {
218
+			$query->matching($constraints);
219
+		}
220
+
221
+		return $query->execute();
222
+	}
223
+
224
+	/**
225
+	 * Find one Content object given specified matches.
226
+	 *
227
+	 * @param Matcher $matcher
228
+	 * @return Content
229
+	 */
230
+	public function findOneBy(Matcher $matcher)
231
+	{
232
+
233
+		$query = $this->createQuery();
234
+
235
+		$constraints = $this->computeConstraints($query, $matcher);
236
+
237
+		if ($constraints) {
238
+			$query->matching($constraints);
239
+		}
240
+
241
+		$query->setLimit(1); // only take one!
242
+
243
+		$resultSet = $query->execute();
244
+		if ($resultSet) {
245
+			$resultSet = current($resultSet);
246
+		}
247
+		return $resultSet;
248
+	}
249
+
250
+	/**
251
+	 * Count all Contents given specified matches.
252
+	 *
253
+	 * @param Matcher $matcher
254
+	 * @return int
255
+	 */
256
+	public function countBy(Matcher $matcher)
257
+	{
258
+
259
+		$query = $this->createQuery();
260
+
261
+		$constraints = $this->computeConstraints($query, $matcher);
262
+
263
+		if ($constraints) {
264
+			$query->matching($constraints);
265
+		}
266
+
267
+		return $query->count();
268
+	}
269
+
270
+	/**
271
+	 * Update a content with new information.
272
+	 *
273
+	 * @param Content $content
274
+	 * @param $language
275
+	 * @return bool
276
+	 */
277
+	public function localize($content, $language)
278
+	{
279
+
280
+		// Security check
281
+		$this->getContentValidator()->validate($content);
282
+		$this->getLanguageValidator()->validate($language);
283
+
284
+		$dataType = $content->getDataType();
285
+		$handler = $this->getDataHandlerFactory()->action(ProcessAction::LOCALIZE)->forType($dataType)->getDataHandler();
286
+
287
+		$handlerResult = $handler->processLocalize($content, $language);
288
+		$this->errorMessages = $handler->getErrorMessages();
289
+		return $handlerResult;
290
+	}
291
+
292
+	/**
293
+	 * Update a content with new information.
294
+	 *
295
+	 * @param Content $content
296
+	 * @return bool
297
+	 */
298
+	public function update($content)
299
+	{
300
+
301
+		// Security check.
302
+		$this->getContentValidator()->validate($content);
303
+
304
+		$dataType = $content->getDataType();
305
+		$handler = $this->getDataHandlerFactory()->action(ProcessAction::UPDATE)->forType($dataType)->getDataHandler();
306
+
307
+		$handlerResult = $handler->processUpdate($content);
308
+		$this->errorMessages = $handler->getErrorMessages();
309
+		return $handlerResult;
310
+	}
311
+
312
+	/**
313
+	 * Removes an object from this repository.
314
+	 *
315
+	 * @param Content $content
316
+	 * @return boolean
317
+	 */
318
+	public function remove($content)
319
+	{
320
+		$dataType = $content->getDataType();
321
+		$handler = $this->getDataHandlerFactory()->action(ProcessAction::REMOVE)->forType($dataType)->getDataHandler();
322
+
323
+		$handlerResult = $handler->processRemove($content);
324
+		$this->errorMessages = $handler->getErrorMessages();
325
+		return $handlerResult;
326
+	}
327
+
328
+	/**
329
+	 * Move a content within this repository.
330
+	 * The $target corresponds to the pid to move the records to.
331
+	 * It can also be a negative value in case of sorting. The negative value would be the uid of its predecessor.
332
+	 *
333
+	 * @param Content $content
334
+	 * @param string $target
335
+	 * @return bool
336
+	 */
337
+	public function move($content, $target)
338
+	{
339
+
340
+		// Security check.
341
+		$this->getContentValidator()->validate($content);
342
+
343
+		$dataType = $content->getDataType();
344
+		$handler = $this->getDataHandlerFactory()->action(ProcessAction::MOVE)->forType($dataType)->getDataHandler();
345
+
346
+		$handlerResult = $handler->processMove($content, $target);
347
+		$this->errorMessages = $handler->getErrorMessages();
348
+		return $handlerResult;
349
+	}
350
+
351
+	/**
352
+	 * Copy a content within this repository.
353
+	 *
354
+	 * @param Content $content
355
+	 * @return bool
356
+	 */
357
+	public function copy($content, $target)
358
+	{
359
+
360
+		// Security check.
361
+		$this->getContentValidator()->validate($content);
362
+
363
+		$dataType = $content->getDataType();
364
+		$handler = $this->getDataHandlerFactory()->action(ProcessAction::COPY)->forType($dataType)->getDataHandler();
365
+
366
+		$handlerResult = $handler->processCopy($content, $target);
367
+		$this->errorMessages = $handler->getErrorMessages();
368
+		return $handlerResult;
369
+	}
370
+
371
+	/**
372
+	 * Adds an object to this repository.
373
+	 *
374
+	 * @param object $object The object to add
375
+	 * @throws \BadMethodCallException
376
+	 * @return void
377
+	 * @api
378
+	 */
379
+	public function add($object)
380
+	{
381
+		throw new \BadMethodCallException('Repository does not support the add() method.', 1375805599);
382
+	}
383
+
384
+	/**
385
+	 * Returns the total number objects of this repository.
386
+	 *
387
+	 * @return integer The object count
388
+	 * @api
389
+	 */
390
+	public function countAll()
391
+	{
392
+		$query = $this->createQuery();
393
+		return $query->count();
394
+	}
395
+
396
+	/**
397
+	 * Removes all objects of this repository as if remove() was called for
398
+	 * all of them.
399
+	 *
400
+	 * @return void
401
+	 * @api
402
+	 */
403
+	public function removeAll()
404
+	{
405
+		// TODO: Implement removeAll() method.
406
+	}
407
+
408
+	/**
409
+	 * Finds an object matching the given identifier.
410
+	 *
411
+	 * @param mixed $identifier The identifier of the object to find
412
+	 * @return Content|NULL
413
+	 * @api
414
+	 */
415
+	public function findByIdentifier($identifier)
416
+	{
417
+		$query = $this->createQuery();
418
+
419
+		$result = $query->matching($query->equals('uid', $identifier))
420
+			->execute();
421
+
422
+		if (is_array($result)) {
423
+			$result = current($result);
424
+		}
425
+
426
+		return $result;
427
+	}
428
+
429
+	/**
430
+	 * Dispatches magic methods (findBy[Property]())
431
+	 *
432
+	 * @param string $methodName The name of the magic method
433
+	 * @param string $arguments The arguments of the magic method
434
+	 * @throws UnsupportedMethodException
435
+	 * @return mixed
436
+	 * @api
437
+	 */
438
+	public function __call($methodName, $arguments)
439
+	{
440
+		if (substr($methodName, 0, 6) === 'findBy' && strlen($methodName) > 7) {
441
+			$propertyName = strtolower(substr(substr($methodName, 6), 0, 1)) . substr(substr($methodName, 6), 1);
442
+			$result = $this->processMagicCall($propertyName, $arguments[0]);
443
+		} elseif (substr($methodName, 0, 9) === 'findOneBy' && strlen($methodName) > 10) {
444
+			$propertyName = strtolower(substr(substr($methodName, 9), 0, 1)) . substr(substr($methodName, 9), 1);
445
+			$result = $this->processMagicCall($propertyName, $arguments[0], 'one');
446
+		} elseif (substr($methodName, 0, 7) === 'countBy' && strlen($methodName) > 8) {
447
+			$propertyName = strtolower(substr(substr($methodName, 7), 0, 1)) . substr(substr($methodName, 7), 1);
448
+			$result = $this->processMagicCall($propertyName, $arguments[0], 'count');
449
+		} else {
450
+			throw new UnsupportedMethodException('The method "' . $methodName . '" is not supported by the repository.', 1360838010);
451
+		}
452
+		return $result;
453
+	}
454
+
455
+	/**
456
+	 * Returns a query for objects of this repository
457
+	 *
458
+	 * @return Query
459
+	 * @api
460
+	 */
461
+	public function createQuery()
462
+	{
463
+		/** @var Query $query */
464
+		$query = $this->getObjectManager()->get('Fab\Vidi\Persistence\Query', $this->dataType);
465
+		$query->setSourceFieldName($this->sourceFieldName);
466
+
467
+		if ($this->defaultQuerySettings) {
468
+			$query->setQuerySettings($this->defaultQuerySettings);
469
+		} else {
470
+
471
+			// Initialize and pass the query settings at this level.
472
+			/** @var \Fab\Vidi\Persistence\QuerySettings $querySettings */
473
+			$querySettings = $this->getObjectManager()->get('Fab\Vidi\Persistence\QuerySettings');
474
+
475
+			// Default choice for the BE.
476
+			if ($this->isBackendMode()) {
477
+				$querySettings->setIgnoreEnableFields(TRUE);
478
+			}
479
+
480
+			$query->setQuerySettings($querySettings);
481
+		}
482
+
483
+		return $query;
484
+	}
485
+
486
+	/**
487
+	 * Sets the property names to order the result by per default.
488
+	 * Expected like this:
489
+	 * array(
490
+	 * 'foo' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING,
491
+	 * 'bar' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
492
+	 * )
493
+	 *
494
+	 * @param array $defaultOrderings The property names to order by
495
+	 * @throws \BadMethodCallException
496
+	 * @return void
497
+	 * @api
498
+	 */
499
+	public function setDefaultOrderings(array $defaultOrderings)
500
+	{
501
+		throw new \BadMethodCallException('Repository does not support the setDefaultOrderings() method.', 1375805598);
502
+	}
503
+
504
+	/**
505
+	 * Sets the default query settings to be used in this repository
506
+	 *
507
+	 * @param QuerySettingsInterface $defaultQuerySettings The query settings to be used by default
508
+	 * @throws \BadMethodCallException
509
+	 * @return void
510
+	 * @api
511
+	 */
512
+	public function setDefaultQuerySettings(QuerySettingsInterface $defaultQuerySettings)
513
+	{
514
+		$this->defaultQuerySettings = $defaultQuerySettings;
515
+	}
516
+
517
+	/**
518
+	 * @return array
519
+	 */
520
+	public function getErrorMessages()
521
+	{
522
+		return $this->errorMessages;
523
+	}
524
+
525
+	/**
526
+	 * @param string $sourceFieldName
527
+	 * @return $this
528
+	 */
529
+	public function setSourceFieldName($sourceFieldName)
530
+	{
531
+		$this->sourceFieldName = $sourceFieldName;
532
+		return $this;
533
+	}
534
+
535
+	/**
536
+	 * @return string
537
+	 */
538
+	public function getDataType()
539
+	{
540
+		return $this->dataType;
541
+	}
542
+
543
+	/**
544
+	 * Tell whether the order has a foreign table in its expression, e.g. "metadata.title".
545
+	 *
546
+	 * @param string $ordering
547
+	 * @return bool
548
+	 */
549
+	protected function hasForeignRelationIn($ordering)
550
+	{
551
+		return strpos($ordering, '.') !== FALSE;
552
+	}
553
+
554
+	/**
555
+	 * Extract the foreign relation of the ordering "metadata.title" -> "metadata"
556
+	 *
557
+	 * @param string $ordering
558
+	 * @return string
559
+	 */
560
+	protected function getForeignRelationFrom($ordering)
561
+	{
562
+		$parts = explode('.', $ordering);
563
+		return $parts[0];
564
+	}
565
+
566
+	/**
567
+	 * Get the constraints
568
+	 *
569
+	 * @param Query $query
570
+	 * @param Matcher $matcher
571
+	 * @return ConstraintInterface|NULL
572
+	 */
573
+	protected function computeConstraints(Query $query, Matcher $matcher)
574
+	{
575
+
576
+		$constraints = NULL;
577
+
578
+		$collectedConstraints = array();
579
+
580
+		// Search term
581
+		$constraint = $this->computeSearchTermConstraint($query, $matcher);
582
+		if ($constraint) {
583
+			$collectedConstraints[] = $constraint;
584
+		}
585
+
586
+		foreach ($matcher->getSupportedOperators() as $operator) {
587
+			$constraint = $this->computeConstraint($query, $matcher, $operator);
588
+			if ($constraint) {
589
+				$collectedConstraints[] = $constraint;
590
+			}
591
+		}
592
+
593
+		if (count($collectedConstraints) > 1) {
594
+			$logical = $matcher->getDefaultLogicalSeparator();
595
+			$constraints = $query->$logical($collectedConstraints);
596
+		} elseif (!empty($collectedConstraints)) {
597
+
598
+			// true means there is one constraint only and should become the result
599
+			$constraints = current($collectedConstraints);
600
+		}
601
+
602
+		// Trigger signal for post processing the computed constraints object.
603
+		$constraints = $this->emitPostProcessConstraintsSignal($query, $constraints);
604
+
605
+		return $constraints;
606
+	}
607
+
608
+	/**
609
+	 * Computes the search constraint and returns it.
610
+	 *
611
+	 * @param Query $query
612
+	 * @param Matcher $matcher
613
+	 * @return ConstraintInterface|NULL
614
+	 */
615
+	protected function computeSearchTermConstraint(Query $query, Matcher $matcher)
616
+	{
617
+
618
+		$result = NULL;
619
+
620
+		// Search term case
621
+		if ($matcher->getSearchTerm()) {
622
+
623
+			$fields = GeneralUtility::trimExplode(',', Tca::table($this->dataType)->getSearchFields(), TRUE);
624
+
625
+			$constraints = array();
626
+			$likeClause = sprintf('%%%s%%', $matcher->getSearchTerm());
627
+			foreach ($fields as $fieldNameAndPath) {
628
+				if ($this->isSuitableForLike($fieldNameAndPath, $matcher->getSearchTerm())) {
629
+
630
+					$dataType = $this->getFieldPathResolver()->getDataType($fieldNameAndPath, $this->dataType);
631
+					$fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath, $this->dataType);
632
+
633
+					if (Tca::table($dataType)->hasField($fieldName) && Tca::table($dataType)->field($fieldName)->hasRelation()) {
634
+						$foreignTable = Tca::table($dataType)->field($fieldName)->getForeignTable();
635
+						$fieldNameAndPath = $fieldNameAndPath . '.' . Tca::table($foreignTable)->getLabelField();
636
+					}
637
+					$constraints[] = $query->like($fieldNameAndPath, $likeClause);
638
+				}
639
+			}
640
+			$logical = $matcher->getLogicalSeparatorForSearchTerm();
641
+			$result = $query->$logical($constraints);
642
+		}
643
+
644
+		return $result;
645
+	}
646
+
647
+	/**
648
+	 * It does not make sense to have a "like" in presence of numerical field, e.g "uid".
649
+	 * Tell whether the given value makes sense for a "like" clause.
650
+	 *
651
+	 * @param string $fieldNameAndPath
652
+	 * @param string $value
653
+	 * @return bool
654
+	 */
655
+	protected function isSuitableForLike($fieldNameAndPath, $value)
656
+	{
657
+		$isSuitable = TRUE;
658
+
659
+		// TRUE means it is a string
660
+		if (!MathUtility::canBeInterpretedAsInteger($value)) {
661
+
662
+			$dataType = $this->getFieldPathResolver()->getDataType($fieldNameAndPath, $this->dataType);
663
+			$fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath, $this->dataType);
664
+
665
+			if (Tca::table($dataType)->field($fieldName)->isNumerical()
666
+				&& !Tca::table($dataType)->field($fieldName)->hasRelation()
667
+			) {
668
+				$isSuitable = FALSE;
669
+			}
670
+		}
671
+
672
+		return $isSuitable;
673
+	}
674
+
675
+	/**
676
+	 * Computes the constraint for matches and returns it.
677
+	 *
678
+	 * @param Query $query
679
+	 * @param Matcher $matcher
680
+	 * @param string $operator
681
+	 * @return ConstraintInterface|NULL
682
+	 */
683
+	protected function computeConstraint(Query $query, Matcher $matcher, $operator)
684
+	{
685
+		$result = NULL;
686
+
687
+		$operatorName = ucfirst($operator);
688
+		$getCriteria = sprintf('get%sCriteria', $operatorName);
689
+		$criteria = $matcher->$getCriteria();
690
+
691
+		if (!empty($criteria)) {
692
+			$constraints = array();
693
+
694
+			foreach ($criteria as $criterion) {
695
+
696
+				$fieldNameAndPath = $criterion['fieldNameAndPath'];
697
+				$operand = $criterion['operand'];
698
+
699
+				// Compute a few variables...
700
+				// $dataType is generally equals to $this->dataType but not always... if fieldName is a path.
701
+				$dataType = $this->getFieldPathResolver()->getDataType($fieldNameAndPath, $this->dataType);
702
+				$fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath, $this->dataType);
703
+				$fieldPath = $this->getFieldPathResolver()->stripFieldName($fieldNameAndPath, $this->dataType);
704
+
705
+				if (Tca::table($dataType)->field($fieldName)->hasRelation()) {
706
+					if (MathUtility::canBeInterpretedAsInteger($operand)) {
707
+						$fieldNameAndPath = $fieldName . '.uid';
708
+					} else {
709
+						$foreignTableName = Tca::table($dataType)->field($fieldName)->getForeignTable();
710
+						$foreignTable = Tca::table($foreignTableName);
711
+						$fieldNameAndPath = $fieldName . '.' . $foreignTable->getLabelField();
712
+					}
713
+
714
+					// If different means we should restore the prepended path segment for proper SQL parser.
715
+					// This is TRUE for a composite field, e.g items.sys_file_metadata for categories.
716
+					if ($fieldName !== $fieldPath) {
717
+						$fieldNameAndPath = $fieldPath . '.' . $fieldNameAndPath;
718
+					}
719
+				}
720
+
721
+				$constraints[] = $query->$operator($fieldNameAndPath, $criterion['operand']);
722
+			}
723
+
724
+			$getLogicalSeparator = sprintf('getLogicalSeparatorFor%s', $operatorName);
725
+			$logical = $matcher->$getLogicalSeparator();
726
+			$result = $query->$logical($constraints);
727
+		}
728
+
729
+		return $result;
730
+	}
731
+
732
+	/**
733
+	 * @return \TYPO3\CMS\Core\DataHandling\DataHandler
734
+	 */
735
+	protected function getDataHandler()
736
+	{
737
+		if (!$this->dataHandler) {
738
+			$this->dataHandler = GeneralUtility::makeInstance('TYPO3\CMS\Core\DataHandling\DataHandler');
739
+		}
740
+		return $this->dataHandler;
741
+	}
742
+
743
+	/**
744
+	 * Handle the magic call by properly creating a Query object and returning its result.
745
+	 *
746
+	 * @param string $propertyName
747
+	 * @param string $value
748
+	 * @param string $flag
749
+	 * @return array
750
+	 */
751
+	protected function processMagicCall($propertyName, $value, $flag = '')
752
+	{
753
+
754
+		$fieldName = Property::name($propertyName)->of($this->dataType)->toFieldName();
755
+
756
+		/** @var $matcher Matcher */
757
+		$matcher = GeneralUtility::makeInstance('Fab\Vidi\Persistence\Matcher', array(), $this->getDataType());
758
+
759
+		$table = Tca::table($this->dataType);
760
+		if ($table->field($fieldName)->isGroup()) {
761
+
762
+			$valueParts = explode('.', $value, 2);
763
+			$fieldName = $fieldName . '.' . $valueParts[0];
764
+			$value = $valueParts[1];
765
+		}
766
+
767
+		$matcher->equals($fieldName, $value);
768
+
769
+		if ($flag == 'count') {
770
+			$result = $this->countBy($matcher);
771
+		} else {
772
+			$result = $this->findBy($matcher);
773
+		}
774
+		return $flag == 'one' && !empty($result) ? reset($result) : $result;
775
+	}
776
+
777
+	/**
778
+	 * @return \Fab\Vidi\DataHandler\DataHandlerFactory
779
+	 */
780
+	protected function getDataHandlerFactory()
781
+	{
782
+		return GeneralUtility::makeInstance('Fab\Vidi\DataHandler\DataHandlerFactory');
783
+	}
784
+
785
+	/**
786
+	 * Returns whether the current mode is Backend
787
+	 *
788
+	 * @return bool
789
+	 */
790
+	protected function isBackendMode()
791
+	{
792
+		return TYPO3_MODE == 'BE';
793
+	}
794
+
795
+	/**
796
+	 * @return \Fab\Vidi\Resolver\FieldPathResolver
797
+	 */
798
+	protected function getFieldPathResolver()
799
+	{
800
+		return GeneralUtility::makeInstance('Fab\Vidi\Resolver\FieldPathResolver');
801
+	}
802
+
803
+	/**
804
+	 * @return \TYPO3\CMS\Extbase\Object\ObjectManager
805
+	 */
806
+	protected function getObjectManager()
807
+	{
808
+		return GeneralUtility::makeInstance('TYPO3\CMS\Extbase\Object\ObjectManager');
809
+	}
810
+
811
+	/**
812
+	 * @return \Fab\Vidi\Domain\Validator\ContentValidator
813
+	 */
814
+	protected function getContentValidator()
815
+	{
816
+		return GeneralUtility::makeInstance('Fab\Vidi\Domain\Validator\ContentValidator');
817
+	}
818
+
819
+	/**
820
+	 * @return \Fab\Vidi\Domain\Validator\LanguageValidator
821
+	 */
822
+	protected function getLanguageValidator()
823
+	{
824
+		return GeneralUtility::makeInstance('Fab\Vidi\Domain\Validator\LanguageValidator');
825
+	}
826
+
827
+	/**
828
+	 * Signal that is called for post-processing the computed constraints object.
829
+	 *
830
+	 * @param Query $query
831
+	 * @param ConstraintInterface|NULL $constraints
832
+	 * @return ConstraintInterface|NULL $constraints
833
+	 * @signal
834
+	 */
835
+	protected function emitPostProcessConstraintsSignal(Query $query, $constraints)
836
+	{
837
+		$result = $this->getSignalSlotDispatcher()->dispatch(
838
+			'Fab\Vidi\Domain\Repository\ContentRepository',
839
+			'postProcessConstraintsObject',
840
+			array(
841
+				$query,
842
+				$constraints
843
+			)
844
+		);
845
+
846
+		return $result[1];
847
+	}
848
+
849
+	/**
850
+	 * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
851
+	 */
852
+	protected function getSignalSlotDispatcher()
853
+	{
854
+		return $this->getObjectManager()->get('TYPO3\\CMS\\Extbase\\SignalSlot\\Dispatcher');
855
+	}
856 856
 
857 857
 }
Please login to merge, or discard this patch.
Classes/Grid/GridAnalyserService.php 2 patches
Doc Comments   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -81,7 +81,7 @@  discard block
 block discarded – undo
81 81
      *
82 82
      * @param string $tableName
83 83
      * @param string $fieldName
84
-     * @return array
84
+     * @return string[]
85 85
      */
86 86
     protected function checkRelationManyToMany($tableName, $fieldName)
87 87
     {
@@ -115,7 +115,7 @@  discard block
 block discarded – undo
115 115
      * @param string $tableName
116 116
      * @param string $fieldName
117 117
      * @param string $relationType
118
-     * @return array
118
+     * @return string[]
119 119
      */
120 120
     protected function checkRelationOf($tableName, $fieldName, $relationType)
121 121
     {
Please login to merge, or discard this patch.
Indentation   +102 added lines, -102 removed lines patch added patch discarded remove patch
@@ -22,114 +22,114 @@
 block discarded – undo
22 22
 class GridAnalyserService
23 23
 {
24 24
 
25
-    /**
26
-     * Check relation for table.
27
-     *
28
-     * @param $tableName
29
-     * @return array
30
-     */
31
-    public function checkRelationForTable($tableName)
32
-    {
25
+	/**
26
+	 * Check relation for table.
27
+	 *
28
+	 * @param $tableName
29
+	 * @return array
30
+	 */
31
+	public function checkRelationForTable($tableName)
32
+	{
33 33
 
34
-        $relations = array();
35
-        $table = Tca::table($tableName);
34
+		$relations = array();
35
+		$table = Tca::table($tableName);
36 36
 
37
-        $missingOppositionRelationMessage = <<<EOF
37
+		$missingOppositionRelationMessage = <<<EOF
38 38
 
39 39
   WARNING! Could not define relation precisely. This is not necessarily a problem
40 40
   if the opposite relation is not required in a Grid. But consider adding the opposite
41 41
   TCA configuration if so.
42 42
 EOF;
43 43
 
44
-        foreach (Tca::grid($tableName)->getFields() as $fieldName => $configuration) {
45
-
46
-            if ($table->hasField($fieldName)) {
47
-                if ($table->field($fieldName)->hasMany()) {
48
-                    if ($table->field($fieldName)->hasRelationWithCommaSeparatedValues()) {
49
-                        $_relations = $this->checkRelationOf($tableName, $fieldName, 'comma separated values');
50
-                        $relations = array_merge($relations, $_relations);
51
-                    } elseif ($table->field($fieldName)->hasRelationManyToMany()) {
52
-                        $_relations = $this->checkRelationManyToMany($tableName, $fieldName);
53
-                        $relations = array_merge($relations, $_relations);
54
-
55
-                    } elseif ($table->field($fieldName)->hasRelationOneToMany()) {
56
-                        $_relations = $this->checkRelationOf($tableName, $fieldName, 'one-to-many');
57
-                        $relations = array_merge($relations, $_relations);
58
-                    } else {
59
-                        $relations[] = sprintf('* field: "%s", relation: ?-to-many%s', $fieldName, $missingOppositionRelationMessage);
60
-                    }
61
-                    $relations[] = '';
62
-                } elseif ($table->field($fieldName)->hasOne()) {
63
-
64
-                    if ($table->field($fieldName)->hasRelationOneToOne()) {
65
-                        $relations[] = sprintf('* one-to-one "%s"', $fieldName);
66
-                    } elseif ($table->field($fieldName)->hasRelationManyToOne()) {
67
-                        $_relations = $this->checkRelationOf($tableName, $fieldName, 'many-to-one');
68
-                        $relations = array_merge($relations, $_relations);
69
-                    } else {
70
-                        $relations[] = sprintf('* field: "%s", relation: ?-to-one%s', $fieldName, $missingOppositionRelationMessage);
71
-                    }
72
-                    $relations[] = '';
73
-                }
74
-            }
75
-        }
76
-        return $relations;
77
-    }
78
-
79
-    /**
80
-     * Convenience method for printing out relation many-to-many.
81
-     *
82
-     * @param string $tableName
83
-     * @param string $fieldName
84
-     * @return array
85
-     */
86
-    protected function checkRelationManyToMany($tableName, $fieldName)
87
-    {
88
-
89
-        $output = array();
90
-
91
-        $table = Tca::table($tableName);
92
-        $output[] = sprintf('* field: "%s", relation: many-to-many', $fieldName);
93
-
94
-        $foreignTable = $table->field($fieldName)->getForeignTable();
95
-        $manyToManyTable = $table->field($fieldName)->getManyToManyTable();
96
-        $foreignField = $table->field($fieldName)->getForeignField();
97
-
98
-        if (!$foreignField) {
99
-            $output[] = sprintf('  ERROR! Can not found foreign field for "%s". Perhaps missing opposite configuration?', $fieldName);
100
-        } elseif (!$foreignTable) {
101
-            $output[] = sprintf('  ERROR! Can not found foreign table for "%s". Perhaps missing opposite configuration?', $fieldName);
102
-        } elseif (!$manyToManyTable) {
103
-            $output[] = sprintf('  ERROR! Can not found relation table (MM) for "%s". Perhaps missing opposite configuration?', $fieldName);
104
-        } else {
105
-            $output[] = sprintf('  %s.%s <--> %s <--> %s.%s', $tableName, $fieldName, $manyToManyTable, $foreignTable, $foreignField);
106
-        }
107
-
108
-        $output[] = '';
109
-        return $output;
110
-    }
111
-
112
-    /**
113
-     * Convenience method for printing out relation.
114
-     *
115
-     * @param string $tableName
116
-     * @param string $fieldName
117
-     * @param string $relationType
118
-     * @return array
119
-     */
120
-    protected function checkRelationOf($tableName, $fieldName, $relationType)
121
-    {
122
-
123
-        $output = array();
124
-
125
-        $table = Tca::table($tableName);
126
-        $output[] = sprintf('* field: "%s", relation: %s', $fieldName, $relationType);
127
-
128
-        $foreignTable = $table->field($fieldName)->getForeignTable();
129
-        $foreignField = $table->field($fieldName)->getForeignField();
130
-        $output[] = sprintf('  %s.%s <--> %s.%s', $tableName, $fieldName, $foreignTable, $foreignField);
131
-        $output[] = '';
132
-
133
-        return $output;
134
-    }
44
+		foreach (Tca::grid($tableName)->getFields() as $fieldName => $configuration) {
45
+
46
+			if ($table->hasField($fieldName)) {
47
+				if ($table->field($fieldName)->hasMany()) {
48
+					if ($table->field($fieldName)->hasRelationWithCommaSeparatedValues()) {
49
+						$_relations = $this->checkRelationOf($tableName, $fieldName, 'comma separated values');
50
+						$relations = array_merge($relations, $_relations);
51
+					} elseif ($table->field($fieldName)->hasRelationManyToMany()) {
52
+						$_relations = $this->checkRelationManyToMany($tableName, $fieldName);
53
+						$relations = array_merge($relations, $_relations);
54
+
55
+					} elseif ($table->field($fieldName)->hasRelationOneToMany()) {
56
+						$_relations = $this->checkRelationOf($tableName, $fieldName, 'one-to-many');
57
+						$relations = array_merge($relations, $_relations);
58
+					} else {
59
+						$relations[] = sprintf('* field: "%s", relation: ?-to-many%s', $fieldName, $missingOppositionRelationMessage);
60
+					}
61
+					$relations[] = '';
62
+				} elseif ($table->field($fieldName)->hasOne()) {
63
+
64
+					if ($table->field($fieldName)->hasRelationOneToOne()) {
65
+						$relations[] = sprintf('* one-to-one "%s"', $fieldName);
66
+					} elseif ($table->field($fieldName)->hasRelationManyToOne()) {
67
+						$_relations = $this->checkRelationOf($tableName, $fieldName, 'many-to-one');
68
+						$relations = array_merge($relations, $_relations);
69
+					} else {
70
+						$relations[] = sprintf('* field: "%s", relation: ?-to-one%s', $fieldName, $missingOppositionRelationMessage);
71
+					}
72
+					$relations[] = '';
73
+				}
74
+			}
75
+		}
76
+		return $relations;
77
+	}
78
+
79
+	/**
80
+	 * Convenience method for printing out relation many-to-many.
81
+	 *
82
+	 * @param string $tableName
83
+	 * @param string $fieldName
84
+	 * @return array
85
+	 */
86
+	protected function checkRelationManyToMany($tableName, $fieldName)
87
+	{
88
+
89
+		$output = array();
90
+
91
+		$table = Tca::table($tableName);
92
+		$output[] = sprintf('* field: "%s", relation: many-to-many', $fieldName);
93
+
94
+		$foreignTable = $table->field($fieldName)->getForeignTable();
95
+		$manyToManyTable = $table->field($fieldName)->getManyToManyTable();
96
+		$foreignField = $table->field($fieldName)->getForeignField();
97
+
98
+		if (!$foreignField) {
99
+			$output[] = sprintf('  ERROR! Can not found foreign field for "%s". Perhaps missing opposite configuration?', $fieldName);
100
+		} elseif (!$foreignTable) {
101
+			$output[] = sprintf('  ERROR! Can not found foreign table for "%s". Perhaps missing opposite configuration?', $fieldName);
102
+		} elseif (!$manyToManyTable) {
103
+			$output[] = sprintf('  ERROR! Can not found relation table (MM) for "%s". Perhaps missing opposite configuration?', $fieldName);
104
+		} else {
105
+			$output[] = sprintf('  %s.%s <--> %s <--> %s.%s', $tableName, $fieldName, $manyToManyTable, $foreignTable, $foreignField);
106
+		}
107
+
108
+		$output[] = '';
109
+		return $output;
110
+	}
111
+
112
+	/**
113
+	 * Convenience method for printing out relation.
114
+	 *
115
+	 * @param string $tableName
116
+	 * @param string $fieldName
117
+	 * @param string $relationType
118
+	 * @return array
119
+	 */
120
+	protected function checkRelationOf($tableName, $fieldName, $relationType)
121
+	{
122
+
123
+		$output = array();
124
+
125
+		$table = Tca::table($tableName);
126
+		$output[] = sprintf('* field: "%s", relation: %s', $fieldName, $relationType);
127
+
128
+		$foreignTable = $table->field($fieldName)->getForeignTable();
129
+		$foreignField = $table->field($fieldName)->getForeignField();
130
+		$output[] = sprintf('  %s.%s <--> %s.%s', $tableName, $fieldName, $foreignTable, $foreignField);
131
+		$output[] = '';
132
+
133
+		return $output;
134
+	}
135 135
 }
Please login to merge, or discard this patch.
Classes/Language/LanguageService.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -109,7 +109,7 @@
 block discarded – undo
109 109
      *
110 110
      * @param Content $object
111 111
      * @param int $language
112
-     * @return string
112
+     * @return boolean
113 113
      */
114 114
     public function hasLocalization(Content $object, $language)
115 115
     {
Please login to merge, or discard this patch.
Indentation   +173 added lines, -173 removed lines patch added patch discarded remove patch
@@ -25,178 +25,178 @@
 block discarded – undo
25 25
 class LanguageService implements SingletonInterface
26 26
 {
27 27
 
28
-    /**
29
-     * @var array
30
-     */
31
-    protected $languages;
32
-
33
-    /**
34
-     * @var array
35
-     */
36
-    protected $defaultIcon;
37
-
38
-    /**
39
-     * Store the localized records to boost up performance.
40
-     *
41
-     * @var array
42
-     */
43
-    protected $localizedRecordStorage;
44
-
45
-    /**
46
-     * Returns available language records.
47
-     * The method stores the records in the property to speed up the process as the method can be often called.
48
-     *
49
-     * @return array
50
-     */
51
-    public function getLanguages()
52
-    {
53
-        if (is_null($this->languages)) {
54
-
55
-            $tableName = 'sys_language';
56
-
57
-            $clause = '1 = 1';
58
-            $clause .= BackendUtility::deleteClause($tableName);
59
-            $clause .= BackendUtility::BEenableFields($tableName);
60
-            $this->languages = $this->getDatabaseConnection()->exec_SELECTgetRows('*', $tableName, $clause);
61
-        }
62
-        return $this->languages;
63
-    }
64
-
65
-    /**
66
-     * Returns a localized record according to a Content object and a language identifier.
67
-     * Notice! This method does not overlay anything but simply returns the raw localized record.
68
-     *
69
-     * @param Content $object
70
-     * @param int $language
71
-     * @return Content
72
-     */
73
-    public function getLocalizedContent(Content $object, $language)
74
-    {
75
-
76
-        // We want to cache data per Content object. Retrieve the Object hash.
77
-        $objectHash = spl_object_hash($object);
78
-
79
-        // Initialize the storage
80
-        if (empty($this->localizedRecordStorage[$objectHash])) {
81
-            $this->localizedRecordStorage[$objectHash] = array();
82
-        }
83
-
84
-        if (empty($this->localizedRecordStorage[$objectHash][$language])) {
85
-
86
-            $clause = sprintf('%s = %s AND %s = %s',
87
-                Tca::table($object)->getLanguageParentField(), // e.g. l10n_parent
88
-                $object->getUid(),
89
-                Tca::table($object)->getLanguageField(), // e.g. sys_language_uid
90
-                $language
91
-            );
92
-
93
-            $clause .= BackendUtility::deleteClause($object->getDataType());
94
-            $localizedRecord = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('*', $object->getDataType(), $clause);
95
-
96
-            if ($localizedRecord) {
97
-                $localizedContent = GeneralUtility::makeInstance('Fab\Vidi\Domain\Model\Content', $object->getDataType(), $localizedRecord);
98
-                $this->localizedRecordStorage[$objectHash][$language] = $localizedContent;
99
-            } else {
100
-                $this->localizedRecordStorage[$objectHash][$language] = array(); // We want an array at least, even if empty.
101
-            }
102
-        }
103
-
104
-        return $this->localizedRecordStorage[$objectHash][$language];
105
-    }
106
-
107
-    /**
108
-     * Tell whether the given Content object has a localization.
109
-     *
110
-     * @param Content $object
111
-     * @param int $language
112
-     * @return string
113
-     */
114
-    public function hasLocalization(Content $object, $language)
115
-    {
116
-        $localizedRecord = $this->getLocalizedContent($object, $language);
117
-        return !empty($localizedRecord);
118
-    }
119
-
120
-    /**
121
-     * Returns a localized field according to a Content object and a language identifier.
122
-     * Notice! If there is not translation, simply returns an empty string.
123
-     *
124
-     * @param Content $object
125
-     * @param int $language
126
-     * @param string $fieldName
127
-     * @return string
128
-     */
129
-    public function getLocalizedFieldName(Content $object, $language, $fieldName)
130
-    {
131
-        $localizedRecord = $this->getLocalizedContent($object, $language);
132
-        return empty($localizedRecord) ? '' : $localizedRecord[$fieldName];
133
-    }
134
-
135
-    /**
136
-     * Returns the default language configured by TSConfig.
137
-     *
138
-     * @return array
139
-     */
140
-    public function getDefaultFlag()
141
-    {
142
-
143
-        if (is_null($this->defaultIcon)) {
144
-
145
-            $defaultFlag = ''; // default value
146
-
147
-            $tsConfig = BackendUtility::getModTSconfig(0, 'mod.SHARED');
148
-
149
-            // Fallback non sprite-configuration
150
-            if (($pos = strrpos($tsConfig['properties']['defaultLanguageFlag'], '.')) !== FALSE) {
151
-                $defaultFlag = substr($tsConfig['properties']['defaultLanguageFlag'], 0, $pos);
152
-            }
153
-
154
-            $this->defaultIcon = $defaultFlag;
155
-        }
156
-
157
-        return $this->defaultIcon;
158
-    }
159
-
160
-    /**
161
-     * Returns whether the system includes language records.
162
-     *
163
-     * @return bool
164
-     */
165
-    public function hasLanguages()
166
-    {
167
-        $languages = $this->getLanguages();
168
-        return !empty($languages);
169
-    }
170
-
171
-    /**
172
-     * Tell whether the given language exists.
173
-     *
174
-     * @param int $language
175
-     * @return bool
176
-     */
177
-    public function languageExists($language)
178
-    {
179
-        $languages = $this->getLanguages();
180
-
181
-        $LanguageExists = FALSE;
182
-        foreach ($languages as $_language) {
183
-            if ((int)$_language['uid'] === (int)$language) {
184
-                $LanguageExists = TRUE;
185
-                break;
186
-            }
187
-        }
188
-
189
-        return $LanguageExists;
190
-    }
191
-
192
-    /**
193
-     * Returns a pointer to the database.
194
-     *
195
-     * @return \TYPO3\CMS\Core\Database\DatabaseConnection
196
-     */
197
-    protected function getDatabaseConnection()
198
-    {
199
-        return $GLOBALS['TYPO3_DB'];
200
-    }
28
+	/**
29
+	 * @var array
30
+	 */
31
+	protected $languages;
32
+
33
+	/**
34
+	 * @var array
35
+	 */
36
+	protected $defaultIcon;
37
+
38
+	/**
39
+	 * Store the localized records to boost up performance.
40
+	 *
41
+	 * @var array
42
+	 */
43
+	protected $localizedRecordStorage;
44
+
45
+	/**
46
+	 * Returns available language records.
47
+	 * The method stores the records in the property to speed up the process as the method can be often called.
48
+	 *
49
+	 * @return array
50
+	 */
51
+	public function getLanguages()
52
+	{
53
+		if (is_null($this->languages)) {
54
+
55
+			$tableName = 'sys_language';
56
+
57
+			$clause = '1 = 1';
58
+			$clause .= BackendUtility::deleteClause($tableName);
59
+			$clause .= BackendUtility::BEenableFields($tableName);
60
+			$this->languages = $this->getDatabaseConnection()->exec_SELECTgetRows('*', $tableName, $clause);
61
+		}
62
+		return $this->languages;
63
+	}
64
+
65
+	/**
66
+	 * Returns a localized record according to a Content object and a language identifier.
67
+	 * Notice! This method does not overlay anything but simply returns the raw localized record.
68
+	 *
69
+	 * @param Content $object
70
+	 * @param int $language
71
+	 * @return Content
72
+	 */
73
+	public function getLocalizedContent(Content $object, $language)
74
+	{
75
+
76
+		// We want to cache data per Content object. Retrieve the Object hash.
77
+		$objectHash = spl_object_hash($object);
78
+
79
+		// Initialize the storage
80
+		if (empty($this->localizedRecordStorage[$objectHash])) {
81
+			$this->localizedRecordStorage[$objectHash] = array();
82
+		}
83
+
84
+		if (empty($this->localizedRecordStorage[$objectHash][$language])) {
85
+
86
+			$clause = sprintf('%s = %s AND %s = %s',
87
+				Tca::table($object)->getLanguageParentField(), // e.g. l10n_parent
88
+				$object->getUid(),
89
+				Tca::table($object)->getLanguageField(), // e.g. sys_language_uid
90
+				$language
91
+			);
92
+
93
+			$clause .= BackendUtility::deleteClause($object->getDataType());
94
+			$localizedRecord = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('*', $object->getDataType(), $clause);
95
+
96
+			if ($localizedRecord) {
97
+				$localizedContent = GeneralUtility::makeInstance('Fab\Vidi\Domain\Model\Content', $object->getDataType(), $localizedRecord);
98
+				$this->localizedRecordStorage[$objectHash][$language] = $localizedContent;
99
+			} else {
100
+				$this->localizedRecordStorage[$objectHash][$language] = array(); // We want an array at least, even if empty.
101
+			}
102
+		}
103
+
104
+		return $this->localizedRecordStorage[$objectHash][$language];
105
+	}
106
+
107
+	/**
108
+	 * Tell whether the given Content object has a localization.
109
+	 *
110
+	 * @param Content $object
111
+	 * @param int $language
112
+	 * @return string
113
+	 */
114
+	public function hasLocalization(Content $object, $language)
115
+	{
116
+		$localizedRecord = $this->getLocalizedContent($object, $language);
117
+		return !empty($localizedRecord);
118
+	}
119
+
120
+	/**
121
+	 * Returns a localized field according to a Content object and a language identifier.
122
+	 * Notice! If there is not translation, simply returns an empty string.
123
+	 *
124
+	 * @param Content $object
125
+	 * @param int $language
126
+	 * @param string $fieldName
127
+	 * @return string
128
+	 */
129
+	public function getLocalizedFieldName(Content $object, $language, $fieldName)
130
+	{
131
+		$localizedRecord = $this->getLocalizedContent($object, $language);
132
+		return empty($localizedRecord) ? '' : $localizedRecord[$fieldName];
133
+	}
134
+
135
+	/**
136
+	 * Returns the default language configured by TSConfig.
137
+	 *
138
+	 * @return array
139
+	 */
140
+	public function getDefaultFlag()
141
+	{
142
+
143
+		if (is_null($this->defaultIcon)) {
144
+
145
+			$defaultFlag = ''; // default value
146
+
147
+			$tsConfig = BackendUtility::getModTSconfig(0, 'mod.SHARED');
148
+
149
+			// Fallback non sprite-configuration
150
+			if (($pos = strrpos($tsConfig['properties']['defaultLanguageFlag'], '.')) !== FALSE) {
151
+				$defaultFlag = substr($tsConfig['properties']['defaultLanguageFlag'], 0, $pos);
152
+			}
153
+
154
+			$this->defaultIcon = $defaultFlag;
155
+		}
156
+
157
+		return $this->defaultIcon;
158
+	}
159
+
160
+	/**
161
+	 * Returns whether the system includes language records.
162
+	 *
163
+	 * @return bool
164
+	 */
165
+	public function hasLanguages()
166
+	{
167
+		$languages = $this->getLanguages();
168
+		return !empty($languages);
169
+	}
170
+
171
+	/**
172
+	 * Tell whether the given language exists.
173
+	 *
174
+	 * @param int $language
175
+	 * @return bool
176
+	 */
177
+	public function languageExists($language)
178
+	{
179
+		$languages = $this->getLanguages();
180
+
181
+		$LanguageExists = FALSE;
182
+		foreach ($languages as $_language) {
183
+			if ((int)$_language['uid'] === (int)$language) {
184
+				$LanguageExists = TRUE;
185
+				break;
186
+			}
187
+		}
188
+
189
+		return $LanguageExists;
190
+	}
191
+
192
+	/**
193
+	 * Returns a pointer to the database.
194
+	 *
195
+	 * @return \TYPO3\CMS\Core\Database\DatabaseConnection
196
+	 */
197
+	protected function getDatabaseConnection()
198
+	{
199
+		return $GLOBALS['TYPO3_DB'];
200
+	}
201 201
 
202 202
 }
Please login to merge, or discard this patch.
Classes/Module/ModuleLoader.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -242,7 +242,7 @@
 block discarded – undo
242 242
     /**
243 243
      * Returns the current pid.
244 244
      *
245
-     * @return bool
245
+     * @return integer
246 246
      */
247 247
     public function getCurrentPid()
248 248
     {
Please login to merge, or discard this patch.
Indentation   +895 added lines, -895 removed lines patch added patch discarded remove patch
@@ -26,900 +26,900 @@
 block discarded – undo
26 26
 class ModuleLoader
27 27
 {
28 28
 
29
-    /**
30
-     * Define the default main module
31
-     */
32
-    const DEFAULT_MAIN_MODULE = 'content';
33
-
34
-    /**
35
-     * Define the default pid
36
-     */
37
-    const DEFAULT_PID = 0;
38
-
39
-    /**
40
-     * The type of data being listed (which corresponds to a table name in TCA)
41
-     *
42
-     * @var string
43
-     */
44
-    protected $dataType;
45
-
46
-    /**
47
-     * @var string
48
-     */
49
-    protected $defaultPid;
50
-
51
-    /**
52
-     * @var bool
53
-     */
54
-    protected $showPageTree;
55
-
56
-    /**
57
-     * @var bool
58
-     */
59
-    protected $isShown = TRUE;
60
-
61
-    /**
62
-     * @var string
63
-     */
64
-    protected $access = Access::USER;
65
-
66
-    /**
67
-     * @var string
68
-     */
69
-    protected $mainModule;
70
-
71
-    /**
72
-     * @var string
73
-     */
74
-    protected $position = '';
75
-
76
-    /**
77
-     * @var string
78
-     */
79
-    protected $icon = '';
80
-
81
-    /**
82
-     * @var string
83
-     */
84
-    protected $moduleLanguageFile = 'LLL:EXT:vidi/Resources/Private/Language/locallang_module.xlf';
85
-
86
-    /**
87
-     * The module key such as m1, m2.
88
-     *
89
-     * @var string
90
-     */
91
-    protected $moduleKey = 'm1';
92
-
93
-    /**
94
-     * @var string[]
95
-     */
96
-    protected $additionalJavaScriptFiles = array();
97
-
98
-    /**
99
-     * @var string[]
100
-     */
101
-    protected $additionalStyleSheetFiles = array();
102
-
103
-    /**
104
-     * @var array
105
-     */
106
-    protected $components = array(
107
-        ModulePosition::DOC_HEADER => array(
108
-            ModulePosition::TOP => array(
109
-                ModulePosition::LEFT => array(),
110
-                ModulePosition::RIGHT => array(
111
-                    'Fab\Vidi\View\Button\ToolButton',
112
-                ),
113
-            ),
114
-            ModulePosition::BOTTOM => array(
115
-                ModulePosition::LEFT => array(
116
-                    'Fab\Vidi\View\Button\NewButton',
117
-                    'Fab\Vidi\ViewHelpers\Link\BackViewHelper',
118
-                ),
119
-                ModulePosition::RIGHT => array(),
120
-            ),
121
-        ),
122
-        ModulePosition::GRID => array(
123
-            ModulePosition::TOP => array(
124
-                'Fab\Vidi\View\Check\PidCheck',
125
-                'Fab\Vidi\View\Check\RelationsCheck',
126
-                'Fab\Vidi\View\Tab\DataTypeTab',
127
-            ),
128
-            ModulePosition::BUTTONS => array(
129
-                'Fab\Vidi\View\Button\EditButton',
130
-                'Fab\Vidi\View\Button\DeleteButton',
131
-            ),
132
-            ModulePosition::BOTTOM => array(),
133
-        ),
134
-        ModulePosition::MENU_MASS_ACTION => array(
135
-            'Fab\Vidi\View\MenuItem\ExportXlsMenuItem',
136
-            'Fab\Vidi\View\MenuItem\ExportXmlMenuItem',
137
-            'Fab\Vidi\View\MenuItem\ExportCsvMenuItem',
138
-            'Fab\Vidi\View\MenuItem\DividerMenuItem',
139
-            'Fab\Vidi\View\MenuItem\MassDeleteMenuItem',
140
-            #'Fab\Vidi\View\MenuItem\MassEditMenuItem',
141
-        ),
142
-    );
143
-
144
-    /**
145
-     * @param string $dataType
146
-     */
147
-    public function __construct($dataType = NULL)
148
-    {
149
-        $this->dataType = $dataType;
150
-    }
151
-
152
-    /**
153
-     * Tell whether a module is already registered.
154
-     *
155
-     * @param string $dataType
156
-     * @return bool
157
-     */
158
-    public function isRegistered($dataType)
159
-    {
160
-        $internalModuleSignature = $this->getInternalModuleSignature($dataType);
161
-        return !empty($GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature]);
162
-    }
163
-
164
-    /**
165
-     * Register the module
166
-     *
167
-     * @return void
168
-     */
169
-    public function register()
170
-    {
171
-
172
-        $this->initializeDefaultValues();
173
-        $internalModuleSignature = $this->getInternalModuleSignature();
174
-
175
-        $GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature] = array();
176
-        $GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature]['dataType'] = $this->dataType;
177
-        $GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature]['mainModule'] = $this->mainModule;
178
-        $GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature]['defaultPid'] = $this->defaultPid;
179
-        $GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature]['additionalJavaScriptFiles'] = $this->additionalJavaScriptFiles;
180
-        $GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature]['additionalStyleSheetFiles'] = $this->additionalStyleSheetFiles;
181
-        $GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature]['components'] = $this->components;
182
-
183
-        // Register and displays module in the BE only if told, default is TRUE.
184
-        if ($this->isShown) {
185
-            $moduleConfiguration = array(
186
-                #'routeTarget' => \Fab\Vidi\Controller\Backend\ContentController::class . '::mainAction', // what to do here?
187
-                'access' => $this->access,
188
-                'labels' => $this->moduleLanguageFile,
189
-                'inheritNavigationComponentFromMainModule' => TRUE
190
-            );
191
-
192
-            if (!empty($this->icon)) {
193
-                $moduleConfiguration['icon'] = $this->icon;
194
-            }
195
-
196
-            if (!is_null($this->showPageTree)) {
197
-                if ($this->showPageTree) {
198
-                    $moduleConfiguration['navigationComponentId'] = 'typo3-pagetree';
199
-                } else {
200
-                    $moduleConfiguration['inheritNavigationComponentFromMainModule'] = FALSE;
201
-                }
202
-            }
203
-
204
-            ExtensionUtility::registerModule(
205
-                'Fab.vidi',
206
-                $this->mainModule,
207
-                $this->dataType . '_' . $this->moduleKey,
208
-                $this->position,
209
-                array(
210
-                    'Content' => 'index, list, delete, update, edit, copy, move, localize, sort, copyClipboard, moveClipboard',
211
-                    'Tool' => 'welcome, work',
212
-                    'Facet' => 'autoSuggest, autoSuggests',
213
-                    'Selection' => 'edit, update, create, delete, list, show',
214
-                    'UserPreferences' => 'save',
215
-                    'Clipboard' => 'save, flush, show',
216
-                ),
217
-                $moduleConfiguration
218
-            );
219
-        }
220
-    }
221
-
222
-    /**
223
-     * Return the module code for a BE module.
224
-     *
225
-     * @return string
226
-     */
227
-    public function getSignature()
228
-    {
229
-        return GeneralUtility::_GP(Parameter::MODULE);
230
-    }
231
-
232
-    /**
233
-     * Tell whether the current module is the list one.
234
-     *
235
-     * @return bool
236
-     */
237
-    public function copeWithPageTree()
238
-    {
239
-        return GeneralUtility::_GP(Parameter::MODULE) === 'web_VidiM1';
240
-    }
241
-
242
-    /**
243
-     * Returns the current pid.
244
-     *
245
-     * @return bool
246
-     */
247
-    public function getCurrentPid()
248
-    {
249
-        return GeneralUtility::_GET(Parameter::PID) > 0 ? (int)GeneralUtility::_GET(Parameter::PID) : 0;
250
-    }
251
-
252
-    /**
253
-     * Return the Vidi module code which is stored in TBE_MODULES_EXT
254
-     *
255
-     * @return string
256
-     */
257
-    public function getVidiModuleCode()
258
-    {
259
-
260
-        if ($this->copeWithPageTree()) {
261
-            $userPreferenceKey = sprintf('Vidi_pid_%s', $this->getCurrentPid());
262
-
263
-            if (GeneralUtility::_GP(Parameter::SUBMODULE)) {
264
-                $subModuleCode = GeneralUtility::_GP(Parameter::SUBMODULE);
265
-                BackendUserPreferenceService::getInstance()->set($userPreferenceKey, $subModuleCode);
266
-            } else {
267
-
268
-                $defaultModuleCode = BackendUserPreferenceService::getInstance()->get($userPreferenceKey);
269
-                if (empty($defaultModuleCode)) {
270
-                    $defaultModuleCode = 'VidiTtContentM1'; // hard-coded submodule
271
-                    BackendUserPreferenceService::getInstance()->set($userPreferenceKey, $defaultModuleCode);
272
-                }
273
-
274
-                $vidiModules = ModuleService::getInstance()->getModulesForCurrentPid();
275
-
276
-                if (empty($vidiModules)) {
277
-                    $subModuleCode = $defaultModuleCode;
278
-                } elseif (isset($vidiModules[$defaultModuleCode])) {
279
-                    $subModuleCode = $defaultModuleCode;
280
-                } else {
281
-                    $subModuleCode = ModuleService::getInstance()->getFirstModuleForPid($this->getCurrentPid());
282
-                }
283
-            }
284
-        } else {
285
-            $moduleCode = $this->getSignature();
286
-
287
-            // Remove first part which is separated "_"
288
-            $delimiter = strpos($moduleCode, '_') + 1;
289
-            $subModuleCode = substr($moduleCode, $delimiter);
290
-        }
291
-
292
-        return $subModuleCode;
293
-    }
294
-
295
-    /**
296
-     * Return the module URL.
297
-     *
298
-     * @param array $additionalParameters
299
-     * @param bool $absoluteUrl
300
-     * @return string
301
-     */
302
-    public function getModuleUrl(array $additionalParameters = array(), $absoluteUrl = false)
303
-    {
304
-        $moduleCode = $this->getSignature();
305
-
306
-        // Add possible submodule if current module has page tree.
307
-        if ($this->copeWithPageTree() && !isset($additionalParameters[Parameter::SUBMODULE])) {
308
-            $additionalParameters[Parameter::SUBMODULE] = $this->getVidiModuleCode();
309
-        }
310
-
311
-        // And don't forget the pid!
312
-        if (GeneralUtility::_GET(Parameter::PID)) {
313
-            $additionalParameters[Parameter::PID] = GeneralUtility::_GET(Parameter::PID);
314
-        }
315
-
316
-        $moduleUrl = BackendUtility::getModuleUrl($moduleCode, $additionalParameters, false, $absoluteUrl);
317
-        return $moduleUrl;
318
-    }
319
-
320
-    /**
321
-     * Return the module absolute URL.
322
-     *
323
-     * @param array $additionalParameters
324
-     * @return string
325
-     */
326
-    public function getModuleAbsoluteUrl(array $additionalParameters = array())
327
-    {
328
-        return $this->getModuleUrl($additionalParameters, true);
329
-    }
330
-
331
-    /**
332
-     * Return the parameter prefix for a BE module.
333
-     *
334
-     * @return string
335
-     */
336
-    public function getParameterPrefix()
337
-    {
338
-        return 'tx_vidi_' . strtolower($this->getSignature());
339
-    }
340
-
341
-    /**
342
-     * Return a configuration key or the entire module configuration array if not key is given.
343
-     *
344
-     * @param string $key
345
-     * @throws InvalidKeyInArrayException
346
-     * @return mixed
347
-     */
348
-    public function getModuleConfiguration($key = '')
349
-    {
350
-
351
-        $vidiModuleCode = $this->getVidiModuleCode();
352
-
353
-        // Module code must exist
354
-        if (empty($GLOBALS['TBE_MODULES_EXT']['vidi'][$vidiModuleCode])) {
355
-            $message = sprintf('Invalid or not existing module code "%s"', $vidiModuleCode);
356
-            throw new InvalidKeyInArrayException($message, 1375092053);
357
-        }
358
-
359
-        $result = $GLOBALS['TBE_MODULES_EXT']['vidi'][$vidiModuleCode];
360
-
361
-        if (!empty($key)) {
362
-            if (isset($result[$key])) {
363
-                $result = $result[$key];
364
-            } else {
365
-                // key must exist
366
-                $message = sprintf('Invalid key configuration "%s"', $key);
367
-                throw new InvalidKeyInArrayException($message, 1375092054);
368
-            }
369
-        }
370
-        return $result;
371
-    }
372
-
373
-    /**
374
-     * @param string $icon
375
-     * @return $this
376
-     */
377
-    public function setIcon($icon)
378
-    {
379
-        $this->icon = $icon;
380
-        return $this;
381
-    }
382
-
383
-    /**
384
-     * @return string
385
-     */
386
-    public function getIcon()
387
-    {
388
-        return $this->icon;
389
-    }
390
-
391
-    /**
392
-     * @param string $mainModule
393
-     * @return $this
394
-     */
395
-    public function setMainModule($mainModule)
396
-    {
397
-        $this->mainModule = $mainModule;
398
-        return $this;
399
-    }
400
-
401
-    /**
402
-     * @return string
403
-     */
404
-    public function getMainModule()
405
-    {
406
-        if (is_null($this->mainModule)) {
407
-            $this->mainModule = $this->getModuleConfiguration('mainModule');
408
-        }
409
-        return $this->mainModule;
410
-    }
411
-
412
-    /**
413
-     * @param string $moduleLanguageFile
414
-     * @return $this
415
-     */
416
-    public function setModuleLanguageFile($moduleLanguageFile)
417
-    {
418
-        $this->moduleLanguageFile = $moduleLanguageFile;
419
-        return $this;
420
-    }
421
-
422
-    /**
423
-     * @return string
424
-     */
425
-    public function getModuleLanguageFile()
426
-    {
427
-        return $this->moduleLanguageFile;
428
-    }
429
-
430
-    /**
431
-     * @param string $position
432
-     * @return $this
433
-     */
434
-    public function setPosition($position)
435
-    {
436
-        $this->position = $position;
437
-        return $this;
438
-    }
439
-
440
-    /**
441
-     * @return string
442
-     */
443
-    public function getPosition()
444
-    {
445
-        return $this->position;
446
-    }
447
-
448
-    /**
449
-     * @param array $files
450
-     * @return $this
451
-     */
452
-    public function addJavaScriptFiles(array $files)
453
-    {
454
-        foreach ($files as $file) {
455
-            $this->additionalJavaScriptFiles[] = $file;
456
-        }
457
-        return $this;
458
-    }
459
-
460
-    /**
461
-     * @param string $fileNameAndPath
462
-     * @return $this
463
-     */
464
-    public function addJavaScriptFile($fileNameAndPath)
465
-    {
466
-        $this->additionalJavaScriptFiles[] = $fileNameAndPath;
467
-        return $this;
468
-    }
469
-
470
-    /**
471
-     * @param array $files
472
-     * @return $this
473
-     */
474
-    public function addStyleSheetFiles(array $files)
475
-    {
476
-        foreach ($files as $file) {
477
-            $this->additionalStyleSheetFiles[] = $file;
478
-        }
479
-        return $this;
480
-    }
481
-
482
-    /**
483
-     * @param string $fileNameAndPath
484
-     * @return $this
485
-     */
486
-    public function addStyleSheetFile($fileNameAndPath)
487
-    {
488
-        $this->additionalStyleSheetFiles[] = $fileNameAndPath;
489
-        return $this;
490
-    }
491
-
492
-    /**
493
-     * @return string
494
-     */
495
-    public function getDataType()
496
-    {
497
-        if (is_null($this->dataType)) {
498
-            $this->dataType = $this->getModuleConfiguration('dataType');
499
-        }
500
-        return $this->dataType;
501
-    }
502
-
503
-    /**
504
-     * @return array
505
-     */
506
-    public function getDataTypes()
507
-    {
508
-        $dataTypes = array();
509
-        foreach ($GLOBALS['TBE_MODULES_EXT']['vidi'] as $module) {
510
-            $dataTypes[] = $module['dataType'];
511
-        }
512
-        return $dataTypes;
513
-    }
514
-
515
-    /**
516
-     * @param string $dataType
517
-     * @return $this
518
-     */
519
-    public function setDataType($dataType)
520
-    {
521
-        $this->dataType = $dataType;
522
-        return $this;
523
-    }
524
-
525
-    /**
526
-     * @return string
527
-     */
528
-    public function getDefaultPid()
529
-    {
530
-        if (empty($this->defaultPid)) {
531
-            $this->defaultPid = $this->getModuleConfiguration('defaultPid');
532
-        }
533
-        return $this->defaultPid;
534
-    }
535
-
536
-    /**
537
-     * @param string $defaultPid
538
-     * @return $this
539
-     */
540
-    public function setDefaultPid($defaultPid)
541
-    {
542
-        $this->defaultPid = $defaultPid;
543
-        return $this;
544
-    }
545
-
546
-    /**
547
-     * @param bool $isPageTreeShown
548
-     * @return $this
549
-     */
550
-    public function showPageTree($isPageTreeShown)
551
-    {
552
-        $this->showPageTree = $isPageTreeShown;
553
-        return $this;
554
-    }
555
-
556
-    /**
557
-     * @param string $isShown
558
-     * @return $this
559
-     */
560
-    public function isShown($isShown)
561
-    {
562
-        $this->isShown = $isShown;
563
-        return $this;
564
-    }
565
-
566
-    /**
567
-     * @return $array
568
-     */
569
-    public function getDocHeaderTopLeftComponents()
570
-    {
571
-        $configuration = $this->getModuleConfiguration();
572
-        return $configuration['components'][ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::LEFT];
573
-    }
574
-
575
-    /**
576
-     * @param array $components
577
-     * @return $this
578
-     */
579
-    public function setDocHeaderTopLeftComponents(array $components)
580
-    {
581
-        $this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::LEFT] = $components;
582
-        return $this;
583
-    }
584
-
585
-    /**
586
-     * @param string|array $components
587
-     * @return $this
588
-     */
589
-    public function addDocHeaderTopLeftComponents($components)
590
-    {
591
-        if (is_string($components)) {
592
-            $components = array($components);
593
-        }
594
-        $currentComponents = $this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::LEFT];
595
-        $this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::LEFT] = array_merge($currentComponents, $components);
596
-        return $this;
597
-    }
598
-
599
-    /**
600
-     * @return $array
601
-     */
602
-    public function getDocHeaderTopRightComponents()
603
-    {
604
-        $configuration = $this->getModuleConfiguration();
605
-        return $configuration['components'][ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::RIGHT];
606
-    }
607
-
608
-    /**
609
-     * @param array $components
610
-     * @return $this
611
-     */
612
-    public function setDocHeaderTopRightComponents(array $components)
613
-    {
614
-        $this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::RIGHT] = $components;
615
-        return $this;
616
-    }
617
-
618
-    /**
619
-     * @param string|array $components
620
-     * @return $this
621
-     */
622
-    public function addDocHeaderTopRightComponents($components)
623
-    {
624
-        if (is_string($components)) {
625
-            $components = array($components);
626
-        }
627
-        $currentComponents = $this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::RIGHT];
628
-        $this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::RIGHT] = array_merge($currentComponents, $components);
629
-        return $this;
630
-    }
631
-
632
-    /**
633
-     * @return $array
634
-     */
635
-    public function getDocHeaderBottomLeftComponents()
636
-    {
637
-        $configuration = $this->getModuleConfiguration();
638
-        return $configuration['components'][ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::LEFT];
639
-    }
640
-
641
-    /**
642
-     * @param array $components
643
-     * @return $this
644
-     */
645
-    public function setDocHeaderBottomLeftComponents(array $components)
646
-    {
647
-        $this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::LEFT] = $components;
648
-        return $this;
649
-    }
650
-
651
-    /**
652
-     * @param string|array $components
653
-     * @return $this
654
-     */
655
-    public function addDocHeaderBottomLeftComponents($components)
656
-    {
657
-        if (is_string($components)) {
658
-            $components = array($components);
659
-        }
660
-        $currentComponents = $this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::LEFT];
661
-        $this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::LEFT] = array_merge($currentComponents, $components);
662
-        return $this;
663
-    }
664
-
665
-    /**
666
-     * @return $array
667
-     */
668
-    public function getDocHeaderBottomRightComponents()
669
-    {
670
-        $configuration = $this->getModuleConfiguration();
671
-        return $configuration['components'][ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::RIGHT];
672
-    }
673
-
674
-    /**
675
-     * @param array $components
676
-     * @return $this
677
-     */
678
-    public function setDocHeaderBottomRightComponents(array $components)
679
-    {
680
-        $this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::RIGHT] = $components;
681
-        return $this;
682
-    }
683
-
684
-    /**
685
-     * @param string|array $components
686
-     * @return $this
687
-     */
688
-    public function addDocHeaderBottomRightComponents($components)
689
-    {
690
-        if (is_string($components)) {
691
-            $components = array($components);
692
-        }
693
-        $currentComponents = $this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::RIGHT];
694
-        $this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::RIGHT] = array_merge($currentComponents, $components);
695
-        return $this;
696
-    }
697
-
698
-    /**
699
-     * @return $array
700
-     */
701
-    public function getGridTopComponents()
702
-    {
703
-        $configuration = $this->getModuleConfiguration();
704
-        return $configuration['components'][ModulePosition::GRID][ModulePosition::TOP];
705
-    }
706
-
707
-    /**
708
-     * @param array $components
709
-     * @return $this
710
-     */
711
-    public function setGridTopComponents(array $components)
712
-    {
713
-        $this->components[ModulePosition::GRID][ModulePosition::TOP] = $components;
714
-        return $this;
715
-    }
716
-
717
-    /**
718
-     * @param string|array $components
719
-     * @return $this
720
-     */
721
-    public function addGridTopComponents($components)
722
-    {
723
-        if (is_string($components)) {
724
-            $components = array($components);
725
-        }
726
-        $currentComponents = $this->components[ModulePosition::GRID][ModulePosition::TOP];
727
-        $this->components[ModulePosition::GRID][ModulePosition::TOP] = array_merge($currentComponents, $components);
728
-        return $this;
729
-    }
730
-
731
-    /**
732
-     * @return $array
733
-     */
734
-    public function getGridBottomComponents()
735
-    {
736
-        $configuration = $this->getModuleConfiguration();
737
-        return $configuration['components'][ModulePosition::GRID][ModulePosition::BOTTOM];
738
-    }
739
-
740
-    /**
741
-     * @param array $components
742
-     * @return $this
743
-     */
744
-    public function setGridBottomComponents(array $components)
745
-    {
746
-        $this->components[ModulePosition::GRID][ModulePosition::BOTTOM] = $components;
747
-        return $this;
748
-    }
749
-
750
-    /**
751
-     * @param string|array $components
752
-     * @return $this
753
-     */
754
-    public function addGridBottomComponents($components)
755
-    {
756
-        if (is_string($components)) {
757
-            $components = array($components);
758
-        }
759
-        $currentComponents = $this->components[ModulePosition::GRID][ModulePosition::BOTTOM];
760
-        $this->components[ModulePosition::GRID][ModulePosition::BOTTOM] = array_merge($currentComponents, $components);
761
-        return $this;
762
-    }
763
-
764
-    /**
765
-     * @return $array
766
-     */
767
-    public function getGridButtonsComponents()
768
-    {
769
-        $configuration = $this->getModuleConfiguration();
770
-        return $configuration['components'][ModulePosition::GRID][ModulePosition::BUTTONS];
771
-    }
772
-
773
-    /**
774
-     * @param array $components
775
-     * @return $this
776
-     */
777
-    public function setGridButtonsComponents(array $components)
778
-    {
779
-        $this->components[ModulePosition::GRID][ModulePosition::BUTTONS] = $components;
780
-        return $this;
781
-    }
782
-
783
-    /**
784
-     * @param string|array $components
785
-     * @return $this
786
-     */
787
-    public function addGridButtonsComponents($components)
788
-    {
789
-        if (is_string($components)) {
790
-            $components = array($components);
791
-        }
792
-        $currentComponents = $this->components[ModulePosition::GRID][ModulePosition::BUTTONS];
793
-        $this->components[ModulePosition::GRID][ModulePosition::BUTTONS] = array_merge($currentComponents, $components);
794
-        return $this;
795
-    }
796
-
797
-    /**
798
-     * @return $array
799
-     */
800
-    public function getMenuMassActionComponents()
801
-    {
802
-        $configuration = $this->getModuleConfiguration();
803
-        return $configuration['components'][ModulePosition::MENU_MASS_ACTION];
804
-    }
805
-
806
-    /**
807
-     * @param array $components
808
-     * @return $this
809
-     */
810
-    public function setMenuMassActionComponents(array $components)
811
-    {
812
-        $this->components[ModulePosition::MENU_MASS_ACTION] = $components;
813
-        return $this;
814
-    }
815
-
816
-    /**
817
-     * @param string|array $components
818
-     * @return $this
819
-     */
820
-    public function addMenuMassActionComponents($components)
821
-    {
822
-        if (is_string($components)) {
823
-            $components = array($components);
824
-        }
825
-        $currentComponents = $this->components[ModulePosition::MENU_MASS_ACTION];
826
-        $this->components[ModulePosition::MENU_MASS_ACTION] = array_merge($currentComponents, $components);
827
-        return $this;
828
-    }
829
-
830
-    /**
831
-     * @return string
832
-     */
833
-    public function getAccess()
834
-    {
835
-        return $this->access;
836
-    }
837
-
838
-    /**
839
-     * @param string $access
840
-     * @return $this
841
-     */
842
-    public function setAccess($access)
843
-    {
844
-        $this->access = $access;
845
-        return $this;
846
-    }
847
-
848
-    /**
849
-     * @return \string[]
850
-     */
851
-    public function getAdditionalJavaScriptFiles()
852
-    {
853
-        if (empty($this->additionalJavaScriptFiles)) {
854
-            $this->additionalJavaScriptFiles = $this->getModuleConfiguration('additionalJavaScriptFiles');
855
-        }
856
-        return $this->additionalJavaScriptFiles;
857
-    }
858
-
859
-    /**
860
-     * @return \string[]
861
-     */
862
-    public function getAdditionalStyleSheetFiles()
863
-    {
864
-        if (empty($this->additionalStyleSheetFiles)) {
865
-            $this->additionalStyleSheetFiles = $this->getModuleConfiguration('additionalStyleSheetFiles');
866
-        }
867
-        return $this->additionalStyleSheetFiles;
868
-    }
869
-
870
-    /**
871
-     * @return array
872
-     */
873
-    public function getComponents()
874
-    {
875
-        return $this->components;
876
-    }
877
-
878
-    /**
879
-     * @param string $pluginName
880
-     * @return bool
881
-     */
882
-    public function hasPlugin($pluginName = '')
883
-    {
884
-        $parameterPrefix = $this->getParameterPrefix();
885
-        $parameters = GeneralUtility::_GET($parameterPrefix);
886
-
887
-        $hasPlugin = !empty($parameters['plugins']) && is_array($parameters['plugins']);
888
-        if ($hasPlugin && $pluginName) {
889
-            $hasPlugin = in_array($pluginName, $parameters['plugins']);
890
-        }
891
-        return $hasPlugin;
892
-    }
893
-
894
-    /**
895
-     * Compute the internal module code
896
-     *
897
-     * @param NULL|string $dataType
898
-     * @return string
899
-     */
900
-    protected function getInternalModuleSignature($dataType = NULL)
901
-    {
902
-        if (is_null($dataType)) {
903
-            $dataType = $this->dataType;
904
-        }
905
-        $subModuleName = $dataType . '_' . $this->moduleKey;
906
-        return 'Vidi' . GeneralUtility::underscoredToUpperCamelCase($subModuleName);
907
-    }
908
-
909
-    /**
910
-     * Make sure default values are correctly initialized for the module.
911
-     *
912
-     * @return void
913
-     */
914
-    protected function initializeDefaultValues()
915
-    {
916
-        if (is_null($this->mainModule)) {
917
-            $this->mainModule = self::DEFAULT_MAIN_MODULE;
918
-        }
919
-
920
-        if (is_null($this->defaultPid)) {
921
-            $this->defaultPid = self::DEFAULT_PID;
922
-        }
923
-    }
29
+	/**
30
+	 * Define the default main module
31
+	 */
32
+	const DEFAULT_MAIN_MODULE = 'content';
33
+
34
+	/**
35
+	 * Define the default pid
36
+	 */
37
+	const DEFAULT_PID = 0;
38
+
39
+	/**
40
+	 * The type of data being listed (which corresponds to a table name in TCA)
41
+	 *
42
+	 * @var string
43
+	 */
44
+	protected $dataType;
45
+
46
+	/**
47
+	 * @var string
48
+	 */
49
+	protected $defaultPid;
50
+
51
+	/**
52
+	 * @var bool
53
+	 */
54
+	protected $showPageTree;
55
+
56
+	/**
57
+	 * @var bool
58
+	 */
59
+	protected $isShown = TRUE;
60
+
61
+	/**
62
+	 * @var string
63
+	 */
64
+	protected $access = Access::USER;
65
+
66
+	/**
67
+	 * @var string
68
+	 */
69
+	protected $mainModule;
70
+
71
+	/**
72
+	 * @var string
73
+	 */
74
+	protected $position = '';
75
+
76
+	/**
77
+	 * @var string
78
+	 */
79
+	protected $icon = '';
80
+
81
+	/**
82
+	 * @var string
83
+	 */
84
+	protected $moduleLanguageFile = 'LLL:EXT:vidi/Resources/Private/Language/locallang_module.xlf';
85
+
86
+	/**
87
+	 * The module key such as m1, m2.
88
+	 *
89
+	 * @var string
90
+	 */
91
+	protected $moduleKey = 'm1';
92
+
93
+	/**
94
+	 * @var string[]
95
+	 */
96
+	protected $additionalJavaScriptFiles = array();
97
+
98
+	/**
99
+	 * @var string[]
100
+	 */
101
+	protected $additionalStyleSheetFiles = array();
102
+
103
+	/**
104
+	 * @var array
105
+	 */
106
+	protected $components = array(
107
+		ModulePosition::DOC_HEADER => array(
108
+			ModulePosition::TOP => array(
109
+				ModulePosition::LEFT => array(),
110
+				ModulePosition::RIGHT => array(
111
+					'Fab\Vidi\View\Button\ToolButton',
112
+				),
113
+			),
114
+			ModulePosition::BOTTOM => array(
115
+				ModulePosition::LEFT => array(
116
+					'Fab\Vidi\View\Button\NewButton',
117
+					'Fab\Vidi\ViewHelpers\Link\BackViewHelper',
118
+				),
119
+				ModulePosition::RIGHT => array(),
120
+			),
121
+		),
122
+		ModulePosition::GRID => array(
123
+			ModulePosition::TOP => array(
124
+				'Fab\Vidi\View\Check\PidCheck',
125
+				'Fab\Vidi\View\Check\RelationsCheck',
126
+				'Fab\Vidi\View\Tab\DataTypeTab',
127
+			),
128
+			ModulePosition::BUTTONS => array(
129
+				'Fab\Vidi\View\Button\EditButton',
130
+				'Fab\Vidi\View\Button\DeleteButton',
131
+			),
132
+			ModulePosition::BOTTOM => array(),
133
+		),
134
+		ModulePosition::MENU_MASS_ACTION => array(
135
+			'Fab\Vidi\View\MenuItem\ExportXlsMenuItem',
136
+			'Fab\Vidi\View\MenuItem\ExportXmlMenuItem',
137
+			'Fab\Vidi\View\MenuItem\ExportCsvMenuItem',
138
+			'Fab\Vidi\View\MenuItem\DividerMenuItem',
139
+			'Fab\Vidi\View\MenuItem\MassDeleteMenuItem',
140
+			#'Fab\Vidi\View\MenuItem\MassEditMenuItem',
141
+		),
142
+	);
143
+
144
+	/**
145
+	 * @param string $dataType
146
+	 */
147
+	public function __construct($dataType = NULL)
148
+	{
149
+		$this->dataType = $dataType;
150
+	}
151
+
152
+	/**
153
+	 * Tell whether a module is already registered.
154
+	 *
155
+	 * @param string $dataType
156
+	 * @return bool
157
+	 */
158
+	public function isRegistered($dataType)
159
+	{
160
+		$internalModuleSignature = $this->getInternalModuleSignature($dataType);
161
+		return !empty($GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature]);
162
+	}
163
+
164
+	/**
165
+	 * Register the module
166
+	 *
167
+	 * @return void
168
+	 */
169
+	public function register()
170
+	{
171
+
172
+		$this->initializeDefaultValues();
173
+		$internalModuleSignature = $this->getInternalModuleSignature();
174
+
175
+		$GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature] = array();
176
+		$GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature]['dataType'] = $this->dataType;
177
+		$GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature]['mainModule'] = $this->mainModule;
178
+		$GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature]['defaultPid'] = $this->defaultPid;
179
+		$GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature]['additionalJavaScriptFiles'] = $this->additionalJavaScriptFiles;
180
+		$GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature]['additionalStyleSheetFiles'] = $this->additionalStyleSheetFiles;
181
+		$GLOBALS['TBE_MODULES_EXT']['vidi'][$internalModuleSignature]['components'] = $this->components;
182
+
183
+		// Register and displays module in the BE only if told, default is TRUE.
184
+		if ($this->isShown) {
185
+			$moduleConfiguration = array(
186
+				#'routeTarget' => \Fab\Vidi\Controller\Backend\ContentController::class . '::mainAction', // what to do here?
187
+				'access' => $this->access,
188
+				'labels' => $this->moduleLanguageFile,
189
+				'inheritNavigationComponentFromMainModule' => TRUE
190
+			);
191
+
192
+			if (!empty($this->icon)) {
193
+				$moduleConfiguration['icon'] = $this->icon;
194
+			}
195
+
196
+			if (!is_null($this->showPageTree)) {
197
+				if ($this->showPageTree) {
198
+					$moduleConfiguration['navigationComponentId'] = 'typo3-pagetree';
199
+				} else {
200
+					$moduleConfiguration['inheritNavigationComponentFromMainModule'] = FALSE;
201
+				}
202
+			}
203
+
204
+			ExtensionUtility::registerModule(
205
+				'Fab.vidi',
206
+				$this->mainModule,
207
+				$this->dataType . '_' . $this->moduleKey,
208
+				$this->position,
209
+				array(
210
+					'Content' => 'index, list, delete, update, edit, copy, move, localize, sort, copyClipboard, moveClipboard',
211
+					'Tool' => 'welcome, work',
212
+					'Facet' => 'autoSuggest, autoSuggests',
213
+					'Selection' => 'edit, update, create, delete, list, show',
214
+					'UserPreferences' => 'save',
215
+					'Clipboard' => 'save, flush, show',
216
+				),
217
+				$moduleConfiguration
218
+			);
219
+		}
220
+	}
221
+
222
+	/**
223
+	 * Return the module code for a BE module.
224
+	 *
225
+	 * @return string
226
+	 */
227
+	public function getSignature()
228
+	{
229
+		return GeneralUtility::_GP(Parameter::MODULE);
230
+	}
231
+
232
+	/**
233
+	 * Tell whether the current module is the list one.
234
+	 *
235
+	 * @return bool
236
+	 */
237
+	public function copeWithPageTree()
238
+	{
239
+		return GeneralUtility::_GP(Parameter::MODULE) === 'web_VidiM1';
240
+	}
241
+
242
+	/**
243
+	 * Returns the current pid.
244
+	 *
245
+	 * @return bool
246
+	 */
247
+	public function getCurrentPid()
248
+	{
249
+		return GeneralUtility::_GET(Parameter::PID) > 0 ? (int)GeneralUtility::_GET(Parameter::PID) : 0;
250
+	}
251
+
252
+	/**
253
+	 * Return the Vidi module code which is stored in TBE_MODULES_EXT
254
+	 *
255
+	 * @return string
256
+	 */
257
+	public function getVidiModuleCode()
258
+	{
259
+
260
+		if ($this->copeWithPageTree()) {
261
+			$userPreferenceKey = sprintf('Vidi_pid_%s', $this->getCurrentPid());
262
+
263
+			if (GeneralUtility::_GP(Parameter::SUBMODULE)) {
264
+				$subModuleCode = GeneralUtility::_GP(Parameter::SUBMODULE);
265
+				BackendUserPreferenceService::getInstance()->set($userPreferenceKey, $subModuleCode);
266
+			} else {
267
+
268
+				$defaultModuleCode = BackendUserPreferenceService::getInstance()->get($userPreferenceKey);
269
+				if (empty($defaultModuleCode)) {
270
+					$defaultModuleCode = 'VidiTtContentM1'; // hard-coded submodule
271
+					BackendUserPreferenceService::getInstance()->set($userPreferenceKey, $defaultModuleCode);
272
+				}
273
+
274
+				$vidiModules = ModuleService::getInstance()->getModulesForCurrentPid();
275
+
276
+				if (empty($vidiModules)) {
277
+					$subModuleCode = $defaultModuleCode;
278
+				} elseif (isset($vidiModules[$defaultModuleCode])) {
279
+					$subModuleCode = $defaultModuleCode;
280
+				} else {
281
+					$subModuleCode = ModuleService::getInstance()->getFirstModuleForPid($this->getCurrentPid());
282
+				}
283
+			}
284
+		} else {
285
+			$moduleCode = $this->getSignature();
286
+
287
+			// Remove first part which is separated "_"
288
+			$delimiter = strpos($moduleCode, '_') + 1;
289
+			$subModuleCode = substr($moduleCode, $delimiter);
290
+		}
291
+
292
+		return $subModuleCode;
293
+	}
294
+
295
+	/**
296
+	 * Return the module URL.
297
+	 *
298
+	 * @param array $additionalParameters
299
+	 * @param bool $absoluteUrl
300
+	 * @return string
301
+	 */
302
+	public function getModuleUrl(array $additionalParameters = array(), $absoluteUrl = false)
303
+	{
304
+		$moduleCode = $this->getSignature();
305
+
306
+		// Add possible submodule if current module has page tree.
307
+		if ($this->copeWithPageTree() && !isset($additionalParameters[Parameter::SUBMODULE])) {
308
+			$additionalParameters[Parameter::SUBMODULE] = $this->getVidiModuleCode();
309
+		}
310
+
311
+		// And don't forget the pid!
312
+		if (GeneralUtility::_GET(Parameter::PID)) {
313
+			$additionalParameters[Parameter::PID] = GeneralUtility::_GET(Parameter::PID);
314
+		}
315
+
316
+		$moduleUrl = BackendUtility::getModuleUrl($moduleCode, $additionalParameters, false, $absoluteUrl);
317
+		return $moduleUrl;
318
+	}
319
+
320
+	/**
321
+	 * Return the module absolute URL.
322
+	 *
323
+	 * @param array $additionalParameters
324
+	 * @return string
325
+	 */
326
+	public function getModuleAbsoluteUrl(array $additionalParameters = array())
327
+	{
328
+		return $this->getModuleUrl($additionalParameters, true);
329
+	}
330
+
331
+	/**
332
+	 * Return the parameter prefix for a BE module.
333
+	 *
334
+	 * @return string
335
+	 */
336
+	public function getParameterPrefix()
337
+	{
338
+		return 'tx_vidi_' . strtolower($this->getSignature());
339
+	}
340
+
341
+	/**
342
+	 * Return a configuration key or the entire module configuration array if not key is given.
343
+	 *
344
+	 * @param string $key
345
+	 * @throws InvalidKeyInArrayException
346
+	 * @return mixed
347
+	 */
348
+	public function getModuleConfiguration($key = '')
349
+	{
350
+
351
+		$vidiModuleCode = $this->getVidiModuleCode();
352
+
353
+		// Module code must exist
354
+		if (empty($GLOBALS['TBE_MODULES_EXT']['vidi'][$vidiModuleCode])) {
355
+			$message = sprintf('Invalid or not existing module code "%s"', $vidiModuleCode);
356
+			throw new InvalidKeyInArrayException($message, 1375092053);
357
+		}
358
+
359
+		$result = $GLOBALS['TBE_MODULES_EXT']['vidi'][$vidiModuleCode];
360
+
361
+		if (!empty($key)) {
362
+			if (isset($result[$key])) {
363
+				$result = $result[$key];
364
+			} else {
365
+				// key must exist
366
+				$message = sprintf('Invalid key configuration "%s"', $key);
367
+				throw new InvalidKeyInArrayException($message, 1375092054);
368
+			}
369
+		}
370
+		return $result;
371
+	}
372
+
373
+	/**
374
+	 * @param string $icon
375
+	 * @return $this
376
+	 */
377
+	public function setIcon($icon)
378
+	{
379
+		$this->icon = $icon;
380
+		return $this;
381
+	}
382
+
383
+	/**
384
+	 * @return string
385
+	 */
386
+	public function getIcon()
387
+	{
388
+		return $this->icon;
389
+	}
390
+
391
+	/**
392
+	 * @param string $mainModule
393
+	 * @return $this
394
+	 */
395
+	public function setMainModule($mainModule)
396
+	{
397
+		$this->mainModule = $mainModule;
398
+		return $this;
399
+	}
400
+
401
+	/**
402
+	 * @return string
403
+	 */
404
+	public function getMainModule()
405
+	{
406
+		if (is_null($this->mainModule)) {
407
+			$this->mainModule = $this->getModuleConfiguration('mainModule');
408
+		}
409
+		return $this->mainModule;
410
+	}
411
+
412
+	/**
413
+	 * @param string $moduleLanguageFile
414
+	 * @return $this
415
+	 */
416
+	public function setModuleLanguageFile($moduleLanguageFile)
417
+	{
418
+		$this->moduleLanguageFile = $moduleLanguageFile;
419
+		return $this;
420
+	}
421
+
422
+	/**
423
+	 * @return string
424
+	 */
425
+	public function getModuleLanguageFile()
426
+	{
427
+		return $this->moduleLanguageFile;
428
+	}
429
+
430
+	/**
431
+	 * @param string $position
432
+	 * @return $this
433
+	 */
434
+	public function setPosition($position)
435
+	{
436
+		$this->position = $position;
437
+		return $this;
438
+	}
439
+
440
+	/**
441
+	 * @return string
442
+	 */
443
+	public function getPosition()
444
+	{
445
+		return $this->position;
446
+	}
447
+
448
+	/**
449
+	 * @param array $files
450
+	 * @return $this
451
+	 */
452
+	public function addJavaScriptFiles(array $files)
453
+	{
454
+		foreach ($files as $file) {
455
+			$this->additionalJavaScriptFiles[] = $file;
456
+		}
457
+		return $this;
458
+	}
459
+
460
+	/**
461
+	 * @param string $fileNameAndPath
462
+	 * @return $this
463
+	 */
464
+	public function addJavaScriptFile($fileNameAndPath)
465
+	{
466
+		$this->additionalJavaScriptFiles[] = $fileNameAndPath;
467
+		return $this;
468
+	}
469
+
470
+	/**
471
+	 * @param array $files
472
+	 * @return $this
473
+	 */
474
+	public function addStyleSheetFiles(array $files)
475
+	{
476
+		foreach ($files as $file) {
477
+			$this->additionalStyleSheetFiles[] = $file;
478
+		}
479
+		return $this;
480
+	}
481
+
482
+	/**
483
+	 * @param string $fileNameAndPath
484
+	 * @return $this
485
+	 */
486
+	public function addStyleSheetFile($fileNameAndPath)
487
+	{
488
+		$this->additionalStyleSheetFiles[] = $fileNameAndPath;
489
+		return $this;
490
+	}
491
+
492
+	/**
493
+	 * @return string
494
+	 */
495
+	public function getDataType()
496
+	{
497
+		if (is_null($this->dataType)) {
498
+			$this->dataType = $this->getModuleConfiguration('dataType');
499
+		}
500
+		return $this->dataType;
501
+	}
502
+
503
+	/**
504
+	 * @return array
505
+	 */
506
+	public function getDataTypes()
507
+	{
508
+		$dataTypes = array();
509
+		foreach ($GLOBALS['TBE_MODULES_EXT']['vidi'] as $module) {
510
+			$dataTypes[] = $module['dataType'];
511
+		}
512
+		return $dataTypes;
513
+	}
514
+
515
+	/**
516
+	 * @param string $dataType
517
+	 * @return $this
518
+	 */
519
+	public function setDataType($dataType)
520
+	{
521
+		$this->dataType = $dataType;
522
+		return $this;
523
+	}
524
+
525
+	/**
526
+	 * @return string
527
+	 */
528
+	public function getDefaultPid()
529
+	{
530
+		if (empty($this->defaultPid)) {
531
+			$this->defaultPid = $this->getModuleConfiguration('defaultPid');
532
+		}
533
+		return $this->defaultPid;
534
+	}
535
+
536
+	/**
537
+	 * @param string $defaultPid
538
+	 * @return $this
539
+	 */
540
+	public function setDefaultPid($defaultPid)
541
+	{
542
+		$this->defaultPid = $defaultPid;
543
+		return $this;
544
+	}
545
+
546
+	/**
547
+	 * @param bool $isPageTreeShown
548
+	 * @return $this
549
+	 */
550
+	public function showPageTree($isPageTreeShown)
551
+	{
552
+		$this->showPageTree = $isPageTreeShown;
553
+		return $this;
554
+	}
555
+
556
+	/**
557
+	 * @param string $isShown
558
+	 * @return $this
559
+	 */
560
+	public function isShown($isShown)
561
+	{
562
+		$this->isShown = $isShown;
563
+		return $this;
564
+	}
565
+
566
+	/**
567
+	 * @return $array
568
+	 */
569
+	public function getDocHeaderTopLeftComponents()
570
+	{
571
+		$configuration = $this->getModuleConfiguration();
572
+		return $configuration['components'][ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::LEFT];
573
+	}
574
+
575
+	/**
576
+	 * @param array $components
577
+	 * @return $this
578
+	 */
579
+	public function setDocHeaderTopLeftComponents(array $components)
580
+	{
581
+		$this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::LEFT] = $components;
582
+		return $this;
583
+	}
584
+
585
+	/**
586
+	 * @param string|array $components
587
+	 * @return $this
588
+	 */
589
+	public function addDocHeaderTopLeftComponents($components)
590
+	{
591
+		if (is_string($components)) {
592
+			$components = array($components);
593
+		}
594
+		$currentComponents = $this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::LEFT];
595
+		$this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::LEFT] = array_merge($currentComponents, $components);
596
+		return $this;
597
+	}
598
+
599
+	/**
600
+	 * @return $array
601
+	 */
602
+	public function getDocHeaderTopRightComponents()
603
+	{
604
+		$configuration = $this->getModuleConfiguration();
605
+		return $configuration['components'][ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::RIGHT];
606
+	}
607
+
608
+	/**
609
+	 * @param array $components
610
+	 * @return $this
611
+	 */
612
+	public function setDocHeaderTopRightComponents(array $components)
613
+	{
614
+		$this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::RIGHT] = $components;
615
+		return $this;
616
+	}
617
+
618
+	/**
619
+	 * @param string|array $components
620
+	 * @return $this
621
+	 */
622
+	public function addDocHeaderTopRightComponents($components)
623
+	{
624
+		if (is_string($components)) {
625
+			$components = array($components);
626
+		}
627
+		$currentComponents = $this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::RIGHT];
628
+		$this->components[ModulePosition::DOC_HEADER][ModulePosition::TOP][ModulePosition::RIGHT] = array_merge($currentComponents, $components);
629
+		return $this;
630
+	}
631
+
632
+	/**
633
+	 * @return $array
634
+	 */
635
+	public function getDocHeaderBottomLeftComponents()
636
+	{
637
+		$configuration = $this->getModuleConfiguration();
638
+		return $configuration['components'][ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::LEFT];
639
+	}
640
+
641
+	/**
642
+	 * @param array $components
643
+	 * @return $this
644
+	 */
645
+	public function setDocHeaderBottomLeftComponents(array $components)
646
+	{
647
+		$this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::LEFT] = $components;
648
+		return $this;
649
+	}
650
+
651
+	/**
652
+	 * @param string|array $components
653
+	 * @return $this
654
+	 */
655
+	public function addDocHeaderBottomLeftComponents($components)
656
+	{
657
+		if (is_string($components)) {
658
+			$components = array($components);
659
+		}
660
+		$currentComponents = $this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::LEFT];
661
+		$this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::LEFT] = array_merge($currentComponents, $components);
662
+		return $this;
663
+	}
664
+
665
+	/**
666
+	 * @return $array
667
+	 */
668
+	public function getDocHeaderBottomRightComponents()
669
+	{
670
+		$configuration = $this->getModuleConfiguration();
671
+		return $configuration['components'][ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::RIGHT];
672
+	}
673
+
674
+	/**
675
+	 * @param array $components
676
+	 * @return $this
677
+	 */
678
+	public function setDocHeaderBottomRightComponents(array $components)
679
+	{
680
+		$this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::RIGHT] = $components;
681
+		return $this;
682
+	}
683
+
684
+	/**
685
+	 * @param string|array $components
686
+	 * @return $this
687
+	 */
688
+	public function addDocHeaderBottomRightComponents($components)
689
+	{
690
+		if (is_string($components)) {
691
+			$components = array($components);
692
+		}
693
+		$currentComponents = $this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::RIGHT];
694
+		$this->components[ModulePosition::DOC_HEADER][ModulePosition::BOTTOM][ModulePosition::RIGHT] = array_merge($currentComponents, $components);
695
+		return $this;
696
+	}
697
+
698
+	/**
699
+	 * @return $array
700
+	 */
701
+	public function getGridTopComponents()
702
+	{
703
+		$configuration = $this->getModuleConfiguration();
704
+		return $configuration['components'][ModulePosition::GRID][ModulePosition::TOP];
705
+	}
706
+
707
+	/**
708
+	 * @param array $components
709
+	 * @return $this
710
+	 */
711
+	public function setGridTopComponents(array $components)
712
+	{
713
+		$this->components[ModulePosition::GRID][ModulePosition::TOP] = $components;
714
+		return $this;
715
+	}
716
+
717
+	/**
718
+	 * @param string|array $components
719
+	 * @return $this
720
+	 */
721
+	public function addGridTopComponents($components)
722
+	{
723
+		if (is_string($components)) {
724
+			$components = array($components);
725
+		}
726
+		$currentComponents = $this->components[ModulePosition::GRID][ModulePosition::TOP];
727
+		$this->components[ModulePosition::GRID][ModulePosition::TOP] = array_merge($currentComponents, $components);
728
+		return $this;
729
+	}
730
+
731
+	/**
732
+	 * @return $array
733
+	 */
734
+	public function getGridBottomComponents()
735
+	{
736
+		$configuration = $this->getModuleConfiguration();
737
+		return $configuration['components'][ModulePosition::GRID][ModulePosition::BOTTOM];
738
+	}
739
+
740
+	/**
741
+	 * @param array $components
742
+	 * @return $this
743
+	 */
744
+	public function setGridBottomComponents(array $components)
745
+	{
746
+		$this->components[ModulePosition::GRID][ModulePosition::BOTTOM] = $components;
747
+		return $this;
748
+	}
749
+
750
+	/**
751
+	 * @param string|array $components
752
+	 * @return $this
753
+	 */
754
+	public function addGridBottomComponents($components)
755
+	{
756
+		if (is_string($components)) {
757
+			$components = array($components);
758
+		}
759
+		$currentComponents = $this->components[ModulePosition::GRID][ModulePosition::BOTTOM];
760
+		$this->components[ModulePosition::GRID][ModulePosition::BOTTOM] = array_merge($currentComponents, $components);
761
+		return $this;
762
+	}
763
+
764
+	/**
765
+	 * @return $array
766
+	 */
767
+	public function getGridButtonsComponents()
768
+	{
769
+		$configuration = $this->getModuleConfiguration();
770
+		return $configuration['components'][ModulePosition::GRID][ModulePosition::BUTTONS];
771
+	}
772
+
773
+	/**
774
+	 * @param array $components
775
+	 * @return $this
776
+	 */
777
+	public function setGridButtonsComponents(array $components)
778
+	{
779
+		$this->components[ModulePosition::GRID][ModulePosition::BUTTONS] = $components;
780
+		return $this;
781
+	}
782
+
783
+	/**
784
+	 * @param string|array $components
785
+	 * @return $this
786
+	 */
787
+	public function addGridButtonsComponents($components)
788
+	{
789
+		if (is_string($components)) {
790
+			$components = array($components);
791
+		}
792
+		$currentComponents = $this->components[ModulePosition::GRID][ModulePosition::BUTTONS];
793
+		$this->components[ModulePosition::GRID][ModulePosition::BUTTONS] = array_merge($currentComponents, $components);
794
+		return $this;
795
+	}
796
+
797
+	/**
798
+	 * @return $array
799
+	 */
800
+	public function getMenuMassActionComponents()
801
+	{
802
+		$configuration = $this->getModuleConfiguration();
803
+		return $configuration['components'][ModulePosition::MENU_MASS_ACTION];
804
+	}
805
+
806
+	/**
807
+	 * @param array $components
808
+	 * @return $this
809
+	 */
810
+	public function setMenuMassActionComponents(array $components)
811
+	{
812
+		$this->components[ModulePosition::MENU_MASS_ACTION] = $components;
813
+		return $this;
814
+	}
815
+
816
+	/**
817
+	 * @param string|array $components
818
+	 * @return $this
819
+	 */
820
+	public function addMenuMassActionComponents($components)
821
+	{
822
+		if (is_string($components)) {
823
+			$components = array($components);
824
+		}
825
+		$currentComponents = $this->components[ModulePosition::MENU_MASS_ACTION];
826
+		$this->components[ModulePosition::MENU_MASS_ACTION] = array_merge($currentComponents, $components);
827
+		return $this;
828
+	}
829
+
830
+	/**
831
+	 * @return string
832
+	 */
833
+	public function getAccess()
834
+	{
835
+		return $this->access;
836
+	}
837
+
838
+	/**
839
+	 * @param string $access
840
+	 * @return $this
841
+	 */
842
+	public function setAccess($access)
843
+	{
844
+		$this->access = $access;
845
+		return $this;
846
+	}
847
+
848
+	/**
849
+	 * @return \string[]
850
+	 */
851
+	public function getAdditionalJavaScriptFiles()
852
+	{
853
+		if (empty($this->additionalJavaScriptFiles)) {
854
+			$this->additionalJavaScriptFiles = $this->getModuleConfiguration('additionalJavaScriptFiles');
855
+		}
856
+		return $this->additionalJavaScriptFiles;
857
+	}
858
+
859
+	/**
860
+	 * @return \string[]
861
+	 */
862
+	public function getAdditionalStyleSheetFiles()
863
+	{
864
+		if (empty($this->additionalStyleSheetFiles)) {
865
+			$this->additionalStyleSheetFiles = $this->getModuleConfiguration('additionalStyleSheetFiles');
866
+		}
867
+		return $this->additionalStyleSheetFiles;
868
+	}
869
+
870
+	/**
871
+	 * @return array
872
+	 */
873
+	public function getComponents()
874
+	{
875
+		return $this->components;
876
+	}
877
+
878
+	/**
879
+	 * @param string $pluginName
880
+	 * @return bool
881
+	 */
882
+	public function hasPlugin($pluginName = '')
883
+	{
884
+		$parameterPrefix = $this->getParameterPrefix();
885
+		$parameters = GeneralUtility::_GET($parameterPrefix);
886
+
887
+		$hasPlugin = !empty($parameters['plugins']) && is_array($parameters['plugins']);
888
+		if ($hasPlugin && $pluginName) {
889
+			$hasPlugin = in_array($pluginName, $parameters['plugins']);
890
+		}
891
+		return $hasPlugin;
892
+	}
893
+
894
+	/**
895
+	 * Compute the internal module code
896
+	 *
897
+	 * @param NULL|string $dataType
898
+	 * @return string
899
+	 */
900
+	protected function getInternalModuleSignature($dataType = NULL)
901
+	{
902
+		if (is_null($dataType)) {
903
+			$dataType = $this->dataType;
904
+		}
905
+		$subModuleName = $dataType . '_' . $this->moduleKey;
906
+		return 'Vidi' . GeneralUtility::underscoredToUpperCamelCase($subModuleName);
907
+	}
908
+
909
+	/**
910
+	 * Make sure default values are correctly initialized for the module.
911
+	 *
912
+	 * @return void
913
+	 */
914
+	protected function initializeDefaultValues()
915
+	{
916
+		if (is_null($this->mainModule)) {
917
+			$this->mainModule = self::DEFAULT_MAIN_MODULE;
918
+		}
919
+
920
+		if (is_null($this->defaultPid)) {
921
+			$this->defaultPid = self::DEFAULT_PID;
922
+		}
923
+	}
924 924
 
925 925
 }
Please login to merge, or discard this patch.
Classes/Persistence/Query.php 2 patches
Doc Comments   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -290,7 +290,7 @@  discard block
 block discarded – undo
290 290
      * 'bar' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
291 291
      * )
292 292
      *
293
-     * @return array
293
+     * @return integer
294 294
      * @api
295 295
      */
296 296
     public function getOrderings()
@@ -483,7 +483,7 @@  discard block
 block discarded – undo
483 483
      * Returns a like criterion used for matching objects against a query
484 484
      *
485 485
      * @param string $propertyName The name of the property to compare against
486
-     * @param mixed $operand The value to compare with
486
+     * @param string $operand The value to compare with
487 487
      * @param boolean $caseSensitive Whether the matching should be done case-sensitive
488 488
      * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
489 489
      * @api
Please login to merge, or discard this patch.
Indentation   +644 added lines, -644 removed lines patch added patch discarded remove patch
@@ -29,649 +29,649 @@
 block discarded – undo
29 29
 class Query implements QueryInterface
30 30
 {
31 31
 
32
-    /**
33
-     * An inner join.
34
-     */
35
-    const JCR_JOIN_TYPE_INNER = '{http://www.jcp.org/jcr/1.0}joinTypeInner';
36
-
37
-    /**
38
-     * A left-outer join.
39
-     */
40
-    const JCR_JOIN_TYPE_LEFT_OUTER = '{http://www.jcp.org/jcr/1.0}joinTypeLeftOuter';
41
-
42
-    /**
43
-     * A right-outer join.
44
-     */
45
-    const JCR_JOIN_TYPE_RIGHT_OUTER = '{http://www.jcp.org/jcr/1.0}joinTypeRightOuter';
46
-
47
-    /**
48
-     * Charset of strings in QOM
49
-     */
50
-    const CHARSET = 'utf-8';
51
-
52
-    /**
53
-     * @var string
54
-     */
55
-    protected $sourceFieldName;
56
-
57
-    /**
58
-     * @var string
59
-     */
60
-    protected $type;
61
-
62
-    /**
63
-     * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
64
-     */
65
-    protected $objectManager;
66
-
67
-    /**
68
-     * @var \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface
69
-     */
70
-    protected $persistenceManager;
71
-
72
-    /**
73
-     * @var \TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory
74
-     */
75
-    protected $qomFactory;
76
-
77
-    /**
78
-     * @var \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface
79
-     */
80
-    protected $source;
81
-
82
-    /**
83
-     * @var ConstraintInterface
84
-     */
85
-    protected $constraint;
86
-
87
-    /**
88
-     * @var \TYPO3\CMS\Extbase\Persistence\Generic\Qom\Statement
89
-     */
90
-    protected $statement;
91
-
92
-    /**
93
-     * @var int
94
-     */
95
-    protected $orderings = array();
96
-
97
-    /**
98
-     * @var int
99
-     */
100
-    protected $limit;
101
-
102
-    /**
103
-     * @var int
104
-     */
105
-    protected $offset;
106
-
107
-    /**
108
-     * Apply DISTINCT upon property.
109
-     *
110
-     * @var string
111
-     */
112
-    protected $distinct;
113
-
114
-    /**
115
-     * The query settings.
116
-     *
117
-     * @var \Fab\Vidi\Persistence\QuerySettings
118
-     * @inject
119
-     */
120
-    protected $querySettings;
121
-
122
-    /**
123
-     * Constructs a query object working on the given class name
124
-     *
125
-     * @param string $type
126
-     */
127
-    public function __construct($type)
128
-    {
129
-        $this->type = $type;
130
-    }
131
-
132
-    /**
133
-     * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
134
-     * @return void
135
-     */
136
-    public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
137
-    {
138
-        $this->objectManager = $objectManager;
139
-    }
140
-
141
-    /**
142
-     * Injects the persistence manager, used to fetch the CR session
143
-     *
144
-     * @param \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager
145
-     * @return void
146
-     */
147
-    public function injectPersistenceManager(\TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager)
148
-    {
149
-        $this->persistenceManager = $persistenceManager;
150
-    }
151
-
152
-    /**
153
-     * Injects the Query Object Model Factory
154
-     *
155
-     * @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory $qomFactory
156
-     * @return void
157
-     */
158
-    public function injectQomFactory(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory $qomFactory)
159
-    {
160
-        $this->qomFactory = $qomFactory;
161
-    }
162
-
163
-    /**
164
-     * Sets the Query Settings. These Query settings must match the settings expected by
165
-     * the specific Storage Backend.
166
-     *
167
-     * @param \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface $querySettings The Query Settings
168
-     * @return void
169
-     * @api This method is not part of FLOW3 API
170
-     */
171
-    public function setQuerySettings(\TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface $querySettings)
172
-    {
173
-        $this->querySettings = $querySettings;
174
-    }
175
-
176
-    /**
177
-     * Returns the Query Settings.
178
-     *
179
-     * @throws \Exception
180
-     * @return \Fab\Vidi\Persistence\QuerySettings $querySettings The Query Settings
181
-     * @api This method is not part of FLOW3 API
182
-     */
183
-    public function getQuerySettings()
184
-    {
185
-        if (!$this->querySettings instanceof \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface) {
186
-            throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception('Tried to get the query settings without setting them before.', 1248689115);
187
-        }
188
-
189
-        // Apply possible settings to the query.
190
-        if ($this->isBackendMode()) {
191
-            /** @var \TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager $backendConfigurationManager */
192
-            $backendConfigurationManager = $this->objectManager->get('TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager');
193
-            $configuration = $backendConfigurationManager->getTypoScriptSetup();
194
-            $querySettings = array('respectSysLanguage');
195
-            foreach ($querySettings as $setting) {
196
-                if (isset($configuration['config.']['tx_vidi.']['persistence.']['backend.'][$this->type . '.'][$setting])) {
197
-                    $value = (bool)$configuration['config.']['tx_vidi.']['persistence.']['backend.'][$this->type . '.'][$setting];
198
-                    ObjectAccess::setProperty($this->querySettings, $setting, $value);
199
-                }
200
-            }
201
-        }
202
-
203
-        return $this->querySettings;
204
-    }
205
-
206
-    /**
207
-     * Returns the type this query cares for.
208
-     *
209
-     * @return string
210
-     * @api
211
-     */
212
-    public function getType()
213
-    {
214
-        return $this->type;
215
-    }
216
-
217
-    /**
218
-     * Sets the source to fetch the result from
219
-     *
220
-     * @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface $source
221
-     */
222
-    public function setSource(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface $source)
223
-    {
224
-        $this->source = $source;
225
-    }
226
-
227
-    /**
228
-     * Returns the selectorn name or an empty string, if the source is not a selector
229
-     * TODO This has to be checked at another place
230
-     *
231
-     * @return string The selector name
232
-     */
233
-    protected function getSelectorName()
234
-    {
235
-        if ($this->getSource() instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SelectorInterface) {
236
-            return $this->source->getSelectorName();
237
-        } else {
238
-            return '';
239
-        }
240
-    }
241
-
242
-    /**
243
-     * Gets the node-tuple source for this query.
244
-     *
245
-     * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface the node-tuple source; non-null
246
-     */
247
-    public function getSource()
248
-    {
249
-        if ($this->source === NULL) {
250
-            $this->source = $this->qomFactory->selector($this->getType());
251
-        }
252
-        return $this->source;
253
-    }
254
-
255
-    /**
256
-     * Executes the query against the database and returns the result
257
-     *
258
-     * @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface|array The query result object or an array if $this->getQuerySettings()->getReturnRawQueryResult() is TRUE
259
-     * @api
260
-     */
261
-    public function execute($returnRawQueryResult = FALSE)
262
-    {
263
-        /** @var \Fab\Vidi\Persistence\Storage\VidiDbBackend $backend */
264
-        $backend = $this->objectManager->get('Fab\Vidi\Persistence\Storage\VidiDbBackend', $this);
265
-        return $backend->fetchResult();
266
-    }
267
-
268
-    /**
269
-     * Sets the property names to order the result by. Expected like this:
270
-     * array(
271
-     * 'foo' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING,
272
-     * 'bar' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
273
-     * )
274
-     * where 'foo' and 'bar' are property names.
275
-     *
276
-     * @param array $orderings The property names to order by
277
-     * @return QueryInterface
278
-     * @api
279
-     */
280
-    public function setOrderings(array $orderings)
281
-    {
282
-        $this->orderings = $orderings;
283
-        return $this;
284
-    }
285
-
286
-    /**
287
-     * Returns the property names to order the result by. Like this:
288
-     * array(
289
-     * 'foo' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING,
290
-     * 'bar' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
291
-     * )
292
-     *
293
-     * @return array
294
-     * @api
295
-     */
296
-    public function getOrderings()
297
-    {
298
-        return $this->orderings;
299
-    }
300
-
301
-    /**
302
-     * Sets the maximum size of the result set to limit. Returns $this to allow
303
-     * for chaining (fluid interface)
304
-     *
305
-     * @param integer $limit
306
-     * @throws \InvalidArgumentException
307
-     * @return QueryInterface
308
-     * @api
309
-     */
310
-    public function setLimit($limit)
311
-    {
312
-        if (!is_int($limit) || $limit < 1) {
313
-            throw new \InvalidArgumentException('The limit must be an integer >= 1', 1245071870);
314
-        }
315
-        $this->limit = $limit;
316
-        return $this;
317
-    }
318
-
319
-    /**
320
-     * Resets a previously set maximum size of the result set. Returns $this to allow
321
-     * for chaining (fluid interface)
322
-     *
323
-     * @return QueryInterface
324
-     * @api
325
-     */
326
-    public function unsetLimit()
327
-    {
328
-        unset($this->limit);
329
-        return $this;
330
-    }
331
-
332
-    /**
333
-     * Returns the maximum size of the result set to limit.
334
-     *
335
-     * @return integer
336
-     * @api
337
-     */
338
-    public function getLimit()
339
-    {
340
-        return $this->limit;
341
-    }
342
-
343
-    /**
344
-     * Sets the start offset of the result set to offset. Returns $this to
345
-     * allow for chaining (fluid interface)
346
-     *
347
-     * @param integer $offset
348
-     * @throws \InvalidArgumentException
349
-     * @return QueryInterface
350
-     * @api
351
-     */
352
-    public function setOffset($offset)
353
-    {
354
-        if (!is_int($offset) || $offset < 0) {
355
-            throw new \InvalidArgumentException('The offset must be a positive integer', 1245071872);
356
-        }
357
-        $this->offset = $offset;
358
-        return $this;
359
-    }
360
-
361
-    /**
362
-     * Returns the start offset of the result set.
363
-     *
364
-     * @return integer
365
-     * @api
366
-     */
367
-    public function getOffset()
368
-    {
369
-        return $this->offset;
370
-    }
371
-
372
-    /**
373
-     * The constraint used to limit the result set. Returns $this to allow
374
-     * for chaining (fluid interface)
375
-     *
376
-     * @param ConstraintInterface $constraint
377
-     * @return QueryInterface
378
-     * @api
379
-     */
380
-    public function matching($constraint)
381
-    {
382
-        $this->constraint = $constraint;
383
-        return $this;
384
-    }
385
-
386
-    /**
387
-     * Gets the constraint for this query.
388
-     *
389
-     * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\Constraint the constraint, or null if none
390
-     * @api
391
-     */
392
-    public function getConstraint()
393
-    {
394
-        return $this->constraint;
395
-    }
396
-
397
-    /**
398
-     * Performs a logical conjunction of the given constraints. The method takes one or more contraints and concatenates them with a boolean AND.
399
-     * It also scepts a single array of constraints to be concatenated.
400
-     *
401
-     * @param mixed $constraint1 The first of multiple constraints or an array of constraints.
402
-     * @throws InvalidNumberOfConstraintsException
403
-     * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\AndInterface
404
-     * @api
405
-     */
406
-    public function logicalAnd($constraint1)
407
-    {
408
-        if (is_array($constraint1)) {
409
-            $resultingConstraint = array_shift($constraint1);
410
-            $constraints = $constraint1;
411
-        } else {
412
-            $constraints = func_get_args();
413
-            $resultingConstraint = array_shift($constraints);
414
-        }
415
-        if ($resultingConstraint === NULL) {
416
-            throw new InvalidNumberOfConstraintsException('There must be at least one constraint or a non-empty array of constraints given.', 1401289500);
417
-        }
418
-        foreach ($constraints as $constraint) {
419
-            $resultingConstraint = $this->qomFactory->_and($resultingConstraint, $constraint);
420
-        }
421
-        return $resultingConstraint;
422
-    }
423
-
424
-    /**
425
-     * Performs a logical disjunction of the two given constraints
426
-     *
427
-     * @param mixed $constraint1 The first of multiple constraints or an array of constraints.
428
-     * @throws InvalidNumberOfConstraintsException
429
-     * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\OrInterface
430
-     * @api
431
-     */
432
-    public function logicalOr($constraint1)
433
-    {
434
-        if (is_array($constraint1)) {
435
-            $resultingConstraint = array_shift($constraint1);
436
-            $constraints = $constraint1;
437
-        } else {
438
-            $constraints = func_get_args();
439
-            $resultingConstraint = array_shift($constraints);
440
-        }
441
-        if ($resultingConstraint === NULL) {
442
-            throw new InvalidNumberOfConstraintsException('There must be at least one constraint or a non-empty array of constraints given.', 1401289501);
443
-        }
444
-        foreach ($constraints as $constraint) {
445
-            $resultingConstraint = $this->qomFactory->_or($resultingConstraint, $constraint);
446
-        }
447
-        return $resultingConstraint;
448
-    }
449
-
450
-    /**
451
-     * Performs a logical negation of the given constraint
452
-     *
453
-     * @param ConstraintInterface $constraint Constraint to negate
454
-     * @throws \RuntimeException
455
-     * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\NotInterface
456
-     * @api
457
-     */
458
-    public function logicalNot(ConstraintInterface $constraint)
459
-    {
460
-        return $this->qomFactory->not($constraint);
461
-    }
462
-
463
-    /**
464
-     * Returns an equals criterion used for matching objects against a query
465
-     *
466
-     * @param string $propertyName The name of the property to compare against
467
-     * @param mixed $operand The value to compare with
468
-     * @param boolean $caseSensitive Whether the equality test should be done case-sensitive
469
-     * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
470
-     * @api
471
-     */
472
-    public function equals($propertyName, $operand, $caseSensitive = TRUE)
473
-    {
474
-        if (is_object($operand) || $caseSensitive) {
475
-            $comparison = $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_EQUAL_TO, $operand);
476
-        } else {
477
-            $comparison = $this->qomFactory->comparison($this->qomFactory->lowerCase($this->qomFactory->propertyValue($propertyName, $this->getSelectorName())), QueryInterface::OPERATOR_EQUAL_TO, \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Charset\\CharsetConverter')->conv_case(\TYPO3\CMS\Extbase\Persistence\Generic\Query::CHARSET, $operand, 'toLower'));
478
-        }
479
-        return $comparison;
480
-    }
481
-
482
-    /**
483
-     * Returns a like criterion used for matching objects against a query
484
-     *
485
-     * @param string $propertyName The name of the property to compare against
486
-     * @param mixed $operand The value to compare with
487
-     * @param boolean $caseSensitive Whether the matching should be done case-sensitive
488
-     * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
489
-     * @api
490
-     */
491
-    public function like($propertyName, $operand, $caseSensitive = TRUE)
492
-    {
493
-        return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_LIKE, $operand);
494
-    }
495
-
496
-    /**
497
-     * Returns a "contains" criterion used for matching objects against a query.
498
-     * It matches if the multivalued property contains the given operand.
499
-     *
500
-     * @param string $propertyName The name of the (multivalued) property to compare against
501
-     * @param mixed $operand The value to compare with
502
-     * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
503
-     * @api
504
-     */
505
-    public function contains($propertyName, $operand)
506
-    {
507
-        return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_CONTAINS, $operand);
508
-    }
509
-
510
-    /**
511
-     * Returns an "in" criterion used for matching objects against a query. It
512
-     * matches if the property's value is contained in the multivalued operand.
513
-     *
514
-     * @param string $propertyName The name of the property to compare against
515
-     * @param mixed $operand The value to compare with, multivalued
516
-     * @throws UnexpectedTypeException
517
-     * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
518
-     * @api
519
-     */
520
-    public function in($propertyName, $operand)
521
-    {
522
-        if (!is_array($operand) && !$operand instanceof \ArrayAccess && !$operand instanceof \Traversable) {
523
-            throw new UnexpectedTypeException('The "in" operator must be given a mutlivalued operand (array, ArrayAccess, Traversable).', 1264678095);
524
-        }
525
-        return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_IN, $operand);
526
-    }
527
-
528
-    /**
529
-     * Returns a less than criterion used for matching objects against a query
530
-     *
531
-     * @param string $propertyName The name of the property to compare against
532
-     * @param mixed $operand The value to compare with
533
-     * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
534
-     * @api
535
-     */
536
-    public function lessThan($propertyName, $operand)
537
-    {
538
-        return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_LESS_THAN, $operand);
539
-    }
540
-
541
-    /**
542
-     * Returns a less or equal than criterion used for matching objects against a query
543
-     *
544
-     * @param string $propertyName The name of the property to compare against
545
-     * @param mixed $operand The value to compare with
546
-     * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
547
-     * @api
548
-     */
549
-    public function lessThanOrEqual($propertyName, $operand)
550
-    {
551
-        return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_LESS_THAN_OR_EQUAL_TO, $operand);
552
-    }
553
-
554
-    /**
555
-     * Returns a greater than criterion used for matching objects against a query
556
-     *
557
-     * @param string $propertyName The name of the property to compare against
558
-     * @param mixed $operand The value to compare with
559
-     * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
560
-     * @api
561
-     */
562
-    public function greaterThan($propertyName, $operand)
563
-    {
564
-        return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_GREATER_THAN, $operand);
565
-    }
566
-
567
-    /**
568
-     * Returns a greater than or equal criterion used for matching objects against a query
569
-     *
570
-     * @param string $propertyName The name of the property to compare against
571
-     * @param mixed $operand The value to compare with
572
-     * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
573
-     * @api
574
-     */
575
-    public function greaterThanOrEqual($propertyName, $operand)
576
-    {
577
-        return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_GREATER_THAN_OR_EQUAL_TO, $operand);
578
-    }
579
-
580
-    /**
581
-     * Returns the query result count.
582
-     *
583
-     * @return integer The query result count
584
-     * @api
585
-     */
586
-    public function count()
587
-    {
588
-        /** @var \Fab\Vidi\Persistence\Storage\VidiDbBackend $backend */
589
-        $backend = $this->objectManager->get('Fab\Vidi\Persistence\Storage\VidiDbBackend', $this);
590
-        return $backend->countResult();
591
-    }
592
-
593
-    /**
594
-     * Returns an "isEmpty" criterion used for matching objects against a query.
595
-     * It matches if the multivalued property contains no values or is NULL.
596
-     *
597
-     * @param string $propertyName The name of the multivalued property to compare against
598
-     * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\NotImplementedException
599
-     * @throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException if used on a single-valued property
600
-     * @api
601
-     */
602
-    public function isEmpty($propertyName)
603
-    {
604
-        throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\NotImplementedException(__METHOD__);
605
-    }
606
-
607
-    /**
608
-     * @return string
609
-     */
610
-    public function getDistinct()
611
-    {
612
-        return $this->distinct;
613
-    }
614
-
615
-    /**
616
-     * @param string $distinct
617
-     * @return $this
618
-     */
619
-    public function setDistinct($distinct)
620
-    {
621
-        $this->distinct = $distinct;
622
-        return $this;
623
-    }
624
-
625
-    /**
626
-     * Sets the statement of this query. If you use this, you will lose the abstraction from a concrete storage
627
-     * backend (database).
628
-     *
629
-     * @param string|\TYPO3\CMS\Core\Database\PreparedStatement $statement The statement
630
-     * @param array $parameters An array of parameters. These will be bound to placeholders '?' in the $statement.
631
-     * @return QueryInterface
632
-     */
633
-    public function statement($statement, array $parameters = array())
634
-    {
635
-        $this->statement = $this->qomFactory->statement($statement, $parameters);
636
-        return $this;
637
-    }
638
-
639
-    /**
640
-     * Returns the statement of this query.
641
-     *
642
-     * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\Statement
643
-     */
644
-    public function getStatement()
645
-    {
646
-        return $this->statement;
647
-    }
648
-
649
-    /**
650
-     * Returns whether the current mode is Backend.
651
-     *
652
-     * @return bool
653
-     */
654
-    protected function isBackendMode()
655
-    {
656
-        return TYPO3_MODE == 'BE';
657
-    }
658
-
659
-    /**
660
-     * @return string
661
-     */
662
-    public function getSourceFieldName()
663
-    {
664
-        return $this->sourceFieldName;
665
-    }
666
-
667
-    /**
668
-     * @param string $sourceFieldName
669
-     * @return $this
670
-     */
671
-    public function setSourceFieldName($sourceFieldName)
672
-    {
673
-        $this->sourceFieldName = $sourceFieldName;
674
-        return $this;
675
-    }
32
+	/**
33
+	 * An inner join.
34
+	 */
35
+	const JCR_JOIN_TYPE_INNER = '{http://www.jcp.org/jcr/1.0}joinTypeInner';
36
+
37
+	/**
38
+	 * A left-outer join.
39
+	 */
40
+	const JCR_JOIN_TYPE_LEFT_OUTER = '{http://www.jcp.org/jcr/1.0}joinTypeLeftOuter';
41
+
42
+	/**
43
+	 * A right-outer join.
44
+	 */
45
+	const JCR_JOIN_TYPE_RIGHT_OUTER = '{http://www.jcp.org/jcr/1.0}joinTypeRightOuter';
46
+
47
+	/**
48
+	 * Charset of strings in QOM
49
+	 */
50
+	const CHARSET = 'utf-8';
51
+
52
+	/**
53
+	 * @var string
54
+	 */
55
+	protected $sourceFieldName;
56
+
57
+	/**
58
+	 * @var string
59
+	 */
60
+	protected $type;
61
+
62
+	/**
63
+	 * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
64
+	 */
65
+	protected $objectManager;
66
+
67
+	/**
68
+	 * @var \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface
69
+	 */
70
+	protected $persistenceManager;
71
+
72
+	/**
73
+	 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory
74
+	 */
75
+	protected $qomFactory;
76
+
77
+	/**
78
+	 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface
79
+	 */
80
+	protected $source;
81
+
82
+	/**
83
+	 * @var ConstraintInterface
84
+	 */
85
+	protected $constraint;
86
+
87
+	/**
88
+	 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Qom\Statement
89
+	 */
90
+	protected $statement;
91
+
92
+	/**
93
+	 * @var int
94
+	 */
95
+	protected $orderings = array();
96
+
97
+	/**
98
+	 * @var int
99
+	 */
100
+	protected $limit;
101
+
102
+	/**
103
+	 * @var int
104
+	 */
105
+	protected $offset;
106
+
107
+	/**
108
+	 * Apply DISTINCT upon property.
109
+	 *
110
+	 * @var string
111
+	 */
112
+	protected $distinct;
113
+
114
+	/**
115
+	 * The query settings.
116
+	 *
117
+	 * @var \Fab\Vidi\Persistence\QuerySettings
118
+	 * @inject
119
+	 */
120
+	protected $querySettings;
121
+
122
+	/**
123
+	 * Constructs a query object working on the given class name
124
+	 *
125
+	 * @param string $type
126
+	 */
127
+	public function __construct($type)
128
+	{
129
+		$this->type = $type;
130
+	}
131
+
132
+	/**
133
+	 * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
134
+	 * @return void
135
+	 */
136
+	public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
137
+	{
138
+		$this->objectManager = $objectManager;
139
+	}
140
+
141
+	/**
142
+	 * Injects the persistence manager, used to fetch the CR session
143
+	 *
144
+	 * @param \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager
145
+	 * @return void
146
+	 */
147
+	public function injectPersistenceManager(\TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager)
148
+	{
149
+		$this->persistenceManager = $persistenceManager;
150
+	}
151
+
152
+	/**
153
+	 * Injects the Query Object Model Factory
154
+	 *
155
+	 * @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory $qomFactory
156
+	 * @return void
157
+	 */
158
+	public function injectQomFactory(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory $qomFactory)
159
+	{
160
+		$this->qomFactory = $qomFactory;
161
+	}
162
+
163
+	/**
164
+	 * Sets the Query Settings. These Query settings must match the settings expected by
165
+	 * the specific Storage Backend.
166
+	 *
167
+	 * @param \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface $querySettings The Query Settings
168
+	 * @return void
169
+	 * @api This method is not part of FLOW3 API
170
+	 */
171
+	public function setQuerySettings(\TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface $querySettings)
172
+	{
173
+		$this->querySettings = $querySettings;
174
+	}
175
+
176
+	/**
177
+	 * Returns the Query Settings.
178
+	 *
179
+	 * @throws \Exception
180
+	 * @return \Fab\Vidi\Persistence\QuerySettings $querySettings The Query Settings
181
+	 * @api This method is not part of FLOW3 API
182
+	 */
183
+	public function getQuerySettings()
184
+	{
185
+		if (!$this->querySettings instanceof \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface) {
186
+			throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception('Tried to get the query settings without setting them before.', 1248689115);
187
+		}
188
+
189
+		// Apply possible settings to the query.
190
+		if ($this->isBackendMode()) {
191
+			/** @var \TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager $backendConfigurationManager */
192
+			$backendConfigurationManager = $this->objectManager->get('TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager');
193
+			$configuration = $backendConfigurationManager->getTypoScriptSetup();
194
+			$querySettings = array('respectSysLanguage');
195
+			foreach ($querySettings as $setting) {
196
+				if (isset($configuration['config.']['tx_vidi.']['persistence.']['backend.'][$this->type . '.'][$setting])) {
197
+					$value = (bool)$configuration['config.']['tx_vidi.']['persistence.']['backend.'][$this->type . '.'][$setting];
198
+					ObjectAccess::setProperty($this->querySettings, $setting, $value);
199
+				}
200
+			}
201
+		}
202
+
203
+		return $this->querySettings;
204
+	}
205
+
206
+	/**
207
+	 * Returns the type this query cares for.
208
+	 *
209
+	 * @return string
210
+	 * @api
211
+	 */
212
+	public function getType()
213
+	{
214
+		return $this->type;
215
+	}
216
+
217
+	/**
218
+	 * Sets the source to fetch the result from
219
+	 *
220
+	 * @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface $source
221
+	 */
222
+	public function setSource(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface $source)
223
+	{
224
+		$this->source = $source;
225
+	}
226
+
227
+	/**
228
+	 * Returns the selectorn name or an empty string, if the source is not a selector
229
+	 * TODO This has to be checked at another place
230
+	 *
231
+	 * @return string The selector name
232
+	 */
233
+	protected function getSelectorName()
234
+	{
235
+		if ($this->getSource() instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SelectorInterface) {
236
+			return $this->source->getSelectorName();
237
+		} else {
238
+			return '';
239
+		}
240
+	}
241
+
242
+	/**
243
+	 * Gets the node-tuple source for this query.
244
+	 *
245
+	 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface the node-tuple source; non-null
246
+	 */
247
+	public function getSource()
248
+	{
249
+		if ($this->source === NULL) {
250
+			$this->source = $this->qomFactory->selector($this->getType());
251
+		}
252
+		return $this->source;
253
+	}
254
+
255
+	/**
256
+	 * Executes the query against the database and returns the result
257
+	 *
258
+	 * @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface|array The query result object or an array if $this->getQuerySettings()->getReturnRawQueryResult() is TRUE
259
+	 * @api
260
+	 */
261
+	public function execute($returnRawQueryResult = FALSE)
262
+	{
263
+		/** @var \Fab\Vidi\Persistence\Storage\VidiDbBackend $backend */
264
+		$backend = $this->objectManager->get('Fab\Vidi\Persistence\Storage\VidiDbBackend', $this);
265
+		return $backend->fetchResult();
266
+	}
267
+
268
+	/**
269
+	 * Sets the property names to order the result by. Expected like this:
270
+	 * array(
271
+	 * 'foo' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING,
272
+	 * 'bar' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
273
+	 * )
274
+	 * where 'foo' and 'bar' are property names.
275
+	 *
276
+	 * @param array $orderings The property names to order by
277
+	 * @return QueryInterface
278
+	 * @api
279
+	 */
280
+	public function setOrderings(array $orderings)
281
+	{
282
+		$this->orderings = $orderings;
283
+		return $this;
284
+	}
285
+
286
+	/**
287
+	 * Returns the property names to order the result by. Like this:
288
+	 * array(
289
+	 * 'foo' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING,
290
+	 * 'bar' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
291
+	 * )
292
+	 *
293
+	 * @return array
294
+	 * @api
295
+	 */
296
+	public function getOrderings()
297
+	{
298
+		return $this->orderings;
299
+	}
300
+
301
+	/**
302
+	 * Sets the maximum size of the result set to limit. Returns $this to allow
303
+	 * for chaining (fluid interface)
304
+	 *
305
+	 * @param integer $limit
306
+	 * @throws \InvalidArgumentException
307
+	 * @return QueryInterface
308
+	 * @api
309
+	 */
310
+	public function setLimit($limit)
311
+	{
312
+		if (!is_int($limit) || $limit < 1) {
313
+			throw new \InvalidArgumentException('The limit must be an integer >= 1', 1245071870);
314
+		}
315
+		$this->limit = $limit;
316
+		return $this;
317
+	}
318
+
319
+	/**
320
+	 * Resets a previously set maximum size of the result set. Returns $this to allow
321
+	 * for chaining (fluid interface)
322
+	 *
323
+	 * @return QueryInterface
324
+	 * @api
325
+	 */
326
+	public function unsetLimit()
327
+	{
328
+		unset($this->limit);
329
+		return $this;
330
+	}
331
+
332
+	/**
333
+	 * Returns the maximum size of the result set to limit.
334
+	 *
335
+	 * @return integer
336
+	 * @api
337
+	 */
338
+	public function getLimit()
339
+	{
340
+		return $this->limit;
341
+	}
342
+
343
+	/**
344
+	 * Sets the start offset of the result set to offset. Returns $this to
345
+	 * allow for chaining (fluid interface)
346
+	 *
347
+	 * @param integer $offset
348
+	 * @throws \InvalidArgumentException
349
+	 * @return QueryInterface
350
+	 * @api
351
+	 */
352
+	public function setOffset($offset)
353
+	{
354
+		if (!is_int($offset) || $offset < 0) {
355
+			throw new \InvalidArgumentException('The offset must be a positive integer', 1245071872);
356
+		}
357
+		$this->offset = $offset;
358
+		return $this;
359
+	}
360
+
361
+	/**
362
+	 * Returns the start offset of the result set.
363
+	 *
364
+	 * @return integer
365
+	 * @api
366
+	 */
367
+	public function getOffset()
368
+	{
369
+		return $this->offset;
370
+	}
371
+
372
+	/**
373
+	 * The constraint used to limit the result set. Returns $this to allow
374
+	 * for chaining (fluid interface)
375
+	 *
376
+	 * @param ConstraintInterface $constraint
377
+	 * @return QueryInterface
378
+	 * @api
379
+	 */
380
+	public function matching($constraint)
381
+	{
382
+		$this->constraint = $constraint;
383
+		return $this;
384
+	}
385
+
386
+	/**
387
+	 * Gets the constraint for this query.
388
+	 *
389
+	 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\Constraint the constraint, or null if none
390
+	 * @api
391
+	 */
392
+	public function getConstraint()
393
+	{
394
+		return $this->constraint;
395
+	}
396
+
397
+	/**
398
+	 * Performs a logical conjunction of the given constraints. The method takes one or more contraints and concatenates them with a boolean AND.
399
+	 * It also scepts a single array of constraints to be concatenated.
400
+	 *
401
+	 * @param mixed $constraint1 The first of multiple constraints or an array of constraints.
402
+	 * @throws InvalidNumberOfConstraintsException
403
+	 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\AndInterface
404
+	 * @api
405
+	 */
406
+	public function logicalAnd($constraint1)
407
+	{
408
+		if (is_array($constraint1)) {
409
+			$resultingConstraint = array_shift($constraint1);
410
+			$constraints = $constraint1;
411
+		} else {
412
+			$constraints = func_get_args();
413
+			$resultingConstraint = array_shift($constraints);
414
+		}
415
+		if ($resultingConstraint === NULL) {
416
+			throw new InvalidNumberOfConstraintsException('There must be at least one constraint or a non-empty array of constraints given.', 1401289500);
417
+		}
418
+		foreach ($constraints as $constraint) {
419
+			$resultingConstraint = $this->qomFactory->_and($resultingConstraint, $constraint);
420
+		}
421
+		return $resultingConstraint;
422
+	}
423
+
424
+	/**
425
+	 * Performs a logical disjunction of the two given constraints
426
+	 *
427
+	 * @param mixed $constraint1 The first of multiple constraints or an array of constraints.
428
+	 * @throws InvalidNumberOfConstraintsException
429
+	 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\OrInterface
430
+	 * @api
431
+	 */
432
+	public function logicalOr($constraint1)
433
+	{
434
+		if (is_array($constraint1)) {
435
+			$resultingConstraint = array_shift($constraint1);
436
+			$constraints = $constraint1;
437
+		} else {
438
+			$constraints = func_get_args();
439
+			$resultingConstraint = array_shift($constraints);
440
+		}
441
+		if ($resultingConstraint === NULL) {
442
+			throw new InvalidNumberOfConstraintsException('There must be at least one constraint or a non-empty array of constraints given.', 1401289501);
443
+		}
444
+		foreach ($constraints as $constraint) {
445
+			$resultingConstraint = $this->qomFactory->_or($resultingConstraint, $constraint);
446
+		}
447
+		return $resultingConstraint;
448
+	}
449
+
450
+	/**
451
+	 * Performs a logical negation of the given constraint
452
+	 *
453
+	 * @param ConstraintInterface $constraint Constraint to negate
454
+	 * @throws \RuntimeException
455
+	 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\NotInterface
456
+	 * @api
457
+	 */
458
+	public function logicalNot(ConstraintInterface $constraint)
459
+	{
460
+		return $this->qomFactory->not($constraint);
461
+	}
462
+
463
+	/**
464
+	 * Returns an equals criterion used for matching objects against a query
465
+	 *
466
+	 * @param string $propertyName The name of the property to compare against
467
+	 * @param mixed $operand The value to compare with
468
+	 * @param boolean $caseSensitive Whether the equality test should be done case-sensitive
469
+	 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
470
+	 * @api
471
+	 */
472
+	public function equals($propertyName, $operand, $caseSensitive = TRUE)
473
+	{
474
+		if (is_object($operand) || $caseSensitive) {
475
+			$comparison = $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_EQUAL_TO, $operand);
476
+		} else {
477
+			$comparison = $this->qomFactory->comparison($this->qomFactory->lowerCase($this->qomFactory->propertyValue($propertyName, $this->getSelectorName())), QueryInterface::OPERATOR_EQUAL_TO, \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Charset\\CharsetConverter')->conv_case(\TYPO3\CMS\Extbase\Persistence\Generic\Query::CHARSET, $operand, 'toLower'));
478
+		}
479
+		return $comparison;
480
+	}
481
+
482
+	/**
483
+	 * Returns a like criterion used for matching objects against a query
484
+	 *
485
+	 * @param string $propertyName The name of the property to compare against
486
+	 * @param mixed $operand The value to compare with
487
+	 * @param boolean $caseSensitive Whether the matching should be done case-sensitive
488
+	 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
489
+	 * @api
490
+	 */
491
+	public function like($propertyName, $operand, $caseSensitive = TRUE)
492
+	{
493
+		return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_LIKE, $operand);
494
+	}
495
+
496
+	/**
497
+	 * Returns a "contains" criterion used for matching objects against a query.
498
+	 * It matches if the multivalued property contains the given operand.
499
+	 *
500
+	 * @param string $propertyName The name of the (multivalued) property to compare against
501
+	 * @param mixed $operand The value to compare with
502
+	 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
503
+	 * @api
504
+	 */
505
+	public function contains($propertyName, $operand)
506
+	{
507
+		return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_CONTAINS, $operand);
508
+	}
509
+
510
+	/**
511
+	 * Returns an "in" criterion used for matching objects against a query. It
512
+	 * matches if the property's value is contained in the multivalued operand.
513
+	 *
514
+	 * @param string $propertyName The name of the property to compare against
515
+	 * @param mixed $operand The value to compare with, multivalued
516
+	 * @throws UnexpectedTypeException
517
+	 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
518
+	 * @api
519
+	 */
520
+	public function in($propertyName, $operand)
521
+	{
522
+		if (!is_array($operand) && !$operand instanceof \ArrayAccess && !$operand instanceof \Traversable) {
523
+			throw new UnexpectedTypeException('The "in" operator must be given a mutlivalued operand (array, ArrayAccess, Traversable).', 1264678095);
524
+		}
525
+		return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_IN, $operand);
526
+	}
527
+
528
+	/**
529
+	 * Returns a less than criterion used for matching objects against a query
530
+	 *
531
+	 * @param string $propertyName The name of the property to compare against
532
+	 * @param mixed $operand The value to compare with
533
+	 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
534
+	 * @api
535
+	 */
536
+	public function lessThan($propertyName, $operand)
537
+	{
538
+		return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_LESS_THAN, $operand);
539
+	}
540
+
541
+	/**
542
+	 * Returns a less or equal than criterion used for matching objects against a query
543
+	 *
544
+	 * @param string $propertyName The name of the property to compare against
545
+	 * @param mixed $operand The value to compare with
546
+	 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
547
+	 * @api
548
+	 */
549
+	public function lessThanOrEqual($propertyName, $operand)
550
+	{
551
+		return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_LESS_THAN_OR_EQUAL_TO, $operand);
552
+	}
553
+
554
+	/**
555
+	 * Returns a greater than criterion used for matching objects against a query
556
+	 *
557
+	 * @param string $propertyName The name of the property to compare against
558
+	 * @param mixed $operand The value to compare with
559
+	 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
560
+	 * @api
561
+	 */
562
+	public function greaterThan($propertyName, $operand)
563
+	{
564
+		return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_GREATER_THAN, $operand);
565
+	}
566
+
567
+	/**
568
+	 * Returns a greater than or equal criterion used for matching objects against a query
569
+	 *
570
+	 * @param string $propertyName The name of the property to compare against
571
+	 * @param mixed $operand The value to compare with
572
+	 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
573
+	 * @api
574
+	 */
575
+	public function greaterThanOrEqual($propertyName, $operand)
576
+	{
577
+		return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_GREATER_THAN_OR_EQUAL_TO, $operand);
578
+	}
579
+
580
+	/**
581
+	 * Returns the query result count.
582
+	 *
583
+	 * @return integer The query result count
584
+	 * @api
585
+	 */
586
+	public function count()
587
+	{
588
+		/** @var \Fab\Vidi\Persistence\Storage\VidiDbBackend $backend */
589
+		$backend = $this->objectManager->get('Fab\Vidi\Persistence\Storage\VidiDbBackend', $this);
590
+		return $backend->countResult();
591
+	}
592
+
593
+	/**
594
+	 * Returns an "isEmpty" criterion used for matching objects against a query.
595
+	 * It matches if the multivalued property contains no values or is NULL.
596
+	 *
597
+	 * @param string $propertyName The name of the multivalued property to compare against
598
+	 * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\NotImplementedException
599
+	 * @throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException if used on a single-valued property
600
+	 * @api
601
+	 */
602
+	public function isEmpty($propertyName)
603
+	{
604
+		throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\NotImplementedException(__METHOD__);
605
+	}
606
+
607
+	/**
608
+	 * @return string
609
+	 */
610
+	public function getDistinct()
611
+	{
612
+		return $this->distinct;
613
+	}
614
+
615
+	/**
616
+	 * @param string $distinct
617
+	 * @return $this
618
+	 */
619
+	public function setDistinct($distinct)
620
+	{
621
+		$this->distinct = $distinct;
622
+		return $this;
623
+	}
624
+
625
+	/**
626
+	 * Sets the statement of this query. If you use this, you will lose the abstraction from a concrete storage
627
+	 * backend (database).
628
+	 *
629
+	 * @param string|\TYPO3\CMS\Core\Database\PreparedStatement $statement The statement
630
+	 * @param array $parameters An array of parameters. These will be bound to placeholders '?' in the $statement.
631
+	 * @return QueryInterface
632
+	 */
633
+	public function statement($statement, array $parameters = array())
634
+	{
635
+		$this->statement = $this->qomFactory->statement($statement, $parameters);
636
+		return $this;
637
+	}
638
+
639
+	/**
640
+	 * Returns the statement of this query.
641
+	 *
642
+	 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\Statement
643
+	 */
644
+	public function getStatement()
645
+	{
646
+		return $this->statement;
647
+	}
648
+
649
+	/**
650
+	 * Returns whether the current mode is Backend.
651
+	 *
652
+	 * @return bool
653
+	 */
654
+	protected function isBackendMode()
655
+	{
656
+		return TYPO3_MODE == 'BE';
657
+	}
658
+
659
+	/**
660
+	 * @return string
661
+	 */
662
+	public function getSourceFieldName()
663
+	{
664
+		return $this->sourceFieldName;
665
+	}
666
+
667
+	/**
668
+	 * @param string $sourceFieldName
669
+	 * @return $this
670
+	 */
671
+	public function setSourceFieldName($sourceFieldName)
672
+	{
673
+		$this->sourceFieldName = $sourceFieldName;
674
+		return $this;
675
+	}
676 676
 
677 677
 }
Please login to merge, or discard this patch.
Classes/Persistence/Storage/VidiDbBackend.php 3 patches
Doc Comments   +3 added lines, -2 removed lines patch added patch discarded remove patch
@@ -231,7 +231,7 @@  discard block
 block discarded – undo
231 231
     /**
232 232
      * Parses the query and returns the SQL statement parts.
233 233
      *
234
-     * @param QueryInterface $query The query
234
+     * @param \Fab\Vidi\Persistence\Query $query The query
235 235
      * @param array &$parameters
236 236
      * @return array The SQL statement parts
237 237
      */
@@ -774,6 +774,7 @@  discard block
 block discarded – undo
774 774
      * @param string &$sqlString The query part with placeholders
775 775
      * @param array $parameters The parameters
776 776
      * @param string $tableName
777
+     * @param string $sqlString
777 778
      *
778 779
      * @throws Exception
779 780
      */
@@ -1071,7 +1072,7 @@  discard block
 block discarded – undo
1071 1072
      *
1072 1073
      * @param SourceInterface $source The source (selector od join)
1073 1074
      * @param array $row
1074
-     * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
1075
+     * @param \Fab\Vidi\Persistence\QuerySettings $querySettings The TYPO3 CMS specific query settings
1075 1076
      * @return array
1076 1077
      */
1077 1078
     protected function doLanguageAndWorkspaceOverlay(SourceInterface $source, array $row, $querySettings)
Please login to merge, or discard this patch.
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -18,7 +18,6 @@
 block discarded – undo
18 18
 use TYPO3\CMS\Core\Utility\GeneralUtility;
19 19
 use TYPO3\CMS\Core\Versioning\VersionState;
20 20
 use TYPO3\CMS\Extbase\Persistence\Generic\Exception;
21
-use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap;
22 21
 use TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface;
23 22
 use TYPO3\CMS\Extbase\Persistence\Generic\Qom\DynamicOperandInterface;
24 23
 use TYPO3\CMS\Extbase\Persistence\Generic\Qom\JoinInterface;
Please login to merge, or discard this patch.
Indentation   +1207 added lines, -1207 removed lines patch added patch discarded remove patch
@@ -38,1211 +38,1211 @@
 block discarded – undo
38 38
 class VidiDbBackend
39 39
 {
40 40
 
41
-    const OPERATOR_EQUAL_TO_NULL = 'operatorEqualToNull';
42
-    const OPERATOR_NOT_EQUAL_TO_NULL = 'operatorNotEqualToNull';
43
-
44
-    /**
45
-     * The TYPO3 database object
46
-     *
47
-     * @var \TYPO3\CMS\Core\Database\DatabaseConnection
48
-     */
49
-    protected $databaseHandle;
50
-
51
-    /**
52
-     * The TYPO3 page repository. Used for language and workspace overlay
53
-     *
54
-     * @var PageRepository
55
-     */
56
-    protected $pageRepository;
57
-
58
-    /**
59
-     * A first-level TypoScript configuration cache
60
-     *
61
-     * @var array
62
-     */
63
-    protected $pageTSConfigCache = array();
64
-
65
-    /**
66
-     * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
67
-     * @inject
68
-     */
69
-    protected $configurationManager;
70
-
71
-    /**
72
-     * @var \TYPO3\CMS\Extbase\Service\CacheService
73
-     * @inject
74
-     */
75
-    protected $cacheService;
76
-
77
-    /**
78
-     * @var \TYPO3\CMS\Core\Cache\CacheManager
79
-     * @inject
80
-     */
81
-    protected $cacheManager;
82
-
83
-    /**
84
-     * @var \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend
85
-     */
86
-    protected $tableColumnCache;
87
-
88
-    /**
89
-     * @var \TYPO3\CMS\Extbase\Service\EnvironmentService
90
-     * @inject
91
-     */
92
-    protected $environmentService;
93
-
94
-    /**
95
-     * @var \Fab\Vidi\Persistence\Query
96
-     */
97
-    protected $query;
98
-
99
-    /**
100
-     * Store some info related to table name and its aliases.
101
-     *
102
-     * @var array
103
-     */
104
-    protected $tableNameAliases = array(
105
-        'aliases' => array(),
106
-        'aliasIncrement' => array(),
107
-    );
108
-
109
-    /**
110
-     * Use to store the current foreign table name alias.
111
-     *
112
-     * @var string
113
-     */
114
-    protected $currentChildTableNameAlias = '';
115
-
116
-    /**
117
-     * The default object type being returned.
118
-     *
119
-     * @var string
120
-     */
121
-    protected $objectType = 'Fab\Vidi\Domain\Model\Content';
122
-
123
-    /**
124
-     * Constructor. takes the database handle from $GLOBALS['TYPO3_DB']
125
-     */
126
-    public function __construct(QueryInterface $query)
127
-    {
128
-        $this->query = $query;
129
-        $this->databaseHandle = $GLOBALS['TYPO3_DB'];
130
-    }
131
-
132
-    /**
133
-     * Lifecycle method
134
-     *
135
-     * @return void
136
-     */
137
-    public function initializeObject()
138
-    {
139
-        $this->tableColumnCache = $this->cacheManager->getCache('extbase_typo3dbbackend_tablecolumns');
140
-    }
141
-
142
-    /**
143
-     * @param array $identifier
144
-     * @return string
145
-     */
146
-    protected function parseIdentifier(array $identifier)
147
-    {
148
-        $fieldNames = array_keys($identifier);
149
-        $suffixedFieldNames = array();
150
-        foreach ($fieldNames as $fieldName) {
151
-            $suffixedFieldNames[] = $fieldName . '=?';
152
-        }
153
-        return implode(' AND ', $suffixedFieldNames);
154
-    }
155
-
156
-    /**
157
-     * Returns the result of the query
158
-     */
159
-    public function fetchResult()
160
-    {
161
-
162
-        $parameters = array();
163
-        $statementParts = $this->parseQuery($this->query, $parameters);
164
-        $statementParts = $this->processStatementStructureForRecursiveMMRelation($statementParts); // Mmm... check if that is the right way of doing that.
165
-
166
-        $sql = $this->buildQuery($statementParts);
167
-        $tableName = '';
168
-        if (is_array($statementParts) && !empty($statementParts['tables'][0])) {
169
-            $tableName = $statementParts['tables'][0];
170
-        }
171
-        $this->replacePlaceholders($sql, $parameters, $tableName);
172
-        #print $sql; exit(); // @debug
173
-
174
-        $result = $this->databaseHandle->sql_query($sql);
175
-        $this->checkSqlErrors($sql);
176
-        $rows = $this->getRowsFromResult($result);
177
-        $this->databaseHandle->sql_free_result($result);
178
-
179
-        return $rows;
180
-    }
181
-
182
-    /**
183
-     * Returns the number of tuples matching the query.
184
-     *
185
-     * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Storage\Exception\BadConstraintException
186
-     * @return int The number of matching tuples
187
-     */
188
-    public function countResult()
189
-    {
190
-
191
-        $parameters = array();
192
-        $statementParts = $this->parseQuery($this->query, $parameters);
193
-        $statementParts = $this->processStatementStructureForRecursiveMMRelation($statementParts); // Mmm... check if that is the right way of doing that.
194
-        // Reset $statementParts for valid table return
195
-        reset($statementParts);
196
-
197
-        // if limit is set, we need to count the rows "manually" as COUNT(*) ignores LIMIT constraints
198
-        if (!empty($statementParts['limit'])) {
199
-            $statement = $this->buildQuery($statementParts);
200
-            $this->replacePlaceholders($statement, $parameters, current($statementParts['tables']));
201
-            #print $statement; exit(); // @debug
202
-            $result = $this->databaseHandle->sql_query($statement);
203
-            $this->checkSqlErrors($statement);
204
-            $count = $this->databaseHandle->sql_num_rows($result);
205
-        } else {
206
-            $statementParts['fields'] = array('COUNT(*)');
207
-            // having orderings without grouping is not compatible with non-MySQL DBMS
208
-            $statementParts['orderings'] = array();
209
-            if (isset($statementParts['keywords']['distinct'])) {
210
-                unset($statementParts['keywords']['distinct']);
211
-                $distinctField = $this->query->getDistinct() ? $this->query->getDistinct() : 'uid';
212
-                $statementParts['fields'] = array('COUNT(DISTINCT ' . reset($statementParts['tables']) . '.' . $distinctField . ')');
213
-            }
214
-
215
-            $statement = $this->buildQuery($statementParts);
216
-            $this->replacePlaceholders($statement, $parameters, current($statementParts['tables']));
217
-
218
-            #print $statement; exit(); // @debug
219
-            $result = $this->databaseHandle->sql_query($statement);
220
-            $this->checkSqlErrors($statement);
221
-            $count = 0;
222
-            if ($result) {
223
-                $row = $this->databaseHandle->sql_fetch_assoc($result);
224
-                $count = current($row);
225
-            }
226
-        }
227
-        $this->databaseHandle->sql_free_result($result);
228
-        return (int)$count;
229
-    }
230
-
231
-    /**
232
-     * Parses the query and returns the SQL statement parts.
233
-     *
234
-     * @param QueryInterface $query The query
235
-     * @param array &$parameters
236
-     * @return array The SQL statement parts
237
-     */
238
-    public function parseQuery(QueryInterface $query, array &$parameters)
239
-    {
240
-        $statementParts = array();
241
-        $statementParts['keywords'] = array();
242
-        $statementParts['tables'] = array();
243
-        $statementParts['unions'] = array();
244
-        $statementParts['fields'] = array();
245
-        $statementParts['where'] = array();
246
-        $statementParts['additionalWhereClause'] = array();
247
-        $statementParts['orderings'] = array();
248
-        $statementParts['limit'] = array();
249
-        $source = $query->getSource();
250
-        $this->parseSource($source, $statementParts);
251
-        $this->parseConstraint($query->getConstraint(), $source, $statementParts, $parameters);
252
-        $this->parseOrderings($query->getOrderings(), $source, $statementParts);
253
-        $this->parseLimitAndOffset($query->getLimit(), $query->getOffset(), $statementParts);
254
-        $tableNames = array_unique(array_keys($statementParts['tables'] + $statementParts['unions']));
255
-        foreach ($tableNames as $tableNameOrAlias) {
256
-            if (is_string($tableNameOrAlias) && strlen($tableNameOrAlias) > 0) {
257
-                $this->addAdditionalWhereClause($query->getQuerySettings(), $tableNameOrAlias, $statementParts);
258
-            }
259
-        }
260
-
261
-        return $statementParts;
262
-    }
263
-
264
-    /**
265
-     * Fiddle with the statement structure to handle recursive MM relations.
266
-     * For the recursive MM query to work, we must invert some values.
267
-     * Let see if that is the best way of doing that...
268
-     *
269
-     * @param array $statementParts
270
-     * @return array
271
-     */
272
-    public function processStatementStructureForRecursiveMMRelation(array $statementParts)
273
-    {
274
-
275
-        if ($this->hasRecursiveMMRelation()) {
276
-            $tableName = $this->query->getType();
277
-
278
-            // In order the MM query to work for a recursive MM query, we must invert some values.
279
-            // tx_domain_model_foo0 (the alias) <--> tx_domain_model_foo (the origin table name)
280
-            $values = array();
281
-            foreach ($statementParts['fields'] as $key => $value) {
282
-                $values[$key] = str_replace($tableName, $tableName . '0', $value);
283
-            }
284
-            $statementParts['fields'] = $values;
285
-
286
-            // Same comment as above.
287
-            $values = array();
288
-            foreach ($statementParts['where'] as $key => $value) {
289
-                $values[$key] = str_replace($tableName . '0', $tableName, $value);
290
-            }
291
-            $statementParts['where'] = $values;
292
-
293
-            // We must be more restrictive by transforming the "left" union by "inner"
294
-            $values = array();
295
-            foreach ($statementParts['unions'] as $key => $value) {
296
-                $values[$key] = str_replace('LEFT JOIN', 'INNER JOIN', $value);
297
-            }
298
-            $statementParts['unions'] = $values;
299
-        }
300
-
301
-        return $statementParts;
302
-    }
303
-
304
-    /**
305
-     * Tell whether there is a recursive MM relation.
306
-     *
307
-     * @return bool
308
-     */
309
-    public function hasRecursiveMMRelation()
310
-    {
311
-        return isset($this->tableNameAliases['aliasIncrement'][$this->query->getType()]);
312
-
313
-    }
314
-
315
-    /**
316
-     * Returns the statement, ready to be executed.
317
-     *
318
-     * @param array $statementParts The SQL statement parts
319
-     * @return string The SQL statement
320
-     */
321
-    public function buildQuery(array $statementParts)
322
-    {
323
-
324
-        // Add more statement to the UNION part.
325
-        if (!empty($statementParts['unions'])) {
326
-            foreach ($statementParts['unions'] as $tableName => $unionPart) {
327
-                if (!empty($statementParts['additionalWhereClause'][$tableName])) {
328
-                    $statementParts['unions'][$tableName] .= ' AND ' . implode(' AND ', $statementParts['additionalWhereClause'][$tableName]);
329
-                }
330
-            }
331
-        }
332
-
333
-        $statement = 'SELECT ' . implode(' ', $statementParts['keywords']) . ' ' . implode(',', $statementParts['fields']) . ' FROM ' . implode(' ', $statementParts['tables']) . ' ' . implode(' ', $statementParts['unions']);
334
-        if (!empty($statementParts['where'])) {
335
-            $statement .= ' WHERE ' . implode('', $statementParts['where']);
336
-            if (!empty($statementParts['additionalWhereClause'][$this->query->getType()])) {
337
-                $statement .= ' AND ' . implode(' AND ', $statementParts['additionalWhereClause'][$this->query->getType()]);
338
-            }
339
-        } elseif (!empty($statementParts['additionalWhereClause'])) {
340
-            $statement .= ' WHERE ' . implode(' AND ', $statementParts['additionalWhereClause'][$this->query->getType()]);
341
-        }
342
-        if (!empty($statementParts['orderings'])) {
343
-            $statement .= ' ORDER BY ' . implode(', ', $statementParts['orderings']);
344
-        }
345
-        if (!empty($statementParts['limit'])) {
346
-            $statement .= ' LIMIT ' . $statementParts['limit'];
347
-        }
348
-
349
-        return $statement;
350
-    }
351
-
352
-    /**
353
-     * Transforms a Query Source into SQL and parameter arrays
354
-     *
355
-     * @param SourceInterface $source The source
356
-     * @param array &$sql
357
-     * @return void
358
-     */
359
-    protected function parseSource(SourceInterface $source, array &$sql)
360
-    {
361
-        if ($source instanceof SelectorInterface) {
362
-            $tableName = $source->getNodeTypeName();
363
-            $sql['fields'][$tableName] = $tableName . '.*';
364
-            $sql['tables'][$tableName] = $tableName;
365
-            if ($this->query->getDistinct()) {
366
-                $sql['fields'][$tableName] = $tableName . '.' . $this->query->getDistinct();
367
-                $sql['keywords']['distinct'] = 'DISTINCT';
368
-            }
369
-        } elseif ($source instanceof JoinInterface) {
370
-            $this->parseJoin($source, $sql);
371
-        }
372
-    }
373
-
374
-    /**
375
-     * Transforms a Join into SQL and parameter arrays
376
-     *
377
-     * @param JoinInterface $join The join
378
-     * @param array &$sql The query parts
379
-     * @return void
380
-     */
381
-    protected function parseJoin(JoinInterface $join, array &$sql)
382
-    {
383
-        $leftSource = $join->getLeft();
384
-        $leftTableName = $leftSource->getSelectorName();
385
-        // $sql['fields'][$leftTableName] = $leftTableName . '.*';
386
-        $rightSource = $join->getRight();
387
-        if ($rightSource instanceof JoinInterface) {
388
-            $rightTableName = $rightSource->getLeft()->getSelectorName();
389
-        } else {
390
-            $rightTableName = $rightSource->getSelectorName();
391
-            $sql['fields'][$leftTableName] = $rightTableName . '.*';
392
-        }
393
-        $sql['tables'][$leftTableName] = $leftTableName;
394
-        $sql['unions'][$rightTableName] = 'LEFT JOIN ' . $rightTableName;
395
-        $joinCondition = $join->getJoinCondition();
396
-        if ($joinCondition instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\EquiJoinCondition) {
397
-            $column1Name = $joinCondition->getProperty1Name();
398
-            $column2Name = $joinCondition->getProperty2Name();
399
-            $sql['unions'][$rightTableName] .= ' ON ' . $joinCondition->getSelector1Name() . '.' . $column1Name . ' = ' . $joinCondition->getSelector2Name() . '.' . $column2Name;
400
-        }
401
-        if ($rightSource instanceof JoinInterface) {
402
-            $this->parseJoin($rightSource, $sql);
403
-        }
404
-    }
405
-
406
-    /**
407
-     * Transforms a constraint into SQL and parameter arrays
408
-     *
409
-     * @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface $constraint The constraint
410
-     * @param SourceInterface $source The source
411
-     * @param array &$sql The query parts
412
-     * @param array &$parameters The parameters that will replace the markers
413
-     * @return void
414
-     */
415
-    protected function parseConstraint(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface $constraint = NULL, SourceInterface $source, array &$sql, array &$parameters)
416
-    {
417
-        if ($constraint instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\AndInterface) {
418
-            $sql['where'][] = '(';
419
-            $this->parseConstraint($constraint->getConstraint1(), $source, $sql, $parameters);
420
-            $sql['where'][] = ' AND ';
421
-            $this->parseConstraint($constraint->getConstraint2(), $source, $sql, $parameters);
422
-            $sql['where'][] = ')';
423
-        } elseif ($constraint instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\OrInterface) {
424
-            $sql['where'][] = '(';
425
-            $this->parseConstraint($constraint->getConstraint1(), $source, $sql, $parameters);
426
-            $sql['where'][] = ' OR ';
427
-            $this->parseConstraint($constraint->getConstraint2(), $source, $sql, $parameters);
428
-            $sql['where'][] = ')';
429
-        } elseif ($constraint instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\NotInterface) {
430
-            $sql['where'][] = 'NOT (';
431
-            $this->parseConstraint($constraint->getConstraint(), $source, $sql, $parameters);
432
-            $sql['where'][] = ')';
433
-        } elseif ($constraint instanceof ComparisonInterface) {
434
-            $this->parseComparison($constraint, $source, $sql, $parameters);
435
-        }
436
-    }
437
-
438
-    /**
439
-     * Parse a Comparison into SQL and parameter arrays.
440
-     *
441
-     * @param ComparisonInterface $comparison The comparison to parse
442
-     * @param SourceInterface $source The source
443
-     * @param array &$sql SQL query parts to add to
444
-     * @param array &$parameters Parameters to bind to the SQL
445
-     * @throws Exception\RepositoryException
446
-     * @return void
447
-     */
448
-    protected function parseComparison(ComparisonInterface $comparison, SourceInterface $source, array &$sql, array &$parameters)
449
-    {
450
-        $operand1 = $comparison->getOperand1();
451
-        $operator = $comparison->getOperator();
452
-        $operand2 = $comparison->getOperand2();
453
-        if ($operator === QueryInterface::OPERATOR_IN) {
454
-            $items = array();
455
-            $hasValue = FALSE;
456
-            foreach ($operand2 as $value) {
457
-                $value = $this->getPlainValue($value);
458
-                if ($value !== NULL) {
459
-                    $items[] = $value;
460
-                    $hasValue = TRUE;
461
-                }
462
-            }
463
-            if ($hasValue === FALSE) {
464
-                $sql['where'][] = '1<>1';
465
-            } else {
466
-                $this->parseDynamicOperand($operand1, $operator, $source, $sql, $parameters, NULL);
467
-                $parameters[] = $items;
468
-            }
469
-        } elseif ($operator === QueryInterface::OPERATOR_CONTAINS) {
470
-            if ($operand2 === NULL) {
471
-                $sql['where'][] = '1<>1';
472
-            } else {
473
-                throw new \Exception('Not implemented! Contact extension author.', 1412931227);
474
-                # @todo re-implement me if necessary.
475
-                #$tableName = $this->query->getType();
476
-                #$propertyName = $operand1->getPropertyName();
477
-                #while (strpos($propertyName, '.') !== FALSE) {
478
-                #	$this->addUnionStatement($tableName, $propertyName, $sql);
479
-                #}
480
-                #$columnName = $propertyName;
481
-                #$columnMap = $propertyName;
482
-                #$typeOfRelation = $columnMap instanceof ColumnMap ? $columnMap->getTypeOfRelation() : NULL;
483
-                #if ($typeOfRelation === ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
484
-                #	$relationTableName = $columnMap->getRelationTableName();
485
-                #	$sql['where'][] = $tableName . '.uid IN (SELECT ' . $columnMap->getParentKeyFieldName() . ' FROM ' . $relationTableName . ' WHERE ' . $columnMap->getChildKeyFieldName() . '=?)';
486
-                #	$parameters[] = intval($this->getPlainValue($operand2));
487
-                #} elseif ($typeOfRelation === ColumnMap::RELATION_HAS_MANY) {
488
-                #	$parentKeyFieldName = $columnMap->getParentKeyFieldName();
489
-                #	if (isset($parentKeyFieldName)) {
490
-                #		$childTableName = $columnMap->getChildTableName();
491
-                #		$sql['where'][] = $tableName . '.uid=(SELECT ' . $childTableName . '.' . $parentKeyFieldName . ' FROM ' . $childTableName . ' WHERE ' . $childTableName . '.uid=?)';
492
-                #		$parameters[] = intval($this->getPlainValue($operand2));
493
-                #	} else {
494
-                #		$sql['where'][] = 'FIND_IN_SET(?,' . $tableName . '.' . $columnName . ')';
495
-                #		$parameters[] = intval($this->getPlainValue($operand2));
496
-                #	}
497
-                #} else {
498
-                #	throw new Exception\RepositoryException('Unsupported or non-existing property name "' . $propertyName . '" used in relation matching.', 1327065745);
499
-                #}
500
-            }
501
-        } else {
502
-            if ($operand2 === NULL) {
503
-                if ($operator === QueryInterface::OPERATOR_EQUAL_TO) {
504
-                    $operator = self::OPERATOR_EQUAL_TO_NULL;
505
-                } elseif ($operator === QueryInterface::OPERATOR_NOT_EQUAL_TO) {
506
-                    $operator = self::OPERATOR_NOT_EQUAL_TO_NULL;
507
-                }
508
-            }
509
-            $this->parseDynamicOperand($operand1, $operator, $source, $sql, $parameters);
510
-            $parameters[] = $this->getPlainValue($operand2);
511
-        }
512
-    }
513
-
514
-    /**
515
-     * Returns a plain value, i.e. objects are flattened out if possible.
516
-     *
517
-     * @param mixed $input
518
-     * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnexpectedTypeException
519
-     * @return mixed
520
-     */
521
-    protected function getPlainValue($input)
522
-    {
523
-        if (is_array($input)) {
524
-            throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnexpectedTypeException('An array could not be converted to a plain value.', 1274799932);
525
-        }
526
-        if ($input instanceof \DateTime) {
527
-            return $input->format('U');
528
-        } elseif (is_object($input)) {
529
-            if ($input instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy) {
530
-                $realInput = $input->_loadRealInstance();
531
-            } else {
532
-                $realInput = $input;
533
-            }
534
-            if ($realInput instanceof \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface) {
535
-                return $realInput->getUid();
536
-            } else {
537
-                throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnexpectedTypeException('An object of class "' . get_class($realInput) . '" could not be converted to a plain value.', 1274799934);
538
-            }
539
-        } elseif (is_bool($input)) {
540
-            return $input === TRUE ? 1 : 0;
541
-        } else {
542
-            return $input;
543
-        }
544
-    }
545
-
546
-    /**
547
-     * Parse a DynamicOperand into SQL and parameter arrays.
548
-     *
549
-     * @param DynamicOperandInterface $operand
550
-     * @param string $operator One of the JCR_OPERATOR_* constants
551
-     * @param SourceInterface $source The source
552
-     * @param array &$sql The query parts
553
-     * @param array &$parameters The parameters that will replace the markers
554
-     * @param string $valueFunction an optional SQL function to apply to the operand value
555
-     * @return void
556
-     */
557
-    protected function parseDynamicOperand(DynamicOperandInterface $operand, $operator, SourceInterface $source, array &$sql, array &$parameters, $valueFunction = NULL)
558
-    {
559
-        if ($operand instanceof LowerCaseInterface) {
560
-            $this->parseDynamicOperand($operand->getOperand(), $operator, $source, $sql, $parameters, 'LOWER');
561
-        } elseif ($operand instanceof UpperCaseInterface) {
562
-            $this->parseDynamicOperand($operand->getOperand(), $operator, $source, $sql, $parameters, 'UPPER');
563
-        } elseif ($operand instanceof PropertyValueInterface) {
564
-            $propertyName = $operand->getPropertyName();
565
-
566
-            // Reset value.
567
-            $this->currentChildTableNameAlias = '';
568
-
569
-            if ($source instanceof SelectorInterface) {
570
-                $tableName = $this->query->getType();
571
-                while (strpos($propertyName, '.') !== FALSE) {
572
-                    $this->addUnionStatement($tableName, $propertyName, $sql);
573
-                }
574
-            } elseif ($source instanceof JoinInterface) {
575
-                $tableName = $source->getJoinCondition()->getSelector1Name();
576
-            }
577
-
578
-            $columnName = $propertyName;
579
-            $operator = $this->resolveOperator($operator);
580
-            $constraintSQL = '';
581
-            if ($valueFunction === NULL) {
582
-                $constraintSQL .= (!empty($tableName) ? $tableName . '.' : '') . $columnName . ' ' . $operator . ' ?';
583
-            } else {
584
-                $constraintSQL .= $valueFunction . '(' . (!empty($tableName) ? $tableName . '.' : '') . $columnName . ') ' . $operator . ' ?';
585
-            }
586
-
587
-            if (isset($tableName) && !empty($this->currentChildTableNameAlias)) {
588
-                $constraintSQL = $this->replaceTableNameByAlias($tableName, $this->currentChildTableNameAlias, $constraintSQL);
589
-            }
590
-            $sql['where'][] = $constraintSQL;
591
-        }
592
-    }
593
-
594
-    /**
595
-     * @param string &$tableName
596
-     * @param array &$propertyPath
597
-     * @param array &$sql
598
-     * @throws Exception
599
-     * @throws Exception\InvalidRelationConfigurationException
600
-     * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\MissingColumnMapException
601
-     */
602
-    protected function addUnionStatement(&$tableName, &$propertyPath, array &$sql)
603
-    {
604
-
605
-        $table = Tca::table($tableName);
606
-
607
-        $explodedPropertyPath = explode('.', $propertyPath, 2);
608
-        $fieldName = $explodedPropertyPath[0];
609
-
610
-        // Field of type "group" are special because property path must contain the table name
611
-        // to determine the relation type. Example for sys_category, property path will look like "items.sys_file"
612
-        if ($table->field($fieldName)->isGroup()) {
613
-            $parts = explode('.', $propertyPath, 3);
614
-            $explodedPropertyPath[0] = $parts[0] . '.' . $parts[1];
615
-            $explodedPropertyPath[1] = $parts[2];
616
-            $fieldName = $explodedPropertyPath[0];
617
-        }
618
-
619
-        $parentKeyFieldName = $table->field($fieldName)->getForeignField();
620
-        $childTableName = $table->field($fieldName)->getForeignTable();
621
-
622
-        if ($childTableName === NULL) {
623
-            throw new Exception\InvalidRelationConfigurationException('The relation information for property "' . $fieldName . '" of class "' . $tableName . '" is missing.', 1353170925);
624
-        }
625
-
626
-        if ($table->field($fieldName)->hasOne()) { // includes relation "one-to-one" and "many-to-one"
627
-            // sometimes the opposite relation is not defined. We don't want to force this config for backward compatibility reasons.
628
-            // $parentKeyFieldName === NULL does the trick somehow. Before condition was if (isset($parentKeyFieldName))
629
-            if ($table->field($fieldName)->hasRelationManyToOne() || $parentKeyFieldName === NULL) {
630
-                $sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.' . $fieldName . '=' . $childTableName . '.uid';
631
-            } else {
632
-                $sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.uid=' . $childTableName . '.' . $parentKeyFieldName;
633
-            }
634
-        } elseif ($table->field($fieldName)->hasRelationManyToMany()) {
635
-            $relationTableName = $table->field($fieldName)->getManyToManyTable();
636
-
637
-            $parentKeyFieldName = $table->field($fieldName)->isOppositeRelation() ? 'uid_foreign' : 'uid_local';
638
-            $childKeyFieldName = !$table->field($fieldName)->isOppositeRelation() ? 'uid_foreign' : 'uid_local';
639
-
640
-            // MM table e.g sys_category_record_mm
641
-            $relationTableNameAlias = $this->generateAlias($relationTableName);
642
-            $join = sprintf(
643
-                'LEFT JOIN %s AS %s ON %s.uid=%s.%s', $relationTableName,
644
-                $relationTableNameAlias,
645
-                $tableName,
646
-                $relationTableNameAlias,
647
-                $parentKeyFieldName
648
-            );
649
-            $sql['unions'][$relationTableNameAlias] = $join;
650
-
651
-            // Foreign table e.g sys_category
652
-            $childTableNameAlias = $this->generateAlias($childTableName);
653
-            $this->currentChildTableNameAlias = $childTableNameAlias;
654
-            $join = sprintf(
655
-                'LEFT JOIN %s AS %s ON %s.%s=%s.uid',
656
-                $childTableName,
657
-                $childTableNameAlias,
658
-                $relationTableNameAlias,
659
-                $childKeyFieldName,
660
-                $childTableNameAlias
661
-            );
662
-            $sql['unions'][$childTableNameAlias] = $join;
663
-
664
-            // Find a possible table name for a MM condition.
665
-            $tableNameCondition = $table->field($fieldName)->getAdditionalTableNameCondition();
666
-            if ($tableNameCondition) {
667
-
668
-                // If we can find a source file name,  we can then retrieve more MM conditions from the TCA such as a field name.
669
-                $sourceFileName = $this->query->getSourceFieldName();
670
-                if (empty($sourceFileName)) {
671
-                    $additionalMMConditions = array(
672
-                        'tablenames' => $tableNameCondition,
673
-                    );
674
-                } else {
675
-                    $additionalMMConditions = Tca::table($tableNameCondition)->field($sourceFileName)->getAdditionalMMCondition();
676
-                }
677
-
678
-                foreach ($additionalMMConditions as $additionalFieldName => $additionalMMCondition) {
679
-                    $additionalJoin = sprintf(' AND %s.%s = "%s"', $relationTableNameAlias, $additionalFieldName, $additionalMMCondition);
680
-                    $sql['unions'][$relationTableNameAlias] .= $additionalJoin;
681
-
682
-                    $additionalJoin = sprintf(' AND %s.%s = "%s"', $relationTableNameAlias, $additionalFieldName, $additionalMMCondition);
683
-                    $sql['unions'][$childTableNameAlias] .= $additionalJoin;
684
-                }
685
-
686
-            }
687
-
688
-
689
-        } elseif ($table->field($fieldName)->hasMany()) { // includes relations "many-to-one" and "csv" relations
690
-            $childTableNameAlias = $this->generateAlias($childTableName);
691
-            $this->currentChildTableNameAlias = $childTableNameAlias;
692
-
693
-            if (isset($parentKeyFieldName)) {
694
-                $join = sprintf(
695
-                    'LEFT JOIN %s AS %s ON %s.uid=%s.%s',
696
-                    $childTableName,
697
-                    $childTableNameAlias,
698
-                    $tableName,
699
-                    $childTableNameAlias,
700
-                    $parentKeyFieldName
701
-                );
702
-                $sql['unions'][$childTableNameAlias] = $join;
703
-            } else {
704
-                $join = sprintf(
705
-                    'LEFT JOIN %s AS %s ON (FIND_IN_SET(%s.uid, %s.%s))',
706
-                    $childTableName,
707
-                    $childTableNameAlias,
708
-                    $childTableNameAlias,
709
-                    $tableName,
710
-                    $fieldName
711
-                );
712
-                $sql['unions'][$childTableNameAlias] = $join;
713
-            }
714
-        } else {
715
-            throw new Exception('Could not determine type of relation.', 1252502725);
716
-        }
717
-
718
-        // TODO check if there is another solution for this
719
-        $sql['keywords']['distinct'] = 'DISTINCT';
720
-        $propertyPath = $explodedPropertyPath[1];
721
-        $tableName = $childTableName;
722
-    }
723
-
724
-    /**
725
-     * Returns the SQL operator for the given JCR operator type.
726
-     *
727
-     * @param string $operator One of the JCR_OPERATOR_* constants
728
-     * @throws Exception
729
-     * @return string an SQL operator
730
-     */
731
-    protected function resolveOperator($operator)
732
-    {
733
-        switch ($operator) {
734
-            case self::OPERATOR_EQUAL_TO_NULL:
735
-                $operator = 'IS';
736
-                break;
737
-            case self::OPERATOR_NOT_EQUAL_TO_NULL:
738
-                $operator = 'IS NOT';
739
-                break;
740
-            case QueryInterface::OPERATOR_IN:
741
-                $operator = 'IN';
742
-                break;
743
-            case QueryInterface::OPERATOR_EQUAL_TO:
744
-                $operator = '=';
745
-                break;
746
-            case QueryInterface::OPERATOR_NOT_EQUAL_TO:
747
-                $operator = '!=';
748
-                break;
749
-            case QueryInterface::OPERATOR_LESS_THAN:
750
-                $operator = '<';
751
-                break;
752
-            case QueryInterface::OPERATOR_LESS_THAN_OR_EQUAL_TO:
753
-                $operator = '<=';
754
-                break;
755
-            case QueryInterface::OPERATOR_GREATER_THAN:
756
-                $operator = '>';
757
-                break;
758
-            case QueryInterface::OPERATOR_GREATER_THAN_OR_EQUAL_TO:
759
-                $operator = '>=';
760
-                break;
761
-            case QueryInterface::OPERATOR_LIKE:
762
-                $operator = 'LIKE';
763
-                break;
764
-            default:
765
-                throw new Exception('Unsupported operator encountered.', 1242816073);
766
-        }
767
-        return $operator;
768
-    }
769
-
770
-    /**
771
-     * Replace query placeholders in a query part by the given
772
-     * parameters.
773
-     *
774
-     * @param string &$sqlString The query part with placeholders
775
-     * @param array $parameters The parameters
776
-     * @param string $tableName
777
-     *
778
-     * @throws Exception
779
-     */
780
-    protected function replacePlaceholders(&$sqlString, array $parameters, $tableName = 'foo')
781
-    {
782
-        // TODO profile this method again
783
-        if (substr_count($sqlString, '?') !== count($parameters)) {
784
-            throw new Exception('The number of question marks to replace must be equal to the number of parameters.', 1242816074);
785
-        }
786
-        $offset = 0;
787
-        foreach ($parameters as $parameter) {
788
-            $markPosition = strpos($sqlString, '?', $offset);
789
-            if ($markPosition !== FALSE) {
790
-                if ($parameter === NULL) {
791
-                    $parameter = 'NULL';
792
-                } elseif (is_array($parameter) || $parameter instanceof \ArrayAccess || $parameter instanceof \Traversable) {
793
-                    $items = array();
794
-                    foreach ($parameter as $item) {
795
-                        $items[] = $this->databaseHandle->fullQuoteStr($item, $tableName);
796
-                    }
797
-                    $parameter = '(' . implode(',', $items) . ')';
798
-                } else {
799
-                    $parameter = $this->databaseHandle->fullQuoteStr($parameter, $tableName);
800
-                }
801
-                $sqlString = substr($sqlString, 0, $markPosition) . $parameter . substr($sqlString, ($markPosition + 1));
802
-            }
803
-            $offset = $markPosition + strlen($parameter);
804
-        }
805
-    }
806
-
807
-    /**
808
-     * Adds additional WHERE statements according to the query settings.
809
-     *
810
-     * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
811
-     * @param string $tableNameOrAlias The table name to add the additional where clause for
812
-     * @param array &$statementParts
813
-     * @return void
814
-     */
815
-    protected function addAdditionalWhereClause(QuerySettingsInterface $querySettings, $tableNameOrAlias, &$statementParts)
816
-    {
817
-        $this->addVisibilityConstraintStatement($querySettings, $tableNameOrAlias, $statementParts);
818
-        if ($querySettings->getRespectSysLanguage()) {
819
-            $this->addSysLanguageStatement($tableNameOrAlias, $statementParts, $querySettings);
820
-        }
821
-        if ($querySettings->getRespectStoragePage()) {
822
-            $this->addPageIdStatement($tableNameOrAlias, $statementParts, $querySettings->getStoragePageIds());
823
-        }
824
-    }
825
-
826
-    /**
827
-     * Adds enableFields and deletedClause to the query if necessary
828
-     *
829
-     * @param QuerySettingsInterface $querySettings
830
-     * @param string $tableNameOrAlias The database table name
831
-     * @param array &$statementParts The query parts
832
-     * @return void
833
-     */
834
-    protected function addVisibilityConstraintStatement(QuerySettingsInterface $querySettings, $tableNameOrAlias, array &$statementParts)
835
-    {
836
-        $statement = '';
837
-        $tableName = $this->resolveTableNameAlias($tableNameOrAlias);
838
-        if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
839
-            $ignoreEnableFields = $querySettings->getIgnoreEnableFields();
840
-            $enableFieldsToBeIgnored = $querySettings->getEnableFieldsToBeIgnored();
841
-            $includeDeleted = $querySettings->getIncludeDeleted();
842
-            if ($this->environmentService->isEnvironmentInFrontendMode()) {
843
-                $statement .= $this->getFrontendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $enableFieldsToBeIgnored, $includeDeleted);
844
-            } else {
845
-                // TYPO3_MODE === 'BE'
846
-                $statement .= $this->getBackendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $includeDeleted);
847
-            }
848
-
849
-            // Remove the prefixing "AND" if any.
850
-            if (!empty($statement)) {
851
-                $statement = strtolower(substr($statement, 1, 3)) === 'and' ? substr($statement, 5) : $statement;
852
-                $statementParts['additionalWhereClause'][$tableNameOrAlias][] = $statement;
853
-            }
854
-        }
855
-    }
856
-
857
-    /**
858
-     * Returns constraint statement for frontend context
859
-     *
860
-     * @param string $tableNameOrAlias
861
-     * @param boolean $ignoreEnableFields A flag indicating whether the enable fields should be ignored
862
-     * @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.
863
-     * @param boolean $includeDeleted A flag indicating whether deleted records should be included
864
-     * @return string
865
-     * @throws Exception\InconsistentQuerySettingsException
866
-     */
867
-    protected function getFrontendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $enableFieldsToBeIgnored = array(), $includeDeleted)
868
-    {
869
-        $statement = '';
870
-        $tableName = $this->resolveTableNameAlias($tableNameOrAlias);
871
-        if ($ignoreEnableFields && !$includeDeleted) {
872
-            if (count($enableFieldsToBeIgnored)) {
873
-                // array_combine() is necessary because of the way \TYPO3\CMS\Frontend\Page\PageRepository::enableFields() is implemented
874
-                $statement .= $this->getPageRepository()->enableFields($tableName, -1, array_combine($enableFieldsToBeIgnored, $enableFieldsToBeIgnored));
875
-            } else {
876
-                $statement .= $this->getPageRepository()->deleteClause($tableName);
877
-            }
878
-        } elseif (!$ignoreEnableFields && !$includeDeleted) {
879
-            $statement .= $this->getPageRepository()->enableFields($tableName);
880
-        } elseif (!$ignoreEnableFields && $includeDeleted) {
881
-            throw new Exception\InconsistentQuerySettingsException('Query setting "ignoreEnableFields=FALSE" can not be used together with "includeDeleted=TRUE" in frontend context.', 1327678173);
882
-        }
883
-        return $this->replaceTableNameByAlias($tableName, $tableNameOrAlias, $statement);
884
-    }
885
-
886
-    /**
887
-     * Returns constraint statement for backend context
888
-     *
889
-     * @param string $tableNameOrAlias
890
-     * @param boolean $ignoreEnableFields A flag indicating whether the enable fields should be ignored
891
-     * @param boolean $includeDeleted A flag indicating whether deleted records should be included
892
-     * @return string
893
-     */
894
-    protected function getBackendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $includeDeleted)
895
-    {
896
-        $tableName = $this->resolveTableNameAlias($tableNameOrAlias);
897
-        $statement = '';
898
-        if (!$ignoreEnableFields) {
899
-            $statement .= BackendUtility::BEenableFields($tableName);
900
-        }
901
-
902
-        // If the table is found to have "workspace" support, add the corresponding fields in the statement.
903
-        if (Tca::table($tableName)->hasWorkspaceSupport()) {
904
-            if ($this->getBackendUser()->workspace === 0) {
905
-                $statement .= ' AND ' . $tableName . '.t3ver_state<=' . new VersionState(VersionState::DEFAULT_STATE);
906
-            } else {
907
-                // Show only records of live and of the current workspace
908
-                // In case we are in a Versioning preview
909
-                $statement .= ' AND (' .
910
-                    $tableName . '.t3ver_wsid=0 OR ' .
911
-                    $tableName . '.t3ver_wsid=' . (int)$this->getBackendUser()->workspace .
912
-                    ')';
913
-            }
914
-
915
-            // Check if this segment make sense here or whether it should be in the "if" part when we have workspace = 0
916
-            $statement .= ' AND ' . $tableName . '.pid<>-1';
917
-        }
918
-
919
-        if (!$includeDeleted) {
920
-            $statement .= BackendUtility::deleteClause($tableName);
921
-        }
922
-
923
-        return $this->replaceTableNameByAlias($tableName, $tableNameOrAlias, $statement);
924
-    }
925
-
926
-    /**
927
-     * Builds the language field statement
928
-     *
929
-     * @param string $tableNameOrAlias The database table name
930
-     * @param array &$statementParts The query parts
931
-     * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
932
-     * @throws Exception
933
-     * @return void
934
-     */
935
-    protected function addSysLanguageStatement($tableNameOrAlias, array &$statementParts, $querySettings)
936
-    {
937
-
938
-        $tableName = $this->resolveTableNameAlias($tableNameOrAlias);
939
-        if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
940
-            if (!empty($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])) {
941
-                // Select all entries for the current language
942
-                $additionalWhereClause = $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . ' IN (' . intval($querySettings->getLanguageUid()) . ',-1)';
943
-                // If any language is set -> get those entries which are not translated yet
944
-                // They will be removed by t3lib_page::getRecordOverlay if not matching overlay mode
945
-                if (isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
946
-                    && $querySettings->getLanguageUid() > 0
947
-                ) {
948
-                    $additionalWhereClause .= ' OR (' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=0' .
949
-                        ' AND ' . $tableName . '.uid NOT IN (SELECT ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] .
950
-                        ' FROM ' . $tableName .
951
-                        ' WHERE ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] . '>0' .
952
-                        ' AND ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '>0';
953
-
954
-                    // Add delete clause to ensure all entries are loaded
955
-                    if (isset($GLOBALS['TCA'][$tableName]['ctrl']['delete'])) {
956
-                        $additionalWhereClause .= ' AND ' . $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['delete'] . '=0';
957
-                    }
958
-                    $additionalWhereClause .= '))';
959
-                    throw new Exception('Not tested code! It will fail', 1412928284);
960
-                }
961
-                $statementParts['additionalWhereClause'][$tableNameOrAlias][] = '(' . $additionalWhereClause . ')';
962
-            }
963
-        }
964
-    }
965
-
966
-    /**
967
-     * Builds the page ID checking statement
968
-     *
969
-     * @param string $tableNameOrAlias The database table name
970
-     * @param array &$statementParts The query parts
971
-     * @param array $storagePageIds list of storage page ids
972
-     * @throws Exception\InconsistentQuerySettingsException
973
-     * @return void
974
-     */
975
-    protected function addPageIdStatement($tableNameOrAlias, array &$statementParts, array $storagePageIds)
976
-    {
977
-
978
-        $tableName = $this->resolveTableNameAlias($tableNameOrAlias);
979
-        $tableColumns = $this->tableColumnCache->get($tableName);
980
-        if ($tableColumns === FALSE) {
981
-            $tableColumns = $this->databaseHandle->admin_get_fields($tableName);
982
-            $this->tableColumnCache->set($tableName, $tableColumns);
983
-        }
984
-        if (is_array($GLOBALS['TCA'][$tableName]['ctrl']) && array_key_exists('pid', $tableColumns)) {
985
-            $rootLevel = (int)$GLOBALS['TCA'][$tableName]['ctrl']['rootLevel'];
986
-            if ($rootLevel) {
987
-                if ($rootLevel === 1) {
988
-                    $statementParts['additionalWhereClause'][$tableNameOrAlias][] = $tableNameOrAlias . '.pid = 0';
989
-                }
990
-            } else {
991
-                if (empty($storagePageIds)) {
992
-                    throw new Exception\InconsistentQuerySettingsException('Missing storage page ids.', 1365779762);
993
-                }
994
-                $statementParts['additionalWhereClause'][$tableNameOrAlias][] = $tableNameOrAlias . '.pid IN (' . implode(', ', $storagePageIds) . ')';
995
-            }
996
-        }
997
-    }
998
-
999
-    /**
1000
-     * Transforms orderings into SQL.
1001
-     *
1002
-     * @param array $orderings An array of orderings (Tx_Extbase_Persistence_QOM_Ordering)
1003
-     * @param SourceInterface $source The source
1004
-     * @param array &$sql The query parts
1005
-     * @throws Exception\UnsupportedOrderException
1006
-     * @return void
1007
-     */
1008
-    protected function parseOrderings(array $orderings, SourceInterface $source, array &$sql)
1009
-    {
1010
-        foreach ($orderings as $fieldNameAndPath => $order) {
1011
-            switch ($order) {
1012
-                case QueryInterface::ORDER_ASCENDING:
1013
-                    $order = 'ASC';
1014
-                    break;
1015
-                case QueryInterface::ORDER_DESCENDING:
1016
-                    $order = 'DESC';
1017
-                    break;
1018
-                default:
1019
-                    throw new Exception\UnsupportedOrderException('Unsupported order encountered.', 1242816074);
1020
-            }
1021
-
1022
-            $tableName = $this->getFieldPathResolver()->getDataType($fieldNameAndPath, $this->query->getType());
1023
-            $fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath, $tableName);
1024
-            $sql['orderings'][] = sprintf('%s.%s %s', $tableName, $fieldName, $order);
1025
-        }
1026
-    }
1027
-
1028
-    /**
1029
-     * Transforms limit and offset into SQL
1030
-     *
1031
-     * @param int $limit
1032
-     * @param int $offset
1033
-     * @param array &$sql
1034
-     * @return void
1035
-     */
1036
-    protected function parseLimitAndOffset($limit, $offset, array &$sql)
1037
-    {
1038
-        if ($limit !== NULL && $offset !== NULL) {
1039
-            $sql['limit'] = intval($offset) . ', ' . intval($limit);
1040
-        } elseif ($limit !== NULL) {
1041
-            $sql['limit'] = intval($limit);
1042
-        }
1043
-    }
1044
-
1045
-    /**
1046
-     * Transforms a Resource from a database query to an array of rows.
1047
-     *
1048
-     * @param resource $result The result
1049
-     * @return array The result as an array of rows (tuples)
1050
-     */
1051
-    protected function getRowsFromResult($result)
1052
-    {
1053
-        $rows = array();
1054
-        while ($row = $this->databaseHandle->sql_fetch_assoc($result)) {
1055
-            if (is_array($row)) {
1056
-
1057
-                // Get language uid from querySettings.
1058
-                // Ensure the backend handling is not broken (fallback to Get parameter 'L' if needed)
1059
-                $overlaidRow = $this->doLanguageAndWorkspaceOverlay($this->query->getSource(), $row, $this->query->getQuerySettings());
1060
-                $contentObject = GeneralUtility::makeInstance($this->objectType, $this->query->getType(), $overlaidRow);
1061
-                $rows[] = $contentObject;
1062
-            }
1063
-        }
1064
-
1065
-        return $rows;
1066
-    }
1067
-
1068
-    /**
1069
-     * Performs workspace and language overlay on the given row array. The language and workspace id is automatically
1070
-     * detected (depending on FE or BE context). You can also explicitly set the language/workspace id.
1071
-     *
1072
-     * @param SourceInterface $source The source (selector od join)
1073
-     * @param array $row
1074
-     * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
1075
-     * @return array
1076
-     */
1077
-    protected function doLanguageAndWorkspaceOverlay(SourceInterface $source, array $row, $querySettings)
1078
-    {
1079
-
1080
-        /** @var SelectorInterface $source */
1081
-        $tableName = $source->getSelectorName();
1082
-
1083
-        $pageRepository = $this->getPageRepository();
1084
-        if (is_object($GLOBALS['TSFE'])) {
1085
-            $languageMode = $GLOBALS['TSFE']->sys_language_mode;
1086
-            if ($this->isBackendUserLogged() && $this->getBackendUser()->workspace !== 0) {
1087
-                $pageRepository->versioningWorkspaceId = $this->getBackendUser()->workspace;
1088
-            }
1089
-        } else {
1090
-            $languageMode = '';
1091
-            $workspaceUid = $this->getBackendUser()->workspace;
1092
-            $pageRepository->versioningWorkspaceId = $workspaceUid;
1093
-            if ($this->getBackendUser()->workspace !== 0) {
1094
-                $pageRepository->versioningPreview = 1;
1095
-            }
1096
-        }
1097
-
1098
-        // If current row is a translation select its parent
1099
-        if (isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
1100
-            && isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
1101
-        ) {
1102
-            if (isset($row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']])
1103
-                && $row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']] > 0
1104
-            ) {
1105
-                $row = $this->databaseHandle->exec_SELECTgetSingleRow(
1106
-                    $tableName . '.*',
1107
-                    $tableName,
1108
-                    $tableName . '.uid=' . (int)$row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']] .
1109
-                    ' AND ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=0'
1110
-                );
1111
-            }
1112
-        }
1113
-
1114
-        // Retrieve the original uid
1115
-        // @todo It looks for me this code will never be used! "_ORIG_uid" is something from extbase. Adjust me or remove me in 0.4 + 2 version!
1116
-        $pageRepository->versionOL($tableName, $row, TRUE);
1117
-        if ($pageRepository->versioningPreview && isset($row['_ORIG_uid'])) {
1118
-            $row['uid'] = $row['_ORIG_uid'];
1119
-        }
1120
-
1121
-        // Special case for table "pages"
1122
-        if ($tableName == 'pages') {
1123
-            $row = $pageRepository->getPageOverlay($row, $querySettings->getLanguageUid());
1124
-        } elseif (isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
1125
-            && $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] !== ''
1126
-        ) {
1127
-            if (in_array($row[$GLOBALS['TCA'][$tableName]['ctrl']['languageField']], array(-1, 0))) {
1128
-                $overlayMode = $languageMode === 'strict' ? 'hideNonTranslated' : '';
1129
-                $row = $pageRepository->getRecordOverlay($tableName, $row, $querySettings->getLanguageUid(), $overlayMode);
1130
-            }
1131
-        }
1132
-
1133
-        return $row;
1134
-    }
1135
-
1136
-    /**
1137
-     * Return a resolved table name given a possible table name alias.
1138
-     *
1139
-     * @param string $tableNameOrAlias
1140
-     * @return string
1141
-     */
1142
-    protected function resolveTableNameAlias($tableNameOrAlias)
1143
-    {
1144
-        $resolvedTableName = $tableNameOrAlias;
1145
-        if (!empty($this->tableNameAliases['aliases'][$tableNameOrAlias])) {
1146
-            $resolvedTableName = $this->tableNameAliases['aliases'][$tableNameOrAlias];
1147
-        }
1148
-        return $resolvedTableName;
1149
-    }
1150
-
1151
-    /**
1152
-     * Generate a unique table name alias for the given table name.
1153
-     *
1154
-     * @param string $tableName
1155
-     * @return string
1156
-     */
1157
-    protected function generateAlias($tableName)
1158
-    {
1159
-
1160
-        if (!isset($this->tableNameAliases['aliasIncrement'][$tableName])) {
1161
-            $this->tableNameAliases['aliasIncrement'][$tableName] = 0;
1162
-        }
1163
-
1164
-        $numberOfAliases = $this->tableNameAliases['aliasIncrement'][$tableName];
1165
-        $tableNameAlias = $tableName . $numberOfAliases;
1166
-
1167
-        $this->tableNameAliases['aliasIncrement'][$tableName]++;
1168
-        $this->tableNameAliases['aliases'][$tableNameAlias] = $tableName;
1169
-
1170
-        return $tableNameAlias;
1171
-    }
1172
-
1173
-    /**
1174
-     * Replace the table names by its table name alias within the given statement.
1175
-     *
1176
-     * @param string $tableName
1177
-     * @param string $tableNameAlias
1178
-     * @param string $statement
1179
-     * @return string
1180
-     */
1181
-    protected function replaceTableNameByAlias($tableName, $tableNameAlias, $statement)
1182
-    {
1183
-        if ($statement && $tableName !== $tableNameAlias) {
1184
-            $statement = str_replace($tableName, $tableNameAlias, $statement);
1185
-        }
1186
-        return $statement;
1187
-    }
1188
-
1189
-    /**
1190
-     * Returns an instance of the current Backend User.
1191
-     *
1192
-     * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
1193
-     */
1194
-    protected function getBackendUser()
1195
-    {
1196
-        return $GLOBALS['BE_USER'];
1197
-    }
1198
-
1199
-    /**
1200
-     * Tell whether a Backend User is logged in.
1201
-     *
1202
-     * @return bool
1203
-     */
1204
-    protected function isBackendUserLogged()
1205
-    {
1206
-        return is_object($GLOBALS['BE_USER']);
1207
-    }
1208
-
1209
-    /**
1210
-     * @return PageRepository
1211
-     */
1212
-    protected function getPageRepository()
1213
-    {
1214
-        if (!$this->pageRepository instanceof PageRepository) {
1215
-            if ($this->environmentService->isEnvironmentInFrontendMode() && is_object($GLOBALS['TSFE'])) {
1216
-                $this->pageRepository = $GLOBALS['TSFE']->sys_page;
1217
-            } else {
1218
-                $this->pageRepository = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Page\\PageRepository');
1219
-            }
1220
-        }
1221
-
1222
-        return $this->pageRepository;
1223
-    }
1224
-
1225
-    /**
1226
-     * @return \Fab\Vidi\Resolver\FieldPathResolver
1227
-     */
1228
-    protected function getFieldPathResolver()
1229
-    {
1230
-        return GeneralUtility::makeInstance('Fab\Vidi\Resolver\FieldPathResolver');
1231
-    }
1232
-
1233
-    /**
1234
-     * Checks if there are SQL errors in the last query, and if yes, throw an exception.
1235
-     *
1236
-     * @return void
1237
-     * @param string $sql The SQL statement
1238
-     * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Storage\Exception\SqlErrorException
1239
-     */
1240
-    protected function checkSqlErrors($sql = '')
1241
-    {
1242
-        $error = $this->databaseHandle->sql_error();
1243
-        if ($error !== '') {
1244
-            $error .= $sql ? ': ' . $sql : '';
1245
-            throw new \TYPO3\CMS\Extbase\Persistence\Generic\Storage\Exception\SqlErrorException($error, 1247602160);
1246
-        }
1247
-    }
41
+	const OPERATOR_EQUAL_TO_NULL = 'operatorEqualToNull';
42
+	const OPERATOR_NOT_EQUAL_TO_NULL = 'operatorNotEqualToNull';
43
+
44
+	/**
45
+	 * The TYPO3 database object
46
+	 *
47
+	 * @var \TYPO3\CMS\Core\Database\DatabaseConnection
48
+	 */
49
+	protected $databaseHandle;
50
+
51
+	/**
52
+	 * The TYPO3 page repository. Used for language and workspace overlay
53
+	 *
54
+	 * @var PageRepository
55
+	 */
56
+	protected $pageRepository;
57
+
58
+	/**
59
+	 * A first-level TypoScript configuration cache
60
+	 *
61
+	 * @var array
62
+	 */
63
+	protected $pageTSConfigCache = array();
64
+
65
+	/**
66
+	 * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
67
+	 * @inject
68
+	 */
69
+	protected $configurationManager;
70
+
71
+	/**
72
+	 * @var \TYPO3\CMS\Extbase\Service\CacheService
73
+	 * @inject
74
+	 */
75
+	protected $cacheService;
76
+
77
+	/**
78
+	 * @var \TYPO3\CMS\Core\Cache\CacheManager
79
+	 * @inject
80
+	 */
81
+	protected $cacheManager;
82
+
83
+	/**
84
+	 * @var \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend
85
+	 */
86
+	protected $tableColumnCache;
87
+
88
+	/**
89
+	 * @var \TYPO3\CMS\Extbase\Service\EnvironmentService
90
+	 * @inject
91
+	 */
92
+	protected $environmentService;
93
+
94
+	/**
95
+	 * @var \Fab\Vidi\Persistence\Query
96
+	 */
97
+	protected $query;
98
+
99
+	/**
100
+	 * Store some info related to table name and its aliases.
101
+	 *
102
+	 * @var array
103
+	 */
104
+	protected $tableNameAliases = array(
105
+		'aliases' => array(),
106
+		'aliasIncrement' => array(),
107
+	);
108
+
109
+	/**
110
+	 * Use to store the current foreign table name alias.
111
+	 *
112
+	 * @var string
113
+	 */
114
+	protected $currentChildTableNameAlias = '';
115
+
116
+	/**
117
+	 * The default object type being returned.
118
+	 *
119
+	 * @var string
120
+	 */
121
+	protected $objectType = 'Fab\Vidi\Domain\Model\Content';
122
+
123
+	/**
124
+	 * Constructor. takes the database handle from $GLOBALS['TYPO3_DB']
125
+	 */
126
+	public function __construct(QueryInterface $query)
127
+	{
128
+		$this->query = $query;
129
+		$this->databaseHandle = $GLOBALS['TYPO3_DB'];
130
+	}
131
+
132
+	/**
133
+	 * Lifecycle method
134
+	 *
135
+	 * @return void
136
+	 */
137
+	public function initializeObject()
138
+	{
139
+		$this->tableColumnCache = $this->cacheManager->getCache('extbase_typo3dbbackend_tablecolumns');
140
+	}
141
+
142
+	/**
143
+	 * @param array $identifier
144
+	 * @return string
145
+	 */
146
+	protected function parseIdentifier(array $identifier)
147
+	{
148
+		$fieldNames = array_keys($identifier);
149
+		$suffixedFieldNames = array();
150
+		foreach ($fieldNames as $fieldName) {
151
+			$suffixedFieldNames[] = $fieldName . '=?';
152
+		}
153
+		return implode(' AND ', $suffixedFieldNames);
154
+	}
155
+
156
+	/**
157
+	 * Returns the result of the query
158
+	 */
159
+	public function fetchResult()
160
+	{
161
+
162
+		$parameters = array();
163
+		$statementParts = $this->parseQuery($this->query, $parameters);
164
+		$statementParts = $this->processStatementStructureForRecursiveMMRelation($statementParts); // Mmm... check if that is the right way of doing that.
165
+
166
+		$sql = $this->buildQuery($statementParts);
167
+		$tableName = '';
168
+		if (is_array($statementParts) && !empty($statementParts['tables'][0])) {
169
+			$tableName = $statementParts['tables'][0];
170
+		}
171
+		$this->replacePlaceholders($sql, $parameters, $tableName);
172
+		#print $sql; exit(); // @debug
173
+
174
+		$result = $this->databaseHandle->sql_query($sql);
175
+		$this->checkSqlErrors($sql);
176
+		$rows = $this->getRowsFromResult($result);
177
+		$this->databaseHandle->sql_free_result($result);
178
+
179
+		return $rows;
180
+	}
181
+
182
+	/**
183
+	 * Returns the number of tuples matching the query.
184
+	 *
185
+	 * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Storage\Exception\BadConstraintException
186
+	 * @return int The number of matching tuples
187
+	 */
188
+	public function countResult()
189
+	{
190
+
191
+		$parameters = array();
192
+		$statementParts = $this->parseQuery($this->query, $parameters);
193
+		$statementParts = $this->processStatementStructureForRecursiveMMRelation($statementParts); // Mmm... check if that is the right way of doing that.
194
+		// Reset $statementParts for valid table return
195
+		reset($statementParts);
196
+
197
+		// if limit is set, we need to count the rows "manually" as COUNT(*) ignores LIMIT constraints
198
+		if (!empty($statementParts['limit'])) {
199
+			$statement = $this->buildQuery($statementParts);
200
+			$this->replacePlaceholders($statement, $parameters, current($statementParts['tables']));
201
+			#print $statement; exit(); // @debug
202
+			$result = $this->databaseHandle->sql_query($statement);
203
+			$this->checkSqlErrors($statement);
204
+			$count = $this->databaseHandle->sql_num_rows($result);
205
+		} else {
206
+			$statementParts['fields'] = array('COUNT(*)');
207
+			// having orderings without grouping is not compatible with non-MySQL DBMS
208
+			$statementParts['orderings'] = array();
209
+			if (isset($statementParts['keywords']['distinct'])) {
210
+				unset($statementParts['keywords']['distinct']);
211
+				$distinctField = $this->query->getDistinct() ? $this->query->getDistinct() : 'uid';
212
+				$statementParts['fields'] = array('COUNT(DISTINCT ' . reset($statementParts['tables']) . '.' . $distinctField . ')');
213
+			}
214
+
215
+			$statement = $this->buildQuery($statementParts);
216
+			$this->replacePlaceholders($statement, $parameters, current($statementParts['tables']));
217
+
218
+			#print $statement; exit(); // @debug
219
+			$result = $this->databaseHandle->sql_query($statement);
220
+			$this->checkSqlErrors($statement);
221
+			$count = 0;
222
+			if ($result) {
223
+				$row = $this->databaseHandle->sql_fetch_assoc($result);
224
+				$count = current($row);
225
+			}
226
+		}
227
+		$this->databaseHandle->sql_free_result($result);
228
+		return (int)$count;
229
+	}
230
+
231
+	/**
232
+	 * Parses the query and returns the SQL statement parts.
233
+	 *
234
+	 * @param QueryInterface $query The query
235
+	 * @param array &$parameters
236
+	 * @return array The SQL statement parts
237
+	 */
238
+	public function parseQuery(QueryInterface $query, array &$parameters)
239
+	{
240
+		$statementParts = array();
241
+		$statementParts['keywords'] = array();
242
+		$statementParts['tables'] = array();
243
+		$statementParts['unions'] = array();
244
+		$statementParts['fields'] = array();
245
+		$statementParts['where'] = array();
246
+		$statementParts['additionalWhereClause'] = array();
247
+		$statementParts['orderings'] = array();
248
+		$statementParts['limit'] = array();
249
+		$source = $query->getSource();
250
+		$this->parseSource($source, $statementParts);
251
+		$this->parseConstraint($query->getConstraint(), $source, $statementParts, $parameters);
252
+		$this->parseOrderings($query->getOrderings(), $source, $statementParts);
253
+		$this->parseLimitAndOffset($query->getLimit(), $query->getOffset(), $statementParts);
254
+		$tableNames = array_unique(array_keys($statementParts['tables'] + $statementParts['unions']));
255
+		foreach ($tableNames as $tableNameOrAlias) {
256
+			if (is_string($tableNameOrAlias) && strlen($tableNameOrAlias) > 0) {
257
+				$this->addAdditionalWhereClause($query->getQuerySettings(), $tableNameOrAlias, $statementParts);
258
+			}
259
+		}
260
+
261
+		return $statementParts;
262
+	}
263
+
264
+	/**
265
+	 * Fiddle with the statement structure to handle recursive MM relations.
266
+	 * For the recursive MM query to work, we must invert some values.
267
+	 * Let see if that is the best way of doing that...
268
+	 *
269
+	 * @param array $statementParts
270
+	 * @return array
271
+	 */
272
+	public function processStatementStructureForRecursiveMMRelation(array $statementParts)
273
+	{
274
+
275
+		if ($this->hasRecursiveMMRelation()) {
276
+			$tableName = $this->query->getType();
277
+
278
+			// In order the MM query to work for a recursive MM query, we must invert some values.
279
+			// tx_domain_model_foo0 (the alias) <--> tx_domain_model_foo (the origin table name)
280
+			$values = array();
281
+			foreach ($statementParts['fields'] as $key => $value) {
282
+				$values[$key] = str_replace($tableName, $tableName . '0', $value);
283
+			}
284
+			$statementParts['fields'] = $values;
285
+
286
+			// Same comment as above.
287
+			$values = array();
288
+			foreach ($statementParts['where'] as $key => $value) {
289
+				$values[$key] = str_replace($tableName . '0', $tableName, $value);
290
+			}
291
+			$statementParts['where'] = $values;
292
+
293
+			// We must be more restrictive by transforming the "left" union by "inner"
294
+			$values = array();
295
+			foreach ($statementParts['unions'] as $key => $value) {
296
+				$values[$key] = str_replace('LEFT JOIN', 'INNER JOIN', $value);
297
+			}
298
+			$statementParts['unions'] = $values;
299
+		}
300
+
301
+		return $statementParts;
302
+	}
303
+
304
+	/**
305
+	 * Tell whether there is a recursive MM relation.
306
+	 *
307
+	 * @return bool
308
+	 */
309
+	public function hasRecursiveMMRelation()
310
+	{
311
+		return isset($this->tableNameAliases['aliasIncrement'][$this->query->getType()]);
312
+
313
+	}
314
+
315
+	/**
316
+	 * Returns the statement, ready to be executed.
317
+	 *
318
+	 * @param array $statementParts The SQL statement parts
319
+	 * @return string The SQL statement
320
+	 */
321
+	public function buildQuery(array $statementParts)
322
+	{
323
+
324
+		// Add more statement to the UNION part.
325
+		if (!empty($statementParts['unions'])) {
326
+			foreach ($statementParts['unions'] as $tableName => $unionPart) {
327
+				if (!empty($statementParts['additionalWhereClause'][$tableName])) {
328
+					$statementParts['unions'][$tableName] .= ' AND ' . implode(' AND ', $statementParts['additionalWhereClause'][$tableName]);
329
+				}
330
+			}
331
+		}
332
+
333
+		$statement = 'SELECT ' . implode(' ', $statementParts['keywords']) . ' ' . implode(',', $statementParts['fields']) . ' FROM ' . implode(' ', $statementParts['tables']) . ' ' . implode(' ', $statementParts['unions']);
334
+		if (!empty($statementParts['where'])) {
335
+			$statement .= ' WHERE ' . implode('', $statementParts['where']);
336
+			if (!empty($statementParts['additionalWhereClause'][$this->query->getType()])) {
337
+				$statement .= ' AND ' . implode(' AND ', $statementParts['additionalWhereClause'][$this->query->getType()]);
338
+			}
339
+		} elseif (!empty($statementParts['additionalWhereClause'])) {
340
+			$statement .= ' WHERE ' . implode(' AND ', $statementParts['additionalWhereClause'][$this->query->getType()]);
341
+		}
342
+		if (!empty($statementParts['orderings'])) {
343
+			$statement .= ' ORDER BY ' . implode(', ', $statementParts['orderings']);
344
+		}
345
+		if (!empty($statementParts['limit'])) {
346
+			$statement .= ' LIMIT ' . $statementParts['limit'];
347
+		}
348
+
349
+		return $statement;
350
+	}
351
+
352
+	/**
353
+	 * Transforms a Query Source into SQL and parameter arrays
354
+	 *
355
+	 * @param SourceInterface $source The source
356
+	 * @param array &$sql
357
+	 * @return void
358
+	 */
359
+	protected function parseSource(SourceInterface $source, array &$sql)
360
+	{
361
+		if ($source instanceof SelectorInterface) {
362
+			$tableName = $source->getNodeTypeName();
363
+			$sql['fields'][$tableName] = $tableName . '.*';
364
+			$sql['tables'][$tableName] = $tableName;
365
+			if ($this->query->getDistinct()) {
366
+				$sql['fields'][$tableName] = $tableName . '.' . $this->query->getDistinct();
367
+				$sql['keywords']['distinct'] = 'DISTINCT';
368
+			}
369
+		} elseif ($source instanceof JoinInterface) {
370
+			$this->parseJoin($source, $sql);
371
+		}
372
+	}
373
+
374
+	/**
375
+	 * Transforms a Join into SQL and parameter arrays
376
+	 *
377
+	 * @param JoinInterface $join The join
378
+	 * @param array &$sql The query parts
379
+	 * @return void
380
+	 */
381
+	protected function parseJoin(JoinInterface $join, array &$sql)
382
+	{
383
+		$leftSource = $join->getLeft();
384
+		$leftTableName = $leftSource->getSelectorName();
385
+		// $sql['fields'][$leftTableName] = $leftTableName . '.*';
386
+		$rightSource = $join->getRight();
387
+		if ($rightSource instanceof JoinInterface) {
388
+			$rightTableName = $rightSource->getLeft()->getSelectorName();
389
+		} else {
390
+			$rightTableName = $rightSource->getSelectorName();
391
+			$sql['fields'][$leftTableName] = $rightTableName . '.*';
392
+		}
393
+		$sql['tables'][$leftTableName] = $leftTableName;
394
+		$sql['unions'][$rightTableName] = 'LEFT JOIN ' . $rightTableName;
395
+		$joinCondition = $join->getJoinCondition();
396
+		if ($joinCondition instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\EquiJoinCondition) {
397
+			$column1Name = $joinCondition->getProperty1Name();
398
+			$column2Name = $joinCondition->getProperty2Name();
399
+			$sql['unions'][$rightTableName] .= ' ON ' . $joinCondition->getSelector1Name() . '.' . $column1Name . ' = ' . $joinCondition->getSelector2Name() . '.' . $column2Name;
400
+		}
401
+		if ($rightSource instanceof JoinInterface) {
402
+			$this->parseJoin($rightSource, $sql);
403
+		}
404
+	}
405
+
406
+	/**
407
+	 * Transforms a constraint into SQL and parameter arrays
408
+	 *
409
+	 * @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface $constraint The constraint
410
+	 * @param SourceInterface $source The source
411
+	 * @param array &$sql The query parts
412
+	 * @param array &$parameters The parameters that will replace the markers
413
+	 * @return void
414
+	 */
415
+	protected function parseConstraint(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface $constraint = NULL, SourceInterface $source, array &$sql, array &$parameters)
416
+	{
417
+		if ($constraint instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\AndInterface) {
418
+			$sql['where'][] = '(';
419
+			$this->parseConstraint($constraint->getConstraint1(), $source, $sql, $parameters);
420
+			$sql['where'][] = ' AND ';
421
+			$this->parseConstraint($constraint->getConstraint2(), $source, $sql, $parameters);
422
+			$sql['where'][] = ')';
423
+		} elseif ($constraint instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\OrInterface) {
424
+			$sql['where'][] = '(';
425
+			$this->parseConstraint($constraint->getConstraint1(), $source, $sql, $parameters);
426
+			$sql['where'][] = ' OR ';
427
+			$this->parseConstraint($constraint->getConstraint2(), $source, $sql, $parameters);
428
+			$sql['where'][] = ')';
429
+		} elseif ($constraint instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\NotInterface) {
430
+			$sql['where'][] = 'NOT (';
431
+			$this->parseConstraint($constraint->getConstraint(), $source, $sql, $parameters);
432
+			$sql['where'][] = ')';
433
+		} elseif ($constraint instanceof ComparisonInterface) {
434
+			$this->parseComparison($constraint, $source, $sql, $parameters);
435
+		}
436
+	}
437
+
438
+	/**
439
+	 * Parse a Comparison into SQL and parameter arrays.
440
+	 *
441
+	 * @param ComparisonInterface $comparison The comparison to parse
442
+	 * @param SourceInterface $source The source
443
+	 * @param array &$sql SQL query parts to add to
444
+	 * @param array &$parameters Parameters to bind to the SQL
445
+	 * @throws Exception\RepositoryException
446
+	 * @return void
447
+	 */
448
+	protected function parseComparison(ComparisonInterface $comparison, SourceInterface $source, array &$sql, array &$parameters)
449
+	{
450
+		$operand1 = $comparison->getOperand1();
451
+		$operator = $comparison->getOperator();
452
+		$operand2 = $comparison->getOperand2();
453
+		if ($operator === QueryInterface::OPERATOR_IN) {
454
+			$items = array();
455
+			$hasValue = FALSE;
456
+			foreach ($operand2 as $value) {
457
+				$value = $this->getPlainValue($value);
458
+				if ($value !== NULL) {
459
+					$items[] = $value;
460
+					$hasValue = TRUE;
461
+				}
462
+			}
463
+			if ($hasValue === FALSE) {
464
+				$sql['where'][] = '1<>1';
465
+			} else {
466
+				$this->parseDynamicOperand($operand1, $operator, $source, $sql, $parameters, NULL);
467
+				$parameters[] = $items;
468
+			}
469
+		} elseif ($operator === QueryInterface::OPERATOR_CONTAINS) {
470
+			if ($operand2 === NULL) {
471
+				$sql['where'][] = '1<>1';
472
+			} else {
473
+				throw new \Exception('Not implemented! Contact extension author.', 1412931227);
474
+				# @todo re-implement me if necessary.
475
+				#$tableName = $this->query->getType();
476
+				#$propertyName = $operand1->getPropertyName();
477
+				#while (strpos($propertyName, '.') !== FALSE) {
478
+				#	$this->addUnionStatement($tableName, $propertyName, $sql);
479
+				#}
480
+				#$columnName = $propertyName;
481
+				#$columnMap = $propertyName;
482
+				#$typeOfRelation = $columnMap instanceof ColumnMap ? $columnMap->getTypeOfRelation() : NULL;
483
+				#if ($typeOfRelation === ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
484
+				#	$relationTableName = $columnMap->getRelationTableName();
485
+				#	$sql['where'][] = $tableName . '.uid IN (SELECT ' . $columnMap->getParentKeyFieldName() . ' FROM ' . $relationTableName . ' WHERE ' . $columnMap->getChildKeyFieldName() . '=?)';
486
+				#	$parameters[] = intval($this->getPlainValue($operand2));
487
+				#} elseif ($typeOfRelation === ColumnMap::RELATION_HAS_MANY) {
488
+				#	$parentKeyFieldName = $columnMap->getParentKeyFieldName();
489
+				#	if (isset($parentKeyFieldName)) {
490
+				#		$childTableName = $columnMap->getChildTableName();
491
+				#		$sql['where'][] = $tableName . '.uid=(SELECT ' . $childTableName . '.' . $parentKeyFieldName . ' FROM ' . $childTableName . ' WHERE ' . $childTableName . '.uid=?)';
492
+				#		$parameters[] = intval($this->getPlainValue($operand2));
493
+				#	} else {
494
+				#		$sql['where'][] = 'FIND_IN_SET(?,' . $tableName . '.' . $columnName . ')';
495
+				#		$parameters[] = intval($this->getPlainValue($operand2));
496
+				#	}
497
+				#} else {
498
+				#	throw new Exception\RepositoryException('Unsupported or non-existing property name "' . $propertyName . '" used in relation matching.', 1327065745);
499
+				#}
500
+			}
501
+		} else {
502
+			if ($operand2 === NULL) {
503
+				if ($operator === QueryInterface::OPERATOR_EQUAL_TO) {
504
+					$operator = self::OPERATOR_EQUAL_TO_NULL;
505
+				} elseif ($operator === QueryInterface::OPERATOR_NOT_EQUAL_TO) {
506
+					$operator = self::OPERATOR_NOT_EQUAL_TO_NULL;
507
+				}
508
+			}
509
+			$this->parseDynamicOperand($operand1, $operator, $source, $sql, $parameters);
510
+			$parameters[] = $this->getPlainValue($operand2);
511
+		}
512
+	}
513
+
514
+	/**
515
+	 * Returns a plain value, i.e. objects are flattened out if possible.
516
+	 *
517
+	 * @param mixed $input
518
+	 * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnexpectedTypeException
519
+	 * @return mixed
520
+	 */
521
+	protected function getPlainValue($input)
522
+	{
523
+		if (is_array($input)) {
524
+			throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnexpectedTypeException('An array could not be converted to a plain value.', 1274799932);
525
+		}
526
+		if ($input instanceof \DateTime) {
527
+			return $input->format('U');
528
+		} elseif (is_object($input)) {
529
+			if ($input instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy) {
530
+				$realInput = $input->_loadRealInstance();
531
+			} else {
532
+				$realInput = $input;
533
+			}
534
+			if ($realInput instanceof \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface) {
535
+				return $realInput->getUid();
536
+			} else {
537
+				throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnexpectedTypeException('An object of class "' . get_class($realInput) . '" could not be converted to a plain value.', 1274799934);
538
+			}
539
+		} elseif (is_bool($input)) {
540
+			return $input === TRUE ? 1 : 0;
541
+		} else {
542
+			return $input;
543
+		}
544
+	}
545
+
546
+	/**
547
+	 * Parse a DynamicOperand into SQL and parameter arrays.
548
+	 *
549
+	 * @param DynamicOperandInterface $operand
550
+	 * @param string $operator One of the JCR_OPERATOR_* constants
551
+	 * @param SourceInterface $source The source
552
+	 * @param array &$sql The query parts
553
+	 * @param array &$parameters The parameters that will replace the markers
554
+	 * @param string $valueFunction an optional SQL function to apply to the operand value
555
+	 * @return void
556
+	 */
557
+	protected function parseDynamicOperand(DynamicOperandInterface $operand, $operator, SourceInterface $source, array &$sql, array &$parameters, $valueFunction = NULL)
558
+	{
559
+		if ($operand instanceof LowerCaseInterface) {
560
+			$this->parseDynamicOperand($operand->getOperand(), $operator, $source, $sql, $parameters, 'LOWER');
561
+		} elseif ($operand instanceof UpperCaseInterface) {
562
+			$this->parseDynamicOperand($operand->getOperand(), $operator, $source, $sql, $parameters, 'UPPER');
563
+		} elseif ($operand instanceof PropertyValueInterface) {
564
+			$propertyName = $operand->getPropertyName();
565
+
566
+			// Reset value.
567
+			$this->currentChildTableNameAlias = '';
568
+
569
+			if ($source instanceof SelectorInterface) {
570
+				$tableName = $this->query->getType();
571
+				while (strpos($propertyName, '.') !== FALSE) {
572
+					$this->addUnionStatement($tableName, $propertyName, $sql);
573
+				}
574
+			} elseif ($source instanceof JoinInterface) {
575
+				$tableName = $source->getJoinCondition()->getSelector1Name();
576
+			}
577
+
578
+			$columnName = $propertyName;
579
+			$operator = $this->resolveOperator($operator);
580
+			$constraintSQL = '';
581
+			if ($valueFunction === NULL) {
582
+				$constraintSQL .= (!empty($tableName) ? $tableName . '.' : '') . $columnName . ' ' . $operator . ' ?';
583
+			} else {
584
+				$constraintSQL .= $valueFunction . '(' . (!empty($tableName) ? $tableName . '.' : '') . $columnName . ') ' . $operator . ' ?';
585
+			}
586
+
587
+			if (isset($tableName) && !empty($this->currentChildTableNameAlias)) {
588
+				$constraintSQL = $this->replaceTableNameByAlias($tableName, $this->currentChildTableNameAlias, $constraintSQL);
589
+			}
590
+			$sql['where'][] = $constraintSQL;
591
+		}
592
+	}
593
+
594
+	/**
595
+	 * @param string &$tableName
596
+	 * @param array &$propertyPath
597
+	 * @param array &$sql
598
+	 * @throws Exception
599
+	 * @throws Exception\InvalidRelationConfigurationException
600
+	 * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\MissingColumnMapException
601
+	 */
602
+	protected function addUnionStatement(&$tableName, &$propertyPath, array &$sql)
603
+	{
604
+
605
+		$table = Tca::table($tableName);
606
+
607
+		$explodedPropertyPath = explode('.', $propertyPath, 2);
608
+		$fieldName = $explodedPropertyPath[0];
609
+
610
+		// Field of type "group" are special because property path must contain the table name
611
+		// to determine the relation type. Example for sys_category, property path will look like "items.sys_file"
612
+		if ($table->field($fieldName)->isGroup()) {
613
+			$parts = explode('.', $propertyPath, 3);
614
+			$explodedPropertyPath[0] = $parts[0] . '.' . $parts[1];
615
+			$explodedPropertyPath[1] = $parts[2];
616
+			$fieldName = $explodedPropertyPath[0];
617
+		}
618
+
619
+		$parentKeyFieldName = $table->field($fieldName)->getForeignField();
620
+		$childTableName = $table->field($fieldName)->getForeignTable();
621
+
622
+		if ($childTableName === NULL) {
623
+			throw new Exception\InvalidRelationConfigurationException('The relation information for property "' . $fieldName . '" of class "' . $tableName . '" is missing.', 1353170925);
624
+		}
625
+
626
+		if ($table->field($fieldName)->hasOne()) { // includes relation "one-to-one" and "many-to-one"
627
+			// sometimes the opposite relation is not defined. We don't want to force this config for backward compatibility reasons.
628
+			// $parentKeyFieldName === NULL does the trick somehow. Before condition was if (isset($parentKeyFieldName))
629
+			if ($table->field($fieldName)->hasRelationManyToOne() || $parentKeyFieldName === NULL) {
630
+				$sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.' . $fieldName . '=' . $childTableName . '.uid';
631
+			} else {
632
+				$sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.uid=' . $childTableName . '.' . $parentKeyFieldName;
633
+			}
634
+		} elseif ($table->field($fieldName)->hasRelationManyToMany()) {
635
+			$relationTableName = $table->field($fieldName)->getManyToManyTable();
636
+
637
+			$parentKeyFieldName = $table->field($fieldName)->isOppositeRelation() ? 'uid_foreign' : 'uid_local';
638
+			$childKeyFieldName = !$table->field($fieldName)->isOppositeRelation() ? 'uid_foreign' : 'uid_local';
639
+
640
+			// MM table e.g sys_category_record_mm
641
+			$relationTableNameAlias = $this->generateAlias($relationTableName);
642
+			$join = sprintf(
643
+				'LEFT JOIN %s AS %s ON %s.uid=%s.%s', $relationTableName,
644
+				$relationTableNameAlias,
645
+				$tableName,
646
+				$relationTableNameAlias,
647
+				$parentKeyFieldName
648
+			);
649
+			$sql['unions'][$relationTableNameAlias] = $join;
650
+
651
+			// Foreign table e.g sys_category
652
+			$childTableNameAlias = $this->generateAlias($childTableName);
653
+			$this->currentChildTableNameAlias = $childTableNameAlias;
654
+			$join = sprintf(
655
+				'LEFT JOIN %s AS %s ON %s.%s=%s.uid',
656
+				$childTableName,
657
+				$childTableNameAlias,
658
+				$relationTableNameAlias,
659
+				$childKeyFieldName,
660
+				$childTableNameAlias
661
+			);
662
+			$sql['unions'][$childTableNameAlias] = $join;
663
+
664
+			// Find a possible table name for a MM condition.
665
+			$tableNameCondition = $table->field($fieldName)->getAdditionalTableNameCondition();
666
+			if ($tableNameCondition) {
667
+
668
+				// If we can find a source file name,  we can then retrieve more MM conditions from the TCA such as a field name.
669
+				$sourceFileName = $this->query->getSourceFieldName();
670
+				if (empty($sourceFileName)) {
671
+					$additionalMMConditions = array(
672
+						'tablenames' => $tableNameCondition,
673
+					);
674
+				} else {
675
+					$additionalMMConditions = Tca::table($tableNameCondition)->field($sourceFileName)->getAdditionalMMCondition();
676
+				}
677
+
678
+				foreach ($additionalMMConditions as $additionalFieldName => $additionalMMCondition) {
679
+					$additionalJoin = sprintf(' AND %s.%s = "%s"', $relationTableNameAlias, $additionalFieldName, $additionalMMCondition);
680
+					$sql['unions'][$relationTableNameAlias] .= $additionalJoin;
681
+
682
+					$additionalJoin = sprintf(' AND %s.%s = "%s"', $relationTableNameAlias, $additionalFieldName, $additionalMMCondition);
683
+					$sql['unions'][$childTableNameAlias] .= $additionalJoin;
684
+				}
685
+
686
+			}
687
+
688
+
689
+		} elseif ($table->field($fieldName)->hasMany()) { // includes relations "many-to-one" and "csv" relations
690
+			$childTableNameAlias = $this->generateAlias($childTableName);
691
+			$this->currentChildTableNameAlias = $childTableNameAlias;
692
+
693
+			if (isset($parentKeyFieldName)) {
694
+				$join = sprintf(
695
+					'LEFT JOIN %s AS %s ON %s.uid=%s.%s',
696
+					$childTableName,
697
+					$childTableNameAlias,
698
+					$tableName,
699
+					$childTableNameAlias,
700
+					$parentKeyFieldName
701
+				);
702
+				$sql['unions'][$childTableNameAlias] = $join;
703
+			} else {
704
+				$join = sprintf(
705
+					'LEFT JOIN %s AS %s ON (FIND_IN_SET(%s.uid, %s.%s))',
706
+					$childTableName,
707
+					$childTableNameAlias,
708
+					$childTableNameAlias,
709
+					$tableName,
710
+					$fieldName
711
+				);
712
+				$sql['unions'][$childTableNameAlias] = $join;
713
+			}
714
+		} else {
715
+			throw new Exception('Could not determine type of relation.', 1252502725);
716
+		}
717
+
718
+		// TODO check if there is another solution for this
719
+		$sql['keywords']['distinct'] = 'DISTINCT';
720
+		$propertyPath = $explodedPropertyPath[1];
721
+		$tableName = $childTableName;
722
+	}
723
+
724
+	/**
725
+	 * Returns the SQL operator for the given JCR operator type.
726
+	 *
727
+	 * @param string $operator One of the JCR_OPERATOR_* constants
728
+	 * @throws Exception
729
+	 * @return string an SQL operator
730
+	 */
731
+	protected function resolveOperator($operator)
732
+	{
733
+		switch ($operator) {
734
+			case self::OPERATOR_EQUAL_TO_NULL:
735
+				$operator = 'IS';
736
+				break;
737
+			case self::OPERATOR_NOT_EQUAL_TO_NULL:
738
+				$operator = 'IS NOT';
739
+				break;
740
+			case QueryInterface::OPERATOR_IN:
741
+				$operator = 'IN';
742
+				break;
743
+			case QueryInterface::OPERATOR_EQUAL_TO:
744
+				$operator = '=';
745
+				break;
746
+			case QueryInterface::OPERATOR_NOT_EQUAL_TO:
747
+				$operator = '!=';
748
+				break;
749
+			case QueryInterface::OPERATOR_LESS_THAN:
750
+				$operator = '<';
751
+				break;
752
+			case QueryInterface::OPERATOR_LESS_THAN_OR_EQUAL_TO:
753
+				$operator = '<=';
754
+				break;
755
+			case QueryInterface::OPERATOR_GREATER_THAN:
756
+				$operator = '>';
757
+				break;
758
+			case QueryInterface::OPERATOR_GREATER_THAN_OR_EQUAL_TO:
759
+				$operator = '>=';
760
+				break;
761
+			case QueryInterface::OPERATOR_LIKE:
762
+				$operator = 'LIKE';
763
+				break;
764
+			default:
765
+				throw new Exception('Unsupported operator encountered.', 1242816073);
766
+		}
767
+		return $operator;
768
+	}
769
+
770
+	/**
771
+	 * Replace query placeholders in a query part by the given
772
+	 * parameters.
773
+	 *
774
+	 * @param string &$sqlString The query part with placeholders
775
+	 * @param array $parameters The parameters
776
+	 * @param string $tableName
777
+	 *
778
+	 * @throws Exception
779
+	 */
780
+	protected function replacePlaceholders(&$sqlString, array $parameters, $tableName = 'foo')
781
+	{
782
+		// TODO profile this method again
783
+		if (substr_count($sqlString, '?') !== count($parameters)) {
784
+			throw new Exception('The number of question marks to replace must be equal to the number of parameters.', 1242816074);
785
+		}
786
+		$offset = 0;
787
+		foreach ($parameters as $parameter) {
788
+			$markPosition = strpos($sqlString, '?', $offset);
789
+			if ($markPosition !== FALSE) {
790
+				if ($parameter === NULL) {
791
+					$parameter = 'NULL';
792
+				} elseif (is_array($parameter) || $parameter instanceof \ArrayAccess || $parameter instanceof \Traversable) {
793
+					$items = array();
794
+					foreach ($parameter as $item) {
795
+						$items[] = $this->databaseHandle->fullQuoteStr($item, $tableName);
796
+					}
797
+					$parameter = '(' . implode(',', $items) . ')';
798
+				} else {
799
+					$parameter = $this->databaseHandle->fullQuoteStr($parameter, $tableName);
800
+				}
801
+				$sqlString = substr($sqlString, 0, $markPosition) . $parameter . substr($sqlString, ($markPosition + 1));
802
+			}
803
+			$offset = $markPosition + strlen($parameter);
804
+		}
805
+	}
806
+
807
+	/**
808
+	 * Adds additional WHERE statements according to the query settings.
809
+	 *
810
+	 * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
811
+	 * @param string $tableNameOrAlias The table name to add the additional where clause for
812
+	 * @param array &$statementParts
813
+	 * @return void
814
+	 */
815
+	protected function addAdditionalWhereClause(QuerySettingsInterface $querySettings, $tableNameOrAlias, &$statementParts)
816
+	{
817
+		$this->addVisibilityConstraintStatement($querySettings, $tableNameOrAlias, $statementParts);
818
+		if ($querySettings->getRespectSysLanguage()) {
819
+			$this->addSysLanguageStatement($tableNameOrAlias, $statementParts, $querySettings);
820
+		}
821
+		if ($querySettings->getRespectStoragePage()) {
822
+			$this->addPageIdStatement($tableNameOrAlias, $statementParts, $querySettings->getStoragePageIds());
823
+		}
824
+	}
825
+
826
+	/**
827
+	 * Adds enableFields and deletedClause to the query if necessary
828
+	 *
829
+	 * @param QuerySettingsInterface $querySettings
830
+	 * @param string $tableNameOrAlias The database table name
831
+	 * @param array &$statementParts The query parts
832
+	 * @return void
833
+	 */
834
+	protected function addVisibilityConstraintStatement(QuerySettingsInterface $querySettings, $tableNameOrAlias, array &$statementParts)
835
+	{
836
+		$statement = '';
837
+		$tableName = $this->resolveTableNameAlias($tableNameOrAlias);
838
+		if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
839
+			$ignoreEnableFields = $querySettings->getIgnoreEnableFields();
840
+			$enableFieldsToBeIgnored = $querySettings->getEnableFieldsToBeIgnored();
841
+			$includeDeleted = $querySettings->getIncludeDeleted();
842
+			if ($this->environmentService->isEnvironmentInFrontendMode()) {
843
+				$statement .= $this->getFrontendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $enableFieldsToBeIgnored, $includeDeleted);
844
+			} else {
845
+				// TYPO3_MODE === 'BE'
846
+				$statement .= $this->getBackendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $includeDeleted);
847
+			}
848
+
849
+			// Remove the prefixing "AND" if any.
850
+			if (!empty($statement)) {
851
+				$statement = strtolower(substr($statement, 1, 3)) === 'and' ? substr($statement, 5) : $statement;
852
+				$statementParts['additionalWhereClause'][$tableNameOrAlias][] = $statement;
853
+			}
854
+		}
855
+	}
856
+
857
+	/**
858
+	 * Returns constraint statement for frontend context
859
+	 *
860
+	 * @param string $tableNameOrAlias
861
+	 * @param boolean $ignoreEnableFields A flag indicating whether the enable fields should be ignored
862
+	 * @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.
863
+	 * @param boolean $includeDeleted A flag indicating whether deleted records should be included
864
+	 * @return string
865
+	 * @throws Exception\InconsistentQuerySettingsException
866
+	 */
867
+	protected function getFrontendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $enableFieldsToBeIgnored = array(), $includeDeleted)
868
+	{
869
+		$statement = '';
870
+		$tableName = $this->resolveTableNameAlias($tableNameOrAlias);
871
+		if ($ignoreEnableFields && !$includeDeleted) {
872
+			if (count($enableFieldsToBeIgnored)) {
873
+				// array_combine() is necessary because of the way \TYPO3\CMS\Frontend\Page\PageRepository::enableFields() is implemented
874
+				$statement .= $this->getPageRepository()->enableFields($tableName, -1, array_combine($enableFieldsToBeIgnored, $enableFieldsToBeIgnored));
875
+			} else {
876
+				$statement .= $this->getPageRepository()->deleteClause($tableName);
877
+			}
878
+		} elseif (!$ignoreEnableFields && !$includeDeleted) {
879
+			$statement .= $this->getPageRepository()->enableFields($tableName);
880
+		} elseif (!$ignoreEnableFields && $includeDeleted) {
881
+			throw new Exception\InconsistentQuerySettingsException('Query setting "ignoreEnableFields=FALSE" can not be used together with "includeDeleted=TRUE" in frontend context.', 1327678173);
882
+		}
883
+		return $this->replaceTableNameByAlias($tableName, $tableNameOrAlias, $statement);
884
+	}
885
+
886
+	/**
887
+	 * Returns constraint statement for backend context
888
+	 *
889
+	 * @param string $tableNameOrAlias
890
+	 * @param boolean $ignoreEnableFields A flag indicating whether the enable fields should be ignored
891
+	 * @param boolean $includeDeleted A flag indicating whether deleted records should be included
892
+	 * @return string
893
+	 */
894
+	protected function getBackendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $includeDeleted)
895
+	{
896
+		$tableName = $this->resolveTableNameAlias($tableNameOrAlias);
897
+		$statement = '';
898
+		if (!$ignoreEnableFields) {
899
+			$statement .= BackendUtility::BEenableFields($tableName);
900
+		}
901
+
902
+		// If the table is found to have "workspace" support, add the corresponding fields in the statement.
903
+		if (Tca::table($tableName)->hasWorkspaceSupport()) {
904
+			if ($this->getBackendUser()->workspace === 0) {
905
+				$statement .= ' AND ' . $tableName . '.t3ver_state<=' . new VersionState(VersionState::DEFAULT_STATE);
906
+			} else {
907
+				// Show only records of live and of the current workspace
908
+				// In case we are in a Versioning preview
909
+				$statement .= ' AND (' .
910
+					$tableName . '.t3ver_wsid=0 OR ' .
911
+					$tableName . '.t3ver_wsid=' . (int)$this->getBackendUser()->workspace .
912
+					')';
913
+			}
914
+
915
+			// Check if this segment make sense here or whether it should be in the "if" part when we have workspace = 0
916
+			$statement .= ' AND ' . $tableName . '.pid<>-1';
917
+		}
918
+
919
+		if (!$includeDeleted) {
920
+			$statement .= BackendUtility::deleteClause($tableName);
921
+		}
922
+
923
+		return $this->replaceTableNameByAlias($tableName, $tableNameOrAlias, $statement);
924
+	}
925
+
926
+	/**
927
+	 * Builds the language field statement
928
+	 *
929
+	 * @param string $tableNameOrAlias The database table name
930
+	 * @param array &$statementParts The query parts
931
+	 * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
932
+	 * @throws Exception
933
+	 * @return void
934
+	 */
935
+	protected function addSysLanguageStatement($tableNameOrAlias, array &$statementParts, $querySettings)
936
+	{
937
+
938
+		$tableName = $this->resolveTableNameAlias($tableNameOrAlias);
939
+		if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
940
+			if (!empty($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])) {
941
+				// Select all entries for the current language
942
+				$additionalWhereClause = $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . ' IN (' . intval($querySettings->getLanguageUid()) . ',-1)';
943
+				// If any language is set -> get those entries which are not translated yet
944
+				// They will be removed by t3lib_page::getRecordOverlay if not matching overlay mode
945
+				if (isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
946
+					&& $querySettings->getLanguageUid() > 0
947
+				) {
948
+					$additionalWhereClause .= ' OR (' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=0' .
949
+						' AND ' . $tableName . '.uid NOT IN (SELECT ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] .
950
+						' FROM ' . $tableName .
951
+						' WHERE ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] . '>0' .
952
+						' AND ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '>0';
953
+
954
+					// Add delete clause to ensure all entries are loaded
955
+					if (isset($GLOBALS['TCA'][$tableName]['ctrl']['delete'])) {
956
+						$additionalWhereClause .= ' AND ' . $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['delete'] . '=0';
957
+					}
958
+					$additionalWhereClause .= '))';
959
+					throw new Exception('Not tested code! It will fail', 1412928284);
960
+				}
961
+				$statementParts['additionalWhereClause'][$tableNameOrAlias][] = '(' . $additionalWhereClause . ')';
962
+			}
963
+		}
964
+	}
965
+
966
+	/**
967
+	 * Builds the page ID checking statement
968
+	 *
969
+	 * @param string $tableNameOrAlias The database table name
970
+	 * @param array &$statementParts The query parts
971
+	 * @param array $storagePageIds list of storage page ids
972
+	 * @throws Exception\InconsistentQuerySettingsException
973
+	 * @return void
974
+	 */
975
+	protected function addPageIdStatement($tableNameOrAlias, array &$statementParts, array $storagePageIds)
976
+	{
977
+
978
+		$tableName = $this->resolveTableNameAlias($tableNameOrAlias);
979
+		$tableColumns = $this->tableColumnCache->get($tableName);
980
+		if ($tableColumns === FALSE) {
981
+			$tableColumns = $this->databaseHandle->admin_get_fields($tableName);
982
+			$this->tableColumnCache->set($tableName, $tableColumns);
983
+		}
984
+		if (is_array($GLOBALS['TCA'][$tableName]['ctrl']) && array_key_exists('pid', $tableColumns)) {
985
+			$rootLevel = (int)$GLOBALS['TCA'][$tableName]['ctrl']['rootLevel'];
986
+			if ($rootLevel) {
987
+				if ($rootLevel === 1) {
988
+					$statementParts['additionalWhereClause'][$tableNameOrAlias][] = $tableNameOrAlias . '.pid = 0';
989
+				}
990
+			} else {
991
+				if (empty($storagePageIds)) {
992
+					throw new Exception\InconsistentQuerySettingsException('Missing storage page ids.', 1365779762);
993
+				}
994
+				$statementParts['additionalWhereClause'][$tableNameOrAlias][] = $tableNameOrAlias . '.pid IN (' . implode(', ', $storagePageIds) . ')';
995
+			}
996
+		}
997
+	}
998
+
999
+	/**
1000
+	 * Transforms orderings into SQL.
1001
+	 *
1002
+	 * @param array $orderings An array of orderings (Tx_Extbase_Persistence_QOM_Ordering)
1003
+	 * @param SourceInterface $source The source
1004
+	 * @param array &$sql The query parts
1005
+	 * @throws Exception\UnsupportedOrderException
1006
+	 * @return void
1007
+	 */
1008
+	protected function parseOrderings(array $orderings, SourceInterface $source, array &$sql)
1009
+	{
1010
+		foreach ($orderings as $fieldNameAndPath => $order) {
1011
+			switch ($order) {
1012
+				case QueryInterface::ORDER_ASCENDING:
1013
+					$order = 'ASC';
1014
+					break;
1015
+				case QueryInterface::ORDER_DESCENDING:
1016
+					$order = 'DESC';
1017
+					break;
1018
+				default:
1019
+					throw new Exception\UnsupportedOrderException('Unsupported order encountered.', 1242816074);
1020
+			}
1021
+
1022
+			$tableName = $this->getFieldPathResolver()->getDataType($fieldNameAndPath, $this->query->getType());
1023
+			$fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath, $tableName);
1024
+			$sql['orderings'][] = sprintf('%s.%s %s', $tableName, $fieldName, $order);
1025
+		}
1026
+	}
1027
+
1028
+	/**
1029
+	 * Transforms limit and offset into SQL
1030
+	 *
1031
+	 * @param int $limit
1032
+	 * @param int $offset
1033
+	 * @param array &$sql
1034
+	 * @return void
1035
+	 */
1036
+	protected function parseLimitAndOffset($limit, $offset, array &$sql)
1037
+	{
1038
+		if ($limit !== NULL && $offset !== NULL) {
1039
+			$sql['limit'] = intval($offset) . ', ' . intval($limit);
1040
+		} elseif ($limit !== NULL) {
1041
+			$sql['limit'] = intval($limit);
1042
+		}
1043
+	}
1044
+
1045
+	/**
1046
+	 * Transforms a Resource from a database query to an array of rows.
1047
+	 *
1048
+	 * @param resource $result The result
1049
+	 * @return array The result as an array of rows (tuples)
1050
+	 */
1051
+	protected function getRowsFromResult($result)
1052
+	{
1053
+		$rows = array();
1054
+		while ($row = $this->databaseHandle->sql_fetch_assoc($result)) {
1055
+			if (is_array($row)) {
1056
+
1057
+				// Get language uid from querySettings.
1058
+				// Ensure the backend handling is not broken (fallback to Get parameter 'L' if needed)
1059
+				$overlaidRow = $this->doLanguageAndWorkspaceOverlay($this->query->getSource(), $row, $this->query->getQuerySettings());
1060
+				$contentObject = GeneralUtility::makeInstance($this->objectType, $this->query->getType(), $overlaidRow);
1061
+				$rows[] = $contentObject;
1062
+			}
1063
+		}
1064
+
1065
+		return $rows;
1066
+	}
1067
+
1068
+	/**
1069
+	 * Performs workspace and language overlay on the given row array. The language and workspace id is automatically
1070
+	 * detected (depending on FE or BE context). You can also explicitly set the language/workspace id.
1071
+	 *
1072
+	 * @param SourceInterface $source The source (selector od join)
1073
+	 * @param array $row
1074
+	 * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
1075
+	 * @return array
1076
+	 */
1077
+	protected function doLanguageAndWorkspaceOverlay(SourceInterface $source, array $row, $querySettings)
1078
+	{
1079
+
1080
+		/** @var SelectorInterface $source */
1081
+		$tableName = $source->getSelectorName();
1082
+
1083
+		$pageRepository = $this->getPageRepository();
1084
+		if (is_object($GLOBALS['TSFE'])) {
1085
+			$languageMode = $GLOBALS['TSFE']->sys_language_mode;
1086
+			if ($this->isBackendUserLogged() && $this->getBackendUser()->workspace !== 0) {
1087
+				$pageRepository->versioningWorkspaceId = $this->getBackendUser()->workspace;
1088
+			}
1089
+		} else {
1090
+			$languageMode = '';
1091
+			$workspaceUid = $this->getBackendUser()->workspace;
1092
+			$pageRepository->versioningWorkspaceId = $workspaceUid;
1093
+			if ($this->getBackendUser()->workspace !== 0) {
1094
+				$pageRepository->versioningPreview = 1;
1095
+			}
1096
+		}
1097
+
1098
+		// If current row is a translation select its parent
1099
+		if (isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
1100
+			&& isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
1101
+		) {
1102
+			if (isset($row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']])
1103
+				&& $row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']] > 0
1104
+			) {
1105
+				$row = $this->databaseHandle->exec_SELECTgetSingleRow(
1106
+					$tableName . '.*',
1107
+					$tableName,
1108
+					$tableName . '.uid=' . (int)$row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']] .
1109
+					' AND ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=0'
1110
+				);
1111
+			}
1112
+		}
1113
+
1114
+		// Retrieve the original uid
1115
+		// @todo It looks for me this code will never be used! "_ORIG_uid" is something from extbase. Adjust me or remove me in 0.4 + 2 version!
1116
+		$pageRepository->versionOL($tableName, $row, TRUE);
1117
+		if ($pageRepository->versioningPreview && isset($row['_ORIG_uid'])) {
1118
+			$row['uid'] = $row['_ORIG_uid'];
1119
+		}
1120
+
1121
+		// Special case for table "pages"
1122
+		if ($tableName == 'pages') {
1123
+			$row = $pageRepository->getPageOverlay($row, $querySettings->getLanguageUid());
1124
+		} elseif (isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
1125
+			&& $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] !== ''
1126
+		) {
1127
+			if (in_array($row[$GLOBALS['TCA'][$tableName]['ctrl']['languageField']], array(-1, 0))) {
1128
+				$overlayMode = $languageMode === 'strict' ? 'hideNonTranslated' : '';
1129
+				$row = $pageRepository->getRecordOverlay($tableName, $row, $querySettings->getLanguageUid(), $overlayMode);
1130
+			}
1131
+		}
1132
+
1133
+		return $row;
1134
+	}
1135
+
1136
+	/**
1137
+	 * Return a resolved table name given a possible table name alias.
1138
+	 *
1139
+	 * @param string $tableNameOrAlias
1140
+	 * @return string
1141
+	 */
1142
+	protected function resolveTableNameAlias($tableNameOrAlias)
1143
+	{
1144
+		$resolvedTableName = $tableNameOrAlias;
1145
+		if (!empty($this->tableNameAliases['aliases'][$tableNameOrAlias])) {
1146
+			$resolvedTableName = $this->tableNameAliases['aliases'][$tableNameOrAlias];
1147
+		}
1148
+		return $resolvedTableName;
1149
+	}
1150
+
1151
+	/**
1152
+	 * Generate a unique table name alias for the given table name.
1153
+	 *
1154
+	 * @param string $tableName
1155
+	 * @return string
1156
+	 */
1157
+	protected function generateAlias($tableName)
1158
+	{
1159
+
1160
+		if (!isset($this->tableNameAliases['aliasIncrement'][$tableName])) {
1161
+			$this->tableNameAliases['aliasIncrement'][$tableName] = 0;
1162
+		}
1163
+
1164
+		$numberOfAliases = $this->tableNameAliases['aliasIncrement'][$tableName];
1165
+		$tableNameAlias = $tableName . $numberOfAliases;
1166
+
1167
+		$this->tableNameAliases['aliasIncrement'][$tableName]++;
1168
+		$this->tableNameAliases['aliases'][$tableNameAlias] = $tableName;
1169
+
1170
+		return $tableNameAlias;
1171
+	}
1172
+
1173
+	/**
1174
+	 * Replace the table names by its table name alias within the given statement.
1175
+	 *
1176
+	 * @param string $tableName
1177
+	 * @param string $tableNameAlias
1178
+	 * @param string $statement
1179
+	 * @return string
1180
+	 */
1181
+	protected function replaceTableNameByAlias($tableName, $tableNameAlias, $statement)
1182
+	{
1183
+		if ($statement && $tableName !== $tableNameAlias) {
1184
+			$statement = str_replace($tableName, $tableNameAlias, $statement);
1185
+		}
1186
+		return $statement;
1187
+	}
1188
+
1189
+	/**
1190
+	 * Returns an instance of the current Backend User.
1191
+	 *
1192
+	 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
1193
+	 */
1194
+	protected function getBackendUser()
1195
+	{
1196
+		return $GLOBALS['BE_USER'];
1197
+	}
1198
+
1199
+	/**
1200
+	 * Tell whether a Backend User is logged in.
1201
+	 *
1202
+	 * @return bool
1203
+	 */
1204
+	protected function isBackendUserLogged()
1205
+	{
1206
+		return is_object($GLOBALS['BE_USER']);
1207
+	}
1208
+
1209
+	/**
1210
+	 * @return PageRepository
1211
+	 */
1212
+	protected function getPageRepository()
1213
+	{
1214
+		if (!$this->pageRepository instanceof PageRepository) {
1215
+			if ($this->environmentService->isEnvironmentInFrontendMode() && is_object($GLOBALS['TSFE'])) {
1216
+				$this->pageRepository = $GLOBALS['TSFE']->sys_page;
1217
+			} else {
1218
+				$this->pageRepository = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Page\\PageRepository');
1219
+			}
1220
+		}
1221
+
1222
+		return $this->pageRepository;
1223
+	}
1224
+
1225
+	/**
1226
+	 * @return \Fab\Vidi\Resolver\FieldPathResolver
1227
+	 */
1228
+	protected function getFieldPathResolver()
1229
+	{
1230
+		return GeneralUtility::makeInstance('Fab\Vidi\Resolver\FieldPathResolver');
1231
+	}
1232
+
1233
+	/**
1234
+	 * Checks if there are SQL errors in the last query, and if yes, throw an exception.
1235
+	 *
1236
+	 * @return void
1237
+	 * @param string $sql The SQL statement
1238
+	 * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Storage\Exception\SqlErrorException
1239
+	 */
1240
+	protected function checkSqlErrors($sql = '')
1241
+	{
1242
+		$error = $this->databaseHandle->sql_error();
1243
+		if ($error !== '') {
1244
+			$error .= $sql ? ': ' . $sql : '';
1245
+			throw new \TYPO3\CMS\Extbase\Persistence\Generic\Storage\Exception\SqlErrorException($error, 1247602160);
1246
+		}
1247
+	}
1248 1248
 }
Please login to merge, or discard this patch.