Completed
Pull Request — master (#109)
by
unknown
05:43
created
Classes/Controller/Backend/ContentController.php 1 patch
Indentation   +768 added lines, -768 removed lines patch added patch discarded remove patch
@@ -37,773 +37,773 @@
 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
-        // If we are given a list of uids to export, do not use a limit because we want them all.
103
-        $inCriteria = $matcher->getInCriteria();
104
-        $limit = empty($inCriteria) ? $pager->getLimit() : NULL;
105
-
106
-        // Fetch objects via the Content Service.
107
-        $contentService = $this->getContentService()->findBy($matcher, $order, $limit, $pager->getOffset());
108
-        $pager->setCount($contentService->getNumberOfObjects());
109
-
110
-        // Assign values.
111
-        $this->view->assign('columns', $columns);
112
-        $this->view->assign('objects', $contentService->getObjects());
113
-        $this->view->assign('numberOfObjects', $contentService->getNumberOfObjects());
114
-        $this->view->assign('pager', $pager);
115
-        $this->view->assign('response', $this->response);
116
-    }
117
-
118
-    /**
119
-     * Retrieve Content objects first according to matching criteria and then "update" them.
120
-     * Important to notice the field name can contains a path, e.g. metadata.title and therefore must be analysed.
121
-     *
122
-     * Possible values for $matches:
123
-     * -----------------------------
124
-     *
125
-     * $matches = array(uid => 1), will be taken as $query->equals
126
-     * $matches = array(uid => 1,2,3), will be taken as $query->in
127
-     * $matches = array(field_name1 => bar, field_name2 => bax), will be separated by AND.
128
-     *
129
-     * Possible values for $content:
130
-     * -----------------------------
131
-     *
132
-     * $content = array(field_name => bar)
133
-     * $content = array(field_name => array(value1, value2)) <-- will be CSV converted by "value1,value2"
134
-     *
135
-     * @param string $fieldNameAndPath
136
-     * @param array $content
137
-     * @param array $matches
138
-     * @param string $savingBehavior
139
-     * @param int $language
140
-     * @param array $columns
141
-     * @return string
142
-     * @throws \Fab\Vidi\Exception\InvalidKeyInArrayException
143
-     */
144
-    public function updateAction($fieldNameAndPath, array $content, array $matches = array(), $savingBehavior = SavingBehavior::REPLACE, $language = 0, $columns = array())
145
-    {
146
-
147
-        // Instantiate the Matcher object according different rules.
148
-        $matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
149
-        $order = OrderObjectFactory::getInstance()->getOrder();
150
-
151
-        // Fetch objects via the Content Service.
152
-        $contentService = $this->getContentService()->findBy($matcher, $order);
153
-
154
-        // Get the real field that is going to be updated.
155
-        $updatedFieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath);
156
-
157
-        // Get result object for storing data along the processing.
158
-        $result = $this->getJsonResult();
159
-        $result->setNumberOfObjects($contentService->getNumberOfObjects());
160
-
161
-        foreach ($contentService->getObjects() as $index => $object) {
162
-
163
-            $identifier = $this->getContentObjectResolver()->getValue($object, $fieldNameAndPath, 'uid', $language);
164
-
165
-            // It could be the identifier is not found because the translation
166
-            // of the record does not yet exist when mass-editing
167
-            if ((int)$identifier <= 0) {
168
-                continue;
169
-            }
170
-
171
-            $dataType = $this->getContentObjectResolver()->getDataType($object, $fieldNameAndPath);
172
-
173
-            $signalResult = $this->emitProcessContentDataSignal($object, $fieldNameAndPath, $content, $index + 1, $savingBehavior, $language);
174
-            $contentData = $signalResult->getContentData();
175
-
176
-            // Add identifier to content data, required by TCEMain.
177
-            $contentData['uid'] = $identifier;
178
-
179
-            /** @var Content $dataObject */
180
-            $dataObject = GeneralUtility::makeInstance('Fab\Vidi\Domain\Model\Content', $dataType, $contentData);
181
-
182
-            // Properly update object.
183
-            ContentRepositoryFactory::getInstance($dataType)->update($dataObject);
184
-
185
-            // Get the possible error messages and store them.
186
-            $errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
187
-            $result->addErrorMessages($errorMessages);
188
-
189
-            // We only want to see the detail result if there is one object updated.
190
-            // Required for inline editing + it will display some useful info on the GUI in the flash messages.
191
-            if ($contentService->getNumberOfObjects() === 1) {
192
-
193
-                // Fetch the updated object from repository.
194
-                $updatedObject = ContentRepositoryFactory::getInstance()->findByUid($object->getUid());
195
-
196
-                // Re-fetch the updated result.
197
-                $updatedResult = $this->getContentObjectResolver()->getValue($updatedObject, $fieldNameAndPath, $updatedFieldName, $language);
198
-                if (is_array($updatedResult)) {
199
-                    $_updatedResult = array(); // reset result set.
200
-
201
-                    /** @var Content $contentObject */
202
-                    foreach ($updatedResult as $contentObject) {
203
-                        $labelField = Tca::table($contentObject)->getLabelField();
204
-                        $values = array(
205
-                            'uid' => $contentObject->getUid(),
206
-                            'name' => $contentObject[$labelField],
207
-                        );
208
-                        $_updatedResult[] = $values;
209
-                    }
210
-
211
-                    $updatedResult = $_updatedResult;
212
-                }
213
-
214
-                $labelField = Tca::table($object)->getLabelField();
215
-
216
-                $processedObjectData = array(
217
-                    'uid' => $object->getUid(),
218
-                    'name' => $object[$labelField],
219
-                    'updatedField' => $fieldNameAndPath,
220
-                    'updatedValue' => $updatedResult,
221
-                );
222
-                $result->setProcessedObject($processedObjectData);
223
-
224
-                if (!empty($columns)) {
225
-                    /** @var Row $row */
226
-                    $row = GeneralUtility::makeInstance('Fab\Vidi\View\Grid\Row', $columns);
227
-                    $result->setRow($row->render($updatedObject));
228
-                }
229
-            }
230
-        }
231
-
232
-        // Set the result and render the JSON view.
233
-        $this->getJsonView()->setResult($result);
234
-        return $this->getJsonView()->render();
235
-    }
236
-
237
-    /**
238
-     * Set the sorting of a record giving the previous object.
239
-     *
240
-     * @param array $matches
241
-     * @param int $previousIdentifier
242
-     * @return string
243
-     */
244
-    public function sortAction(array $matches = array(), $previousIdentifier = NULL)
245
-    {
246
-
247
-        $matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
248
-
249
-        // Fetch objects via the Content Service.
250
-        $contentService = $this->getContentService()->findBy($matcher);
251
-
252
-        // Compute the label field name of the table.
253
-        $tableTitleField = Tca::table()->getLabelField();
254
-
255
-        // Get result object for storing data along the processing.
256
-        $result = $this->getJsonResult();
257
-        $result->setNumberOfObjects($contentService->getNumberOfObjects());
258
-
259
-        foreach ($contentService->getObjects() as $object) {
260
-
261
-            // Store the first object, so that the "action" message can be more explicit when deleting only one record.
262
-            if ($contentService->getNumberOfObjects() === 1) {
263
-                $tableTitleValue = $object[$tableTitleField];
264
-                $processedObjectData = array(
265
-                    'uid' => $object->getUid(),
266
-                    'name' => $tableTitleValue,
267
-                );
268
-                $result->setProcessedObject($processedObjectData);
269
-            }
270
-
271
-            // The $target corresponds to the pid to move the records to.
272
-            // It can also be a negative value in case of sorting. The negative value would be the uid of its predecessor.
273
-            $target = is_null($previousIdentifier) ? $object->getPid() : (-(int)$previousIdentifier);
274
-
275
-            // Work out the object.
276
-            ContentRepositoryFactory::getInstance()->move($object, $target);
277
-
278
-            // Get the possible error messages and store them.
279
-            $errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
280
-            $result->addErrorMessages($errorMessages);
281
-        }
282
-
283
-        // Set the result and render the JSON view.
284
-        $this->getJsonView()->setResult($result);
285
-        return $this->getJsonView()->render();
286
-    }
287
-
288
-    /**
289
-     * Returns an editing form for a given field name of a Content object.
290
-     * Argument $fieldNameAndPath corresponds to the field name to be edited.
291
-     * Important to notice it can contains a path, e.g. metadata.title and therefore must be analysed.
292
-     *
293
-     * Possible values for $matches, refer to method "updateAction".
294
-     *
295
-     * @param string $fieldNameAndPath
296
-     * @param array $matches
297
-     * @param bool $hasRecursiveSelection
298
-     * @throws \Exception
299
-     */
300
-    public function editAction($fieldNameAndPath, array $matches = array(), $hasRecursiveSelection = FALSE)
301
-    {
302
-
303
-        // Instantiate the Matcher object according different rules.
304
-        $matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
305
-
306
-        // Fetch objects via the Content Service.
307
-        $contentService = $this->getContentService()->findBy($matcher);
308
-
309
-        $dataType = $this->getFieldPathResolver()->getDataType($fieldNameAndPath);
310
-        $fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath);
311
-
312
-        $fieldType = Tca::table($dataType)->field($fieldName)->getType();
313
-        $this->view->assign('fieldType', ucfirst($fieldType));
314
-        $this->view->assign('dataType', $dataType);
315
-        $this->view->assign('fieldName', $fieldName);
316
-        $this->view->assign('matches', $matches);
317
-        $this->view->assign('fieldNameAndPath', $fieldNameAndPath);
318
-        $this->view->assign('numberOfObjects', $contentService->getNumberOfObjects());
319
-        $this->view->assign('hasRecursiveSelection', $hasRecursiveSelection);
320
-        $this->view->assign('editWholeSelection', empty($matches['uid'])); // necessary??
321
-
322
-        // Fetch content and its relations.
323
-        if ($fieldType === FieldType::MULTISELECT) {
324
-
325
-            $object = ContentRepositoryFactory::getInstance()->findOneBy($matcher);
326
-            $identifier = $this->getContentObjectResolver()->getValue($object, $fieldNameAndPath, 'uid');
327
-            $dataType = $this->getContentObjectResolver()->getDataType($object, $fieldNameAndPath);
328
-
329
-            $content = ContentRepositoryFactory::getInstance($dataType)->findByUid($identifier);
330
-
331
-            // Makes sure the object was retrieved. Security!
332
-            if (!$content) {
333
-                $message = sprintf('I could not retrieved content object of type "%s" with identifier %s.', $dataType, $identifier);
334
-                throw new \Exception($message, 1402350182);
335
-            }
336
-
337
-            $relatedDataType = Tca::table($dataType)->field($fieldName)->getForeignTable();
338
-
339
-            // Initialize the matcher object.
340
-            /** @var \Fab\Vidi\Persistence\Matcher $matcher */
341
-            $matcher = GeneralUtility::makeInstance('Fab\Vidi\Persistence\Matcher', array(), $relatedDataType);
342
-
343
-            // Default ordering for related data type.
344
-            $defaultOrderings = Tca::table($relatedDataType)->getDefaultOrderings();
345
-            /** @var \Fab\Vidi\Persistence\Order $order */
346
-            $defaultOrder = GeneralUtility::makeInstance('Fab\Vidi\Persistence\Order', $defaultOrderings);
347
-
348
-            // Fetch related contents
349
-            $relatedContents = ContentRepositoryFactory::getInstance($relatedDataType)->findBy($matcher, $defaultOrder);
350
-
351
-            if (Tca::table($dataType)->field($fieldName)->isRenderModeTree()) {
352
-
353
-                $fieldConfiguration = Tca::table($dataType)->field($fieldName)->getConfiguration();
354
-                $parentField = $fieldConfiguration['treeConfig']['parentField'];
355
-
356
-                $flatTree = array();
357
-                foreach ($relatedContents as $node) {
358
-                    $flatTree[$node->getUid()] = array(
359
-                        'item' => $node,
360
-                        'parent' => $node[$parentField] ? $node[$parentField]['uid'] : NULL,
361
-                    );
362
-                }
363
-
364
-                $tree = array();
365
-
366
-                // If leaves are selected without its parents selected, those are shown as parent
367
-                foreach ($flatTree as $id => &$flatNode) {
368
-                    if (!isset($flatTree[$flatNode['parent']])) {
369
-                        $flatNode['parent'] = NULL;
370
-                    }
371
-                }
372
-
373
-                foreach ($flatTree as $id => &$node) {
374
-                    if ($node['parent'] === NULL) {
375
-                        $tree[$id] = &$node;
376
-                    } else {
377
-                        $flatTree[$node['parent']]['children'][$id] = &$node;
378
-                    }
379
-                }
380
-
381
-                $relatedContents = $tree;
382
-            }
383
-
384
-            $this->view->assign('content', $content);
385
-            $this->view->assign('relatedContents', $relatedContents);
386
-            $this->view->assign('relatedDataType', $relatedDataType);
387
-            $this->view->assign('relatedContentTitle', Tca::table($relatedDataType)->getTitle());
388
-            $this->view->assign(
389
-                'renderMode',
390
-                Tca::table($dataType)->field($fieldName)->isRenderModeTree() ? FieldType::TREE : NULL
391
-            );
392
-        }
393
-    }
394
-
395
-    /**
396
-     * Retrieve Content objects first according to matching criteria and then "delete" them.
397
-     *
398
-     * Possible values for $matches, refer to method "updateAction".
399
-     *
400
-     * @param array $matches
401
-     * @return string
402
-     */
403
-    public function deleteAction(array $matches = array())
404
-    {
405
-
406
-        $matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
407
-
408
-        // Fetch objects via the Content Service.
409
-        $contentService = $this->getContentService()->findBy($matcher);
410
-
411
-        // Compute the label field name of the table.
412
-        $tableTitleField = Tca::table()->getLabelField();
413
-
414
-        // Get result object for storing data along the processing.
415
-        $result = $this->getJsonResult();
416
-        $result->setNumberOfObjects($contentService->getNumberOfObjects());
417
-
418
-        foreach ($contentService->getObjects() as $object) {
419
-
420
-            // Store the first object, so that the delete message can be more explicit when deleting only one record.
421
-            if ($contentService->getNumberOfObjects() === 1) {
422
-                $tableTitleValue = $object[$tableTitleField];
423
-                $processedObjectData = array(
424
-                    'uid' => $object->getUid(),
425
-                    'name' => $tableTitleValue,
426
-                );
427
-                $result->setProcessedObject($processedObjectData);
428
-            }
429
-
430
-            // Properly delete object.
431
-            ContentRepositoryFactory::getInstance()->remove($object);
432
-
433
-            // Get the possible error messages and store them.
434
-            $errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
435
-            $result->addErrorMessages($errorMessages);
436
-        }
437
-
438
-        // Set the result and render the JSON view.
439
-        $this->getJsonView()->setResult($result);
440
-        return $this->getJsonView()->render();
441
-    }
442
-
443
-    /**
444
-     * Retrieve Content objects first according to matching criteria and then "copy" them.
445
-     *
446
-     * Possible values for $matches, refer to method "updateAction".
447
-     *
448
-     * @param string $target
449
-     * @param array $matches
450
-     * @throws \Exception
451
-     * @return string
452
-     */
453
-    public function copyAction($target, array $matches = array())
454
-    {
455
-        // @todo
456
-        throw new \Exception('Not yet implemented', 1410192546);
457
-    }
458
-
459
-    /**
460
-     * Retrieve Content objects from the Clipboard then "copy" them according to the target.
461
-     *
462
-     * @param string $target
463
-     * @throws \Exception
464
-     * @return string
465
-     */
466
-    public function copyClipboardAction($target)
467
-    {
468
-
469
-        // Retrieve matcher object from clipboard.
470
-        $matcher = $this->getClipboardService()->getMatcher();
471
-
472
-        // Fetch objects via the Content Service.
473
-        $contentService = $this->getContentService()->findBy($matcher);
474
-
475
-        // Compute the label field name of the table.
476
-        $tableTitleField = Tca::table()->getLabelField();
477
-
478
-        // Get result object for storing data along the processing.
479
-        $result = $this->getJsonResult();
480
-        $result->setNumberOfObjects($contentService->getNumberOfObjects());
481
-
482
-        foreach ($contentService->getObjects() as $object) {
483
-
484
-            // Store the first object, so that the "action" message can be more explicit when deleting only one record.
485
-            if ($contentService->getNumberOfObjects() === 1) {
486
-                $tableTitleValue = $object[$tableTitleField];
487
-                $processedObjectData = array(
488
-                    'uid' => $object->getUid(),
489
-                    'name' => $tableTitleValue,
490
-                );
491
-                $result->setProcessedObject($processedObjectData);
492
-            }
493
-
494
-            // Work out the object.
495
-            ContentRepositoryFactory::getInstance()->copy($object, $target);
496
-
497
-            // Get the possible error messages and store them.
498
-            $errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
499
-            $result->addErrorMessages($errorMessages);
500
-        }
501
-
502
-        // Flush Clipboard if told so.
503
-        if (GeneralUtility::_GP('flushClipboard')) {
504
-            $this->getClipboardService()->flush();
505
-        }
506
-
507
-        // Set the result and render the JSON view.
508
-        $this->getJsonView()->setResult($result);
509
-        return $this->getJsonView()->render();
510
-    }
511
-
512
-    /**
513
-     * Retrieve Content objects first according to matching criteria and then "move" them.
514
-     *
515
-     * Possible values for $matches, refer to method "updateAction".
516
-     *
517
-     * @param string $target
518
-     * @param array $matches
519
-     * @return string
520
-     */
521
-    public function moveAction($target, array $matches = array())
522
-    {
523
-
524
-        $matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
525
-
526
-        // Fetch objects via the Content Service.
527
-        $contentService = $this->getContentService()->findBy($matcher);
528
-
529
-        // Compute the label field name of the table.
530
-        $tableTitleField = Tca::table()->getLabelField();
531
-
532
-        // Get result object for storing data along the processing.
533
-        $result = $this->getJsonResult();
534
-        $result->setNumberOfObjects($contentService->getNumberOfObjects());
535
-
536
-        foreach ($contentService->getObjects() as $object) {
537
-
538
-            // Store the first object, so that the "action" message can be more explicit when deleting only one record.
539
-            if ($contentService->getNumberOfObjects() === 1) {
540
-                $tableTitleValue = $object[$tableTitleField];
541
-                $processedObjectData = array(
542
-                    'uid' => $object->getUid(),
543
-                    'name' => $tableTitleValue,
544
-                );
545
-                $result->setProcessedObject($processedObjectData);
546
-            }
547
-
548
-            // Work out the object.
549
-            ContentRepositoryFactory::getInstance()->move($object, $target);
550
-
551
-            // Get the possible error messages and store them.
552
-            $errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
553
-            $result->addErrorMessages($errorMessages);
554
-        }
555
-
556
-        // Set the result and render the JSON view.
557
-        $this->getJsonView()->setResult($result);
558
-        return $this->getJsonView()->render();
559
-    }
560
-
561
-    /**
562
-     * Retrieve Content objects from the Clipboard then "move" them according to the target.
563
-     *
564
-     * @param string $target
565
-     * @return string
566
-     */
567
-    public function moveClipboardAction($target)
568
-    {
569
-
570
-        // Retrieve matcher object from clipboard.
571
-        $matcher = $this->getClipboardService()->getMatcher();
572
-
573
-        // Fetch objects via the Content Service.
574
-        $contentService = $this->getContentService()->findBy($matcher);
575
-
576
-        // Compute the label field name of the table.
577
-        $tableTitleField = Tca::table()->getLabelField();
578
-
579
-        // Get result object for storing data along the processing.
580
-        $result = $this->getJsonResult();
581
-        $result->setNumberOfObjects($contentService->getNumberOfObjects());
582
-
583
-        foreach ($contentService->getObjects() as $object) {
584
-
585
-            // Store the first object, so that the "action" message can be more explicit when deleting only one record.
586
-            if ($contentService->getNumberOfObjects() === 1) {
587
-                $tableTitleValue = $object[$tableTitleField];
588
-                $processedObjectData = array(
589
-                    'uid' => $object->getUid(),
590
-                    'name' => $tableTitleValue,
591
-                );
592
-                $result->setProcessedObject($processedObjectData);
593
-            }
594
-
595
-            // Work out the object.
596
-            ContentRepositoryFactory::getInstance()->move($object, $target);
597
-
598
-            // Get the possible error messages and store them.
599
-            $errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
600
-            $result->addErrorMessages($errorMessages);
601
-        }
602
-
603
-        // Flush Clipboard if told so.
604
-        if (GeneralUtility::_GP('flushClipboard')) {
605
-            $this->getClipboardService()->flush();
606
-        }
607
-
608
-        // Set the result and render the JSON view.
609
-        $this->getJsonView()->setResult($result);
610
-        return $this->getJsonView()->render();
611
-    }
612
-
613
-    /**
614
-     * Retrieve Content objects first according to matching criteria and then "localize" them.
615
-     *
616
-     * Possible values for $matches, refer to method "updateAction".
617
-     *
618
-     * @param string $fieldNameAndPath
619
-     * @param array $matches
620
-     * @param int $language
621
-     * @return string
622
-     * @throws \Exception
623
-     */
624
-    public function localizeAction($fieldNameAndPath, array $matches = array(), $language = 0)
625
-    {
626
-
627
-        $matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
628
-
629
-        // Fetch objects via the Content Service.
630
-        $contentService = $this->getContentService()->findBy($matcher);
631
-
632
-        // Get result object for storing data along the processing.
633
-        $result = $this->getJsonResult();
634
-        $result->setNumberOfObjects($contentService->getNumberOfObjects());
635
-
636
-        foreach ($contentService->getObjects() as $object) {
637
-
638
-            $identifier = $this->getContentObjectResolver()->getValue($object, $fieldNameAndPath, 'uid');
639
-            $dataType = $this->getContentObjectResolver()->getDataType($object, $fieldNameAndPath);
640
-
641
-            // Fetch the source object to be localized.
642
-            /** @var Content $content */
643
-            $content = ContentRepositoryFactory::getInstance($dataType)->findByIdentifier($identifier);
644
-
645
-            // Makes sure the object was retrieved. Security!
646
-            if (!$content) {
647
-                $message = sprintf('Something went wrong when retrieving content "%s" with identifier "%s".', $dataType, $identifier);
648
-                throw new \Exception($message, 1412343097);
649
-            }
650
-
651
-            // Handover the localization to the Repository.
652
-            ContentRepositoryFactory::getInstance($dataType)->localize($content, $language);
653
-
654
-            // Get the possible error messages and store them.
655
-            $errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
656
-
657
-            // Redirect to TCEForm so that the BE User can do its job!
658
-            if ($contentService->getNumberOfObjects() === 1) {
659
-
660
-                if (!empty($errorMessages)) {
661
-                    $message = sprintf('Something went wrong when localizing content "%s" with identifier "%s". <br/>%s',
662
-                        $dataType,
663
-                        $identifier,
664
-                        implode('<br/>', $errorMessages)
665
-                    );
666
-                    throw new \Exception($message, 1412343098);
667
-                }
668
-
669
-                $localizedContent = $this->getLanguageService()->getLocalizedContent($content, $language);
670
-                if (empty($localizedContent)) {
671
-                    $message = sprintf('Oups! I could not retrieve localized content of type "%s" with identifier "%s"',
672
-                        $content->getDataType(),
673
-                        $content->getUid()
674
-                    );
675
-                    throw new \Exception($message, 1412343099);
676
-                }
677
-
678
-                /** @var \Fab\Vidi\View\Uri\EditUri $uri */
679
-                $uriRenderer = GeneralUtility::makeInstance('Fab\Vidi\View\Uri\EditUri');
680
-                $uri = $uriRenderer->render($localizedContent);
681
-                HttpUtility::redirect($uri);
682
-                break; // no need to further continue
683
-            }
684
-
685
-            $result->addErrorMessages($errorMessages);
686
-        }
687
-
688
-        // Set the result and render the JSON view.
689
-        $this->getJsonView()->setResult($result);
690
-        return $this->getJsonView()->render();
691
-    }
692
-
693
-    /**
694
-     * Get the Vidi Module Loader.
695
-     *
696
-     * @return \Fab\Vidi\Service\ContentService
697
-     */
698
-    protected function getContentService()
699
-    {
700
-        return GeneralUtility::makeInstance('Fab\Vidi\Service\ContentService');
701
-    }
702
-
703
-    /**
704
-     * @return \Fab\Vidi\Resolver\ContentObjectResolver
705
-     */
706
-    protected function getContentObjectResolver()
707
-    {
708
-        return GeneralUtility::makeInstance('Fab\Vidi\Resolver\ContentObjectResolver');
709
-    }
710
-
711
-    /**
712
-     * @return \Fab\Vidi\Resolver\FieldPathResolver
713
-     */
714
-    protected function getFieldPathResolver()
715
-    {
716
-        return GeneralUtility::makeInstance('Fab\Vidi\Resolver\FieldPathResolver');
717
-    }
718
-
719
-    /**
720
-     * Return a special view for handling JSON
721
-     * Goal is to have this view injected but require more configuration.
722
-     *
723
-     * @return JsonView
724
-     */
725
-    protected function getJsonView()
726
-    {
727
-        if (!$this->view instanceof JsonView) {
728
-            /** @var JsonView $view */
729
-            $this->view = $this->objectManager->get('Fab\Vidi\Mvc\JsonView');
730
-            $this->view->setResponse($this->response);
731
-        }
732
-        return $this->view;
733
-    }
734
-
735
-    /**
736
-     * @return JsonResult
737
-     */
738
-    protected function getJsonResult()
739
-    {
740
-        return GeneralUtility::makeInstance('Fab\Vidi\Mvc\JsonResult');
741
-    }
742
-
743
-    /**
744
-     * Signal that is called for post-processing content data send to the server for update.
745
-     *
746
-     * @param Content $contentObject
747
-     * @param $fieldNameAndPath
748
-     * @param $contentData
749
-     * @param $counter
750
-     * @param $savingBehavior
751
-     * @param $language
752
-     * @return ProcessContentDataSignalArguments
753
-     * @signal
754
-     */
755
-    protected function emitProcessContentDataSignal(Content $contentObject, $fieldNameAndPath, $contentData, $counter, $savingBehavior, $language)
756
-    {
757
-
758
-        /** @var \Fab\Vidi\Signal\ProcessContentDataSignalArguments $signalArguments */
759
-        $signalArguments = GeneralUtility::makeInstance('Fab\Vidi\Signal\ProcessContentDataSignalArguments');
760
-        $signalArguments->setContentObject($contentObject)
761
-            ->setFieldNameAndPath($fieldNameAndPath)
762
-            ->setContentData($contentData)
763
-            ->setCounter($counter)
764
-            ->setSavingBehavior($savingBehavior)
765
-            ->setLanguage($language);
766
-
767
-        $signalResult = $this->getSignalSlotDispatcher()->dispatch('Fab\Vidi\Controller\Backend\ContentController', 'processContentData', array($signalArguments));
768
-        return $signalResult[0];
769
-    }
770
-
771
-    /**
772
-     * Get the SignalSlot dispatcher.
773
-     *
774
-     * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
775
-     */
776
-    protected function getSignalSlotDispatcher()
777
-    {
778
-        return $this->objectManager->get('TYPO3\\CMS\\Extbase\\SignalSlot\\Dispatcher');
779
-    }
780
-
781
-    /**
782
-     * Get the Clipboard service.
783
-     *
784
-     * @return \Fab\Vidi\Service\ClipboardService
785
-     */
786
-    protected function getClipboardService()
787
-    {
788
-        return GeneralUtility::makeInstance('Fab\Vidi\Service\ClipboardService');
789
-    }
790
-
791
-    /**
792
-     * @return \Fab\Vidi\Language\LanguageService
793
-     */
794
-    protected function getLanguageService()
795
-    {
796
-        return GeneralUtility::makeInstance('Fab\Vidi\Language\LanguageService');
797
-    }
798
-
799
-    /**
800
-     * Get the Vidi Module Loader.
801
-     *
802
-     * @return \Fab\Vidi\Module\ModuleLoader
803
-     */
804
-    protected function getModuleLoader()
805
-    {
806
-        return GeneralUtility::makeInstance('Fab\Vidi\Module\ModuleLoader');
807
-    }
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
+		// If we are given a list of uids to export, do not use a limit because we want them all.
103
+		$inCriteria = $matcher->getInCriteria();
104
+		$limit = empty($inCriteria) ? $pager->getLimit() : NULL;
105
+
106
+		// Fetch objects via the Content Service.
107
+		$contentService = $this->getContentService()->findBy($matcher, $order, $limit, $pager->getOffset());
108
+		$pager->setCount($contentService->getNumberOfObjects());
109
+
110
+		// Assign values.
111
+		$this->view->assign('columns', $columns);
112
+		$this->view->assign('objects', $contentService->getObjects());
113
+		$this->view->assign('numberOfObjects', $contentService->getNumberOfObjects());
114
+		$this->view->assign('pager', $pager);
115
+		$this->view->assign('response', $this->response);
116
+	}
117
+
118
+	/**
119
+	 * Retrieve Content objects first according to matching criteria and then "update" them.
120
+	 * Important to notice the field name can contains a path, e.g. metadata.title and therefore must be analysed.
121
+	 *
122
+	 * Possible values for $matches:
123
+	 * -----------------------------
124
+	 *
125
+	 * $matches = array(uid => 1), will be taken as $query->equals
126
+	 * $matches = array(uid => 1,2,3), will be taken as $query->in
127
+	 * $matches = array(field_name1 => bar, field_name2 => bax), will be separated by AND.
128
+	 *
129
+	 * Possible values for $content:
130
+	 * -----------------------------
131
+	 *
132
+	 * $content = array(field_name => bar)
133
+	 * $content = array(field_name => array(value1, value2)) <-- will be CSV converted by "value1,value2"
134
+	 *
135
+	 * @param string $fieldNameAndPath
136
+	 * @param array $content
137
+	 * @param array $matches
138
+	 * @param string $savingBehavior
139
+	 * @param int $language
140
+	 * @param array $columns
141
+	 * @return string
142
+	 * @throws \Fab\Vidi\Exception\InvalidKeyInArrayException
143
+	 */
144
+	public function updateAction($fieldNameAndPath, array $content, array $matches = array(), $savingBehavior = SavingBehavior::REPLACE, $language = 0, $columns = array())
145
+	{
146
+
147
+		// Instantiate the Matcher object according different rules.
148
+		$matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
149
+		$order = OrderObjectFactory::getInstance()->getOrder();
150
+
151
+		// Fetch objects via the Content Service.
152
+		$contentService = $this->getContentService()->findBy($matcher, $order);
153
+
154
+		// Get the real field that is going to be updated.
155
+		$updatedFieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath);
156
+
157
+		// Get result object for storing data along the processing.
158
+		$result = $this->getJsonResult();
159
+		$result->setNumberOfObjects($contentService->getNumberOfObjects());
160
+
161
+		foreach ($contentService->getObjects() as $index => $object) {
162
+
163
+			$identifier = $this->getContentObjectResolver()->getValue($object, $fieldNameAndPath, 'uid', $language);
164
+
165
+			// It could be the identifier is not found because the translation
166
+			// of the record does not yet exist when mass-editing
167
+			if ((int)$identifier <= 0) {
168
+				continue;
169
+			}
170
+
171
+			$dataType = $this->getContentObjectResolver()->getDataType($object, $fieldNameAndPath);
172
+
173
+			$signalResult = $this->emitProcessContentDataSignal($object, $fieldNameAndPath, $content, $index + 1, $savingBehavior, $language);
174
+			$contentData = $signalResult->getContentData();
175
+
176
+			// Add identifier to content data, required by TCEMain.
177
+			$contentData['uid'] = $identifier;
178
+
179
+			/** @var Content $dataObject */
180
+			$dataObject = GeneralUtility::makeInstance('Fab\Vidi\Domain\Model\Content', $dataType, $contentData);
181
+
182
+			// Properly update object.
183
+			ContentRepositoryFactory::getInstance($dataType)->update($dataObject);
184
+
185
+			// Get the possible error messages and store them.
186
+			$errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
187
+			$result->addErrorMessages($errorMessages);
188
+
189
+			// We only want to see the detail result if there is one object updated.
190
+			// Required for inline editing + it will display some useful info on the GUI in the flash messages.
191
+			if ($contentService->getNumberOfObjects() === 1) {
192
+
193
+				// Fetch the updated object from repository.
194
+				$updatedObject = ContentRepositoryFactory::getInstance()->findByUid($object->getUid());
195
+
196
+				// Re-fetch the updated result.
197
+				$updatedResult = $this->getContentObjectResolver()->getValue($updatedObject, $fieldNameAndPath, $updatedFieldName, $language);
198
+				if (is_array($updatedResult)) {
199
+					$_updatedResult = array(); // reset result set.
200
+
201
+					/** @var Content $contentObject */
202
+					foreach ($updatedResult as $contentObject) {
203
+						$labelField = Tca::table($contentObject)->getLabelField();
204
+						$values = array(
205
+							'uid' => $contentObject->getUid(),
206
+							'name' => $contentObject[$labelField],
207
+						);
208
+						$_updatedResult[] = $values;
209
+					}
210
+
211
+					$updatedResult = $_updatedResult;
212
+				}
213
+
214
+				$labelField = Tca::table($object)->getLabelField();
215
+
216
+				$processedObjectData = array(
217
+					'uid' => $object->getUid(),
218
+					'name' => $object[$labelField],
219
+					'updatedField' => $fieldNameAndPath,
220
+					'updatedValue' => $updatedResult,
221
+				);
222
+				$result->setProcessedObject($processedObjectData);
223
+
224
+				if (!empty($columns)) {
225
+					/** @var Row $row */
226
+					$row = GeneralUtility::makeInstance('Fab\Vidi\View\Grid\Row', $columns);
227
+					$result->setRow($row->render($updatedObject));
228
+				}
229
+			}
230
+		}
231
+
232
+		// Set the result and render the JSON view.
233
+		$this->getJsonView()->setResult($result);
234
+		return $this->getJsonView()->render();
235
+	}
236
+
237
+	/**
238
+	 * Set the sorting of a record giving the previous object.
239
+	 *
240
+	 * @param array $matches
241
+	 * @param int $previousIdentifier
242
+	 * @return string
243
+	 */
244
+	public function sortAction(array $matches = array(), $previousIdentifier = NULL)
245
+	{
246
+
247
+		$matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
248
+
249
+		// Fetch objects via the Content Service.
250
+		$contentService = $this->getContentService()->findBy($matcher);
251
+
252
+		// Compute the label field name of the table.
253
+		$tableTitleField = Tca::table()->getLabelField();
254
+
255
+		// Get result object for storing data along the processing.
256
+		$result = $this->getJsonResult();
257
+		$result->setNumberOfObjects($contentService->getNumberOfObjects());
258
+
259
+		foreach ($contentService->getObjects() as $object) {
260
+
261
+			// Store the first object, so that the "action" message can be more explicit when deleting only one record.
262
+			if ($contentService->getNumberOfObjects() === 1) {
263
+				$tableTitleValue = $object[$tableTitleField];
264
+				$processedObjectData = array(
265
+					'uid' => $object->getUid(),
266
+					'name' => $tableTitleValue,
267
+				);
268
+				$result->setProcessedObject($processedObjectData);
269
+			}
270
+
271
+			// The $target corresponds to the pid to move the records to.
272
+			// It can also be a negative value in case of sorting. The negative value would be the uid of its predecessor.
273
+			$target = is_null($previousIdentifier) ? $object->getPid() : (-(int)$previousIdentifier);
274
+
275
+			// Work out the object.
276
+			ContentRepositoryFactory::getInstance()->move($object, $target);
277
+
278
+			// Get the possible error messages and store them.
279
+			$errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
280
+			$result->addErrorMessages($errorMessages);
281
+		}
282
+
283
+		// Set the result and render the JSON view.
284
+		$this->getJsonView()->setResult($result);
285
+		return $this->getJsonView()->render();
286
+	}
287
+
288
+	/**
289
+	 * Returns an editing form for a given field name of a Content object.
290
+	 * Argument $fieldNameAndPath corresponds to the field name to be edited.
291
+	 * Important to notice it can contains a path, e.g. metadata.title and therefore must be analysed.
292
+	 *
293
+	 * Possible values for $matches, refer to method "updateAction".
294
+	 *
295
+	 * @param string $fieldNameAndPath
296
+	 * @param array $matches
297
+	 * @param bool $hasRecursiveSelection
298
+	 * @throws \Exception
299
+	 */
300
+	public function editAction($fieldNameAndPath, array $matches = array(), $hasRecursiveSelection = FALSE)
301
+	{
302
+
303
+		// Instantiate the Matcher object according different rules.
304
+		$matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
305
+
306
+		// Fetch objects via the Content Service.
307
+		$contentService = $this->getContentService()->findBy($matcher);
308
+
309
+		$dataType = $this->getFieldPathResolver()->getDataType($fieldNameAndPath);
310
+		$fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath);
311
+
312
+		$fieldType = Tca::table($dataType)->field($fieldName)->getType();
313
+		$this->view->assign('fieldType', ucfirst($fieldType));
314
+		$this->view->assign('dataType', $dataType);
315
+		$this->view->assign('fieldName', $fieldName);
316
+		$this->view->assign('matches', $matches);
317
+		$this->view->assign('fieldNameAndPath', $fieldNameAndPath);
318
+		$this->view->assign('numberOfObjects', $contentService->getNumberOfObjects());
319
+		$this->view->assign('hasRecursiveSelection', $hasRecursiveSelection);
320
+		$this->view->assign('editWholeSelection', empty($matches['uid'])); // necessary??
321
+
322
+		// Fetch content and its relations.
323
+		if ($fieldType === FieldType::MULTISELECT) {
324
+
325
+			$object = ContentRepositoryFactory::getInstance()->findOneBy($matcher);
326
+			$identifier = $this->getContentObjectResolver()->getValue($object, $fieldNameAndPath, 'uid');
327
+			$dataType = $this->getContentObjectResolver()->getDataType($object, $fieldNameAndPath);
328
+
329
+			$content = ContentRepositoryFactory::getInstance($dataType)->findByUid($identifier);
330
+
331
+			// Makes sure the object was retrieved. Security!
332
+			if (!$content) {
333
+				$message = sprintf('I could not retrieved content object of type "%s" with identifier %s.', $dataType, $identifier);
334
+				throw new \Exception($message, 1402350182);
335
+			}
336
+
337
+			$relatedDataType = Tca::table($dataType)->field($fieldName)->getForeignTable();
338
+
339
+			// Initialize the matcher object.
340
+			/** @var \Fab\Vidi\Persistence\Matcher $matcher */
341
+			$matcher = GeneralUtility::makeInstance('Fab\Vidi\Persistence\Matcher', array(), $relatedDataType);
342
+
343
+			// Default ordering for related data type.
344
+			$defaultOrderings = Tca::table($relatedDataType)->getDefaultOrderings();
345
+			/** @var \Fab\Vidi\Persistence\Order $order */
346
+			$defaultOrder = GeneralUtility::makeInstance('Fab\Vidi\Persistence\Order', $defaultOrderings);
347
+
348
+			// Fetch related contents
349
+			$relatedContents = ContentRepositoryFactory::getInstance($relatedDataType)->findBy($matcher, $defaultOrder);
350
+
351
+			if (Tca::table($dataType)->field($fieldName)->isRenderModeTree()) {
352
+
353
+				$fieldConfiguration = Tca::table($dataType)->field($fieldName)->getConfiguration();
354
+				$parentField = $fieldConfiguration['treeConfig']['parentField'];
355
+
356
+				$flatTree = array();
357
+				foreach ($relatedContents as $node) {
358
+					$flatTree[$node->getUid()] = array(
359
+						'item' => $node,
360
+						'parent' => $node[$parentField] ? $node[$parentField]['uid'] : NULL,
361
+					);
362
+				}
363
+
364
+				$tree = array();
365
+
366
+				// If leaves are selected without its parents selected, those are shown as parent
367
+				foreach ($flatTree as $id => &$flatNode) {
368
+					if (!isset($flatTree[$flatNode['parent']])) {
369
+						$flatNode['parent'] = NULL;
370
+					}
371
+				}
372
+
373
+				foreach ($flatTree as $id => &$node) {
374
+					if ($node['parent'] === NULL) {
375
+						$tree[$id] = &$node;
376
+					} else {
377
+						$flatTree[$node['parent']]['children'][$id] = &$node;
378
+					}
379
+				}
380
+
381
+				$relatedContents = $tree;
382
+			}
383
+
384
+			$this->view->assign('content', $content);
385
+			$this->view->assign('relatedContents', $relatedContents);
386
+			$this->view->assign('relatedDataType', $relatedDataType);
387
+			$this->view->assign('relatedContentTitle', Tca::table($relatedDataType)->getTitle());
388
+			$this->view->assign(
389
+				'renderMode',
390
+				Tca::table($dataType)->field($fieldName)->isRenderModeTree() ? FieldType::TREE : NULL
391
+			);
392
+		}
393
+	}
394
+
395
+	/**
396
+	 * Retrieve Content objects first according to matching criteria and then "delete" them.
397
+	 *
398
+	 * Possible values for $matches, refer to method "updateAction".
399
+	 *
400
+	 * @param array $matches
401
+	 * @return string
402
+	 */
403
+	public function deleteAction(array $matches = array())
404
+	{
405
+
406
+		$matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
407
+
408
+		// Fetch objects via the Content Service.
409
+		$contentService = $this->getContentService()->findBy($matcher);
410
+
411
+		// Compute the label field name of the table.
412
+		$tableTitleField = Tca::table()->getLabelField();
413
+
414
+		// Get result object for storing data along the processing.
415
+		$result = $this->getJsonResult();
416
+		$result->setNumberOfObjects($contentService->getNumberOfObjects());
417
+
418
+		foreach ($contentService->getObjects() as $object) {
419
+
420
+			// Store the first object, so that the delete message can be more explicit when deleting only one record.
421
+			if ($contentService->getNumberOfObjects() === 1) {
422
+				$tableTitleValue = $object[$tableTitleField];
423
+				$processedObjectData = array(
424
+					'uid' => $object->getUid(),
425
+					'name' => $tableTitleValue,
426
+				);
427
+				$result->setProcessedObject($processedObjectData);
428
+			}
429
+
430
+			// Properly delete object.
431
+			ContentRepositoryFactory::getInstance()->remove($object);
432
+
433
+			// Get the possible error messages and store them.
434
+			$errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
435
+			$result->addErrorMessages($errorMessages);
436
+		}
437
+
438
+		// Set the result and render the JSON view.
439
+		$this->getJsonView()->setResult($result);
440
+		return $this->getJsonView()->render();
441
+	}
442
+
443
+	/**
444
+	 * Retrieve Content objects first according to matching criteria and then "copy" them.
445
+	 *
446
+	 * Possible values for $matches, refer to method "updateAction".
447
+	 *
448
+	 * @param string $target
449
+	 * @param array $matches
450
+	 * @throws \Exception
451
+	 * @return string
452
+	 */
453
+	public function copyAction($target, array $matches = array())
454
+	{
455
+		// @todo
456
+		throw new \Exception('Not yet implemented', 1410192546);
457
+	}
458
+
459
+	/**
460
+	 * Retrieve Content objects from the Clipboard then "copy" them according to the target.
461
+	 *
462
+	 * @param string $target
463
+	 * @throws \Exception
464
+	 * @return string
465
+	 */
466
+	public function copyClipboardAction($target)
467
+	{
468
+
469
+		// Retrieve matcher object from clipboard.
470
+		$matcher = $this->getClipboardService()->getMatcher();
471
+
472
+		// Fetch objects via the Content Service.
473
+		$contentService = $this->getContentService()->findBy($matcher);
474
+
475
+		// Compute the label field name of the table.
476
+		$tableTitleField = Tca::table()->getLabelField();
477
+
478
+		// Get result object for storing data along the processing.
479
+		$result = $this->getJsonResult();
480
+		$result->setNumberOfObjects($contentService->getNumberOfObjects());
481
+
482
+		foreach ($contentService->getObjects() as $object) {
483
+
484
+			// Store the first object, so that the "action" message can be more explicit when deleting only one record.
485
+			if ($contentService->getNumberOfObjects() === 1) {
486
+				$tableTitleValue = $object[$tableTitleField];
487
+				$processedObjectData = array(
488
+					'uid' => $object->getUid(),
489
+					'name' => $tableTitleValue,
490
+				);
491
+				$result->setProcessedObject($processedObjectData);
492
+			}
493
+
494
+			// Work out the object.
495
+			ContentRepositoryFactory::getInstance()->copy($object, $target);
496
+
497
+			// Get the possible error messages and store them.
498
+			$errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
499
+			$result->addErrorMessages($errorMessages);
500
+		}
501
+
502
+		// Flush Clipboard if told so.
503
+		if (GeneralUtility::_GP('flushClipboard')) {
504
+			$this->getClipboardService()->flush();
505
+		}
506
+
507
+		// Set the result and render the JSON view.
508
+		$this->getJsonView()->setResult($result);
509
+		return $this->getJsonView()->render();
510
+	}
511
+
512
+	/**
513
+	 * Retrieve Content objects first according to matching criteria and then "move" them.
514
+	 *
515
+	 * Possible values for $matches, refer to method "updateAction".
516
+	 *
517
+	 * @param string $target
518
+	 * @param array $matches
519
+	 * @return string
520
+	 */
521
+	public function moveAction($target, array $matches = array())
522
+	{
523
+
524
+		$matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
525
+
526
+		// Fetch objects via the Content Service.
527
+		$contentService = $this->getContentService()->findBy($matcher);
528
+
529
+		// Compute the label field name of the table.
530
+		$tableTitleField = Tca::table()->getLabelField();
531
+
532
+		// Get result object for storing data along the processing.
533
+		$result = $this->getJsonResult();
534
+		$result->setNumberOfObjects($contentService->getNumberOfObjects());
535
+
536
+		foreach ($contentService->getObjects() as $object) {
537
+
538
+			// Store the first object, so that the "action" message can be more explicit when deleting only one record.
539
+			if ($contentService->getNumberOfObjects() === 1) {
540
+				$tableTitleValue = $object[$tableTitleField];
541
+				$processedObjectData = array(
542
+					'uid' => $object->getUid(),
543
+					'name' => $tableTitleValue,
544
+				);
545
+				$result->setProcessedObject($processedObjectData);
546
+			}
547
+
548
+			// Work out the object.
549
+			ContentRepositoryFactory::getInstance()->move($object, $target);
550
+
551
+			// Get the possible error messages and store them.
552
+			$errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
553
+			$result->addErrorMessages($errorMessages);
554
+		}
555
+
556
+		// Set the result and render the JSON view.
557
+		$this->getJsonView()->setResult($result);
558
+		return $this->getJsonView()->render();
559
+	}
560
+
561
+	/**
562
+	 * Retrieve Content objects from the Clipboard then "move" them according to the target.
563
+	 *
564
+	 * @param string $target
565
+	 * @return string
566
+	 */
567
+	public function moveClipboardAction($target)
568
+	{
569
+
570
+		// Retrieve matcher object from clipboard.
571
+		$matcher = $this->getClipboardService()->getMatcher();
572
+
573
+		// Fetch objects via the Content Service.
574
+		$contentService = $this->getContentService()->findBy($matcher);
575
+
576
+		// Compute the label field name of the table.
577
+		$tableTitleField = Tca::table()->getLabelField();
578
+
579
+		// Get result object for storing data along the processing.
580
+		$result = $this->getJsonResult();
581
+		$result->setNumberOfObjects($contentService->getNumberOfObjects());
582
+
583
+		foreach ($contentService->getObjects() as $object) {
584
+
585
+			// Store the first object, so that the "action" message can be more explicit when deleting only one record.
586
+			if ($contentService->getNumberOfObjects() === 1) {
587
+				$tableTitleValue = $object[$tableTitleField];
588
+				$processedObjectData = array(
589
+					'uid' => $object->getUid(),
590
+					'name' => $tableTitleValue,
591
+				);
592
+				$result->setProcessedObject($processedObjectData);
593
+			}
594
+
595
+			// Work out the object.
596
+			ContentRepositoryFactory::getInstance()->move($object, $target);
597
+
598
+			// Get the possible error messages and store them.
599
+			$errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
600
+			$result->addErrorMessages($errorMessages);
601
+		}
602
+
603
+		// Flush Clipboard if told so.
604
+		if (GeneralUtility::_GP('flushClipboard')) {
605
+			$this->getClipboardService()->flush();
606
+		}
607
+
608
+		// Set the result and render the JSON view.
609
+		$this->getJsonView()->setResult($result);
610
+		return $this->getJsonView()->render();
611
+	}
612
+
613
+	/**
614
+	 * Retrieve Content objects first according to matching criteria and then "localize" them.
615
+	 *
616
+	 * Possible values for $matches, refer to method "updateAction".
617
+	 *
618
+	 * @param string $fieldNameAndPath
619
+	 * @param array $matches
620
+	 * @param int $language
621
+	 * @return string
622
+	 * @throws \Exception
623
+	 */
624
+	public function localizeAction($fieldNameAndPath, array $matches = array(), $language = 0)
625
+	{
626
+
627
+		$matcher = MatcherObjectFactory::getInstance()->getMatcher($matches);
628
+
629
+		// Fetch objects via the Content Service.
630
+		$contentService = $this->getContentService()->findBy($matcher);
631
+
632
+		// Get result object for storing data along the processing.
633
+		$result = $this->getJsonResult();
634
+		$result->setNumberOfObjects($contentService->getNumberOfObjects());
635
+
636
+		foreach ($contentService->getObjects() as $object) {
637
+
638
+			$identifier = $this->getContentObjectResolver()->getValue($object, $fieldNameAndPath, 'uid');
639
+			$dataType = $this->getContentObjectResolver()->getDataType($object, $fieldNameAndPath);
640
+
641
+			// Fetch the source object to be localized.
642
+			/** @var Content $content */
643
+			$content = ContentRepositoryFactory::getInstance($dataType)->findByIdentifier($identifier);
644
+
645
+			// Makes sure the object was retrieved. Security!
646
+			if (!$content) {
647
+				$message = sprintf('Something went wrong when retrieving content "%s" with identifier "%s".', $dataType, $identifier);
648
+				throw new \Exception($message, 1412343097);
649
+			}
650
+
651
+			// Handover the localization to the Repository.
652
+			ContentRepositoryFactory::getInstance($dataType)->localize($content, $language);
653
+
654
+			// Get the possible error messages and store them.
655
+			$errorMessages = ContentRepositoryFactory::getInstance()->getErrorMessages();
656
+
657
+			// Redirect to TCEForm so that the BE User can do its job!
658
+			if ($contentService->getNumberOfObjects() === 1) {
659
+
660
+				if (!empty($errorMessages)) {
661
+					$message = sprintf('Something went wrong when localizing content "%s" with identifier "%s". <br/>%s',
662
+						$dataType,
663
+						$identifier,
664
+						implode('<br/>', $errorMessages)
665
+					);
666
+					throw new \Exception($message, 1412343098);
667
+				}
668
+
669
+				$localizedContent = $this->getLanguageService()->getLocalizedContent($content, $language);
670
+				if (empty($localizedContent)) {
671
+					$message = sprintf('Oups! I could not retrieve localized content of type "%s" with identifier "%s"',
672
+						$content->getDataType(),
673
+						$content->getUid()
674
+					);
675
+					throw new \Exception($message, 1412343099);
676
+				}
677
+
678
+				/** @var \Fab\Vidi\View\Uri\EditUri $uri */
679
+				$uriRenderer = GeneralUtility::makeInstance('Fab\Vidi\View\Uri\EditUri');
680
+				$uri = $uriRenderer->render($localizedContent);
681
+				HttpUtility::redirect($uri);
682
+				break; // no need to further continue
683
+			}
684
+
685
+			$result->addErrorMessages($errorMessages);
686
+		}
687
+
688
+		// Set the result and render the JSON view.
689
+		$this->getJsonView()->setResult($result);
690
+		return $this->getJsonView()->render();
691
+	}
692
+
693
+	/**
694
+	 * Get the Vidi Module Loader.
695
+	 *
696
+	 * @return \Fab\Vidi\Service\ContentService
697
+	 */
698
+	protected function getContentService()
699
+	{
700
+		return GeneralUtility::makeInstance('Fab\Vidi\Service\ContentService');
701
+	}
702
+
703
+	/**
704
+	 * @return \Fab\Vidi\Resolver\ContentObjectResolver
705
+	 */
706
+	protected function getContentObjectResolver()
707
+	{
708
+		return GeneralUtility::makeInstance('Fab\Vidi\Resolver\ContentObjectResolver');
709
+	}
710
+
711
+	/**
712
+	 * @return \Fab\Vidi\Resolver\FieldPathResolver
713
+	 */
714
+	protected function getFieldPathResolver()
715
+	{
716
+		return GeneralUtility::makeInstance('Fab\Vidi\Resolver\FieldPathResolver');
717
+	}
718
+
719
+	/**
720
+	 * Return a special view for handling JSON
721
+	 * Goal is to have this view injected but require more configuration.
722
+	 *
723
+	 * @return JsonView
724
+	 */
725
+	protected function getJsonView()
726
+	{
727
+		if (!$this->view instanceof JsonView) {
728
+			/** @var JsonView $view */
729
+			$this->view = $this->objectManager->get('Fab\Vidi\Mvc\JsonView');
730
+			$this->view->setResponse($this->response);
731
+		}
732
+		return $this->view;
733
+	}
734
+
735
+	/**
736
+	 * @return JsonResult
737
+	 */
738
+	protected function getJsonResult()
739
+	{
740
+		return GeneralUtility::makeInstance('Fab\Vidi\Mvc\JsonResult');
741
+	}
742
+
743
+	/**
744
+	 * Signal that is called for post-processing content data send to the server for update.
745
+	 *
746
+	 * @param Content $contentObject
747
+	 * @param $fieldNameAndPath
748
+	 * @param $contentData
749
+	 * @param $counter
750
+	 * @param $savingBehavior
751
+	 * @param $language
752
+	 * @return ProcessContentDataSignalArguments
753
+	 * @signal
754
+	 */
755
+	protected function emitProcessContentDataSignal(Content $contentObject, $fieldNameAndPath, $contentData, $counter, $savingBehavior, $language)
756
+	{
757
+
758
+		/** @var \Fab\Vidi\Signal\ProcessContentDataSignalArguments $signalArguments */
759
+		$signalArguments = GeneralUtility::makeInstance('Fab\Vidi\Signal\ProcessContentDataSignalArguments');
760
+		$signalArguments->setContentObject($contentObject)
761
+			->setFieldNameAndPath($fieldNameAndPath)
762
+			->setContentData($contentData)
763
+			->setCounter($counter)
764
+			->setSavingBehavior($savingBehavior)
765
+			->setLanguage($language);
766
+
767
+		$signalResult = $this->getSignalSlotDispatcher()->dispatch('Fab\Vidi\Controller\Backend\ContentController', 'processContentData', array($signalArguments));
768
+		return $signalResult[0];
769
+	}
770
+
771
+	/**
772
+	 * Get the SignalSlot dispatcher.
773
+	 *
774
+	 * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
775
+	 */
776
+	protected function getSignalSlotDispatcher()
777
+	{
778
+		return $this->objectManager->get('TYPO3\\CMS\\Extbase\\SignalSlot\\Dispatcher');
779
+	}
780
+
781
+	/**
782
+	 * Get the Clipboard service.
783
+	 *
784
+	 * @return \Fab\Vidi\Service\ClipboardService
785
+	 */
786
+	protected function getClipboardService()
787
+	{
788
+		return GeneralUtility::makeInstance('Fab\Vidi\Service\ClipboardService');
789
+	}
790
+
791
+	/**
792
+	 * @return \Fab\Vidi\Language\LanguageService
793
+	 */
794
+	protected function getLanguageService()
795
+	{
796
+		return GeneralUtility::makeInstance('Fab\Vidi\Language\LanguageService');
797
+	}
798
+
799
+	/**
800
+	 * Get the Vidi Module Loader.
801
+	 *
802
+	 * @return \Fab\Vidi\Module\ModuleLoader
803
+	 */
804
+	protected function getModuleLoader()
805
+	{
806
+		return GeneralUtility::makeInstance('Fab\Vidi\Module\ModuleLoader');
807
+	}
808 808
 
809 809
 }
Please login to merge, or discard this patch.