Passed
Push — master ( 75b9be...3503df )
by
unknown
56:23 queued 38:54
created

SelectMultipleSideBySideElement::render()   F

Complexity

Conditions 28
Paths > 20000

Size

Total Lines 245
Code Lines 203

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 28
eloc 203
c 1
b 0
f 0
nc 24577
nop 0
dl 0
loc 245
rs 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace TYPO3\CMS\Backend\Form\Element;
3
4
/*
5
 * This file is part of the TYPO3 CMS project.
6
 *
7
 * It is free software; you can redistribute it and/or modify it under
8
 * the terms of the GNU General Public License, either version 2
9
 * of the License, or any later version.
10
 *
11
 * For the full copyright and license information, please read the
12
 * LICENSE.txt file that was distributed with this source code.
13
 *
14
 * The TYPO3 project - inspiring people to share!
15
 */
16
17
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
18
use TYPO3\CMS\Core\Imaging\Icon;
19
use TYPO3\CMS\Core\Localization\LanguageService;
20
use TYPO3\CMS\Core\Utility\ArrayUtility;
21
use TYPO3\CMS\Core\Utility\GeneralUtility;
22
use TYPO3\CMS\Core\Utility\MathUtility;
23
use TYPO3\CMS\Core\Utility\StringUtility;
24
25
/**
26
 * Render a widget with two boxes side by side.
27
 *
28
 * This is rendered for config type=select, renderType=selectMultipleSideBySide set
29
 */
30
class SelectMultipleSideBySideElement extends AbstractFormElement
31
{
32
    /**
33
     * Default field information enabled for this element.
34
     *
35
     * @var array
36
     */
37
    protected $defaultFieldInformation = [
38
        'tcaDescription' => [
39
            'renderType' => 'tcaDescription',
40
        ],
41
    ];
42
43
    /**
44
     * Default field controls for this element.
45
     *
46
     * @var array
47
     */
48
    protected $defaultFieldControl = [
49
        'editPopup' => [
50
            'renderType' => 'editPopup',
51
            'disabled' => true,
52
        ],
53
        'addRecord' => [
54
            'renderType' => 'addRecord',
55
            'disabled' => true,
56
        ],
57
        'listModule' => [
58
            'renderType' => 'listModule',
59
            'disabled' => true,
60
            'after' => [ 'addRecord' ],
61
        ],
62
    ];
63
64
    /**
65
     * Default field wizards enabled for this element.
66
     *
67
     * @var array
68
     */
69
    protected $defaultFieldWizard = [
70
        'localizationStateSelector' => [
71
            'renderType' => 'localizationStateSelector',
72
        ],
73
        'otherLanguageContent' => [
74
            'renderType' => 'otherLanguageContent',
75
            'after' => [
76
                'localizationStateSelector'
77
            ],
78
        ],
79
        'defaultLanguageDifferences' => [
80
            'renderType' => 'defaultLanguageDifferences',
81
            'after' => [
82
                'otherLanguageContent',
83
            ],
84
        ],
85
    ];
86
87
    /**
88
     * Merge field control configuration with default controls and render them.
89
     *
90
     * @return array Result array
91
     */
92
    protected function renderFieldControl(): array
93
    {
94
        $alternativeResult =  [
95
            'additionalJavaScriptPost' => [],
96
            'additionalHiddenFields' => [],
97
            'additionalInlineLanguageLabelFiles' => [],
98
            'stylesheetFiles' => [],
99
            'requireJsModules' => [],
100
            'inlineData' => [],
101
            'html' => '',
102
        ];
103
        $options = $this->data;
104
        $fieldControl = $this->defaultFieldControl;
105
        $fieldControlFromTca = $options['parameterArray']['fieldConf']['config']['fieldControl'] ?? [];
106
        ArrayUtility::mergeRecursiveWithOverrule($fieldControl, $fieldControlFromTca);
107
        $options['renderType'] = 'fieldControl';
108
        if (isset($fieldControl['editPopup'])) {
109
            $editPopupControl = $fieldControl['editPopup'];
110
            unset($fieldControl['editPopup']);
111
            $alternativeOptions = $options;
112
            $alternativeOptions['renderData']['fieldControl'] = ['editPopup' => $editPopupControl];
113
            $alternativeResult = $this->nodeFactory->create($alternativeOptions)->render();
114
        }
115
        $options['renderData']['fieldControl'] = $fieldControl;
116
        return [$this->nodeFactory->create($options)->render(), $alternativeResult];
117
    }
118
119
    /**
120
     * Render side by side element.
121
     *
122
     * @return array As defined in initializeResultArray() of AbstractNode
123
     */
124
    public function render()
125
    {
126
        $filterTextfield = [];
127
        $languageService = $this->getLanguageService();
128
        $resultArray = $this->initializeResultArray();
129
130
        $parameterArray = $this->data['parameterArray'];
131
        $config = $parameterArray['fieldConf']['config'];
132
        $elementName = $parameterArray['itemFormElName'];
133
134
        if ($config['readOnly']) {
135
            // Early return for the relatively simple read only case
136
            return $this->renderReadOnly();
137
        }
138
139
        $possibleItems = $config['items'];
140
        $selectedItems = $parameterArray['itemFormElValue'] ?: [];
141
        $selectedItemsCount = count($selectedItems);
142
143
        $maxItems = $config['maxitems'];
144
        $autoSizeMax = MathUtility::forceIntegerInRange($config['autoSizeMax'], 0);
145
        $size = 2;
146
        if (isset($config['size'])) {
147
            $size = (int)$config['size'];
148
        }
149
        if ($autoSizeMax >= 1) {
150
            $size = MathUtility::forceIntegerInRange($selectedItemsCount + 1, MathUtility::forceIntegerInRange($size, 1), $autoSizeMax);
151
        }
152
        $itemCanBeSelectedMoreThanOnce = !empty($config['multiple']);
153
154
        $listOfSelectedValues = [];
155
        $selectedItemsHtml = [];
156
        foreach ($selectedItems as $itemValue) {
157
            foreach ($possibleItems as $possibleItem) {
158
                if ($possibleItem[1] == $itemValue) {
159
                    $title = $possibleItem[0];
160
                    $listOfSelectedValues[] = $itemValue;
161
                    $selectedItemsHtml[] = '<option value="' . htmlspecialchars($itemValue) . '" title="' . htmlspecialchars($title) . '">' . htmlspecialchars($this->appendValueToLabelInDebugMode($title, $itemValue)) . '</option>';
162
                    break;
163
                }
164
            }
165
        }
166
167
        $selectableItemsHtml = [];
168
        foreach ($possibleItems as $possibleItem) {
169
            $disabledAttr = '';
170
            $classAttr = '';
171
            if (!$itemCanBeSelectedMoreThanOnce && in_array((string)$possibleItem[1], $selectedItems, true)) {
172
                $disabledAttr = ' disabled="disabled"';
173
                $classAttr = ' class="hidden"';
174
            }
175
            $selectableItemsHtml[] =
176
                '<option value="'
177
                    . htmlspecialchars($possibleItem[1])
178
                    . '" title="' . htmlspecialchars($possibleItem[0]) . '"'
179
                    . $classAttr . $disabledAttr
180
                . '>'
181
                    . htmlspecialchars($this->appendValueToLabelInDebugMode($possibleItem[0], $possibleItem[1])) .
182
                '</option>';
183
        }
184
185
        // Html stuff for filter and select filter on top of right side of multi select boxes
186
        $filterTextfield[] = '<span class="input-group input-group-sm">';
187
        $filterTextfield[] =    '<span class="input-group-addon">';
188
        $filterTextfield[] =        '<span class="fa fa-filter"></span>';
189
        $filterTextfield[] =    '</span>';
190
        $filterTextfield[] =    '<input class="t3js-formengine-multiselect-filter-textfield form-control" value="">';
191
        $filterTextfield[] = '</span>';
192
193
        $filterDropDownOptions = [];
194
        if (isset($config['multiSelectFilterItems']) && is_array($config['multiSelectFilterItems']) && count($config['multiSelectFilterItems']) > 1) {
195
            foreach ($config['multiSelectFilterItems'] as $optionElement) {
196
                $value = $languageService->sL($optionElement[0]);
197
                $label = $value;
198
                if (isset($optionElement[1]) && trim($optionElement[1]) !== '') {
199
                    $label = $languageService->sL($optionElement[1]);
200
                }
201
                $filterDropDownOptions[] = '<option value="' . htmlspecialchars($value) . '">' . htmlspecialchars($label) . '</option>';
202
            }
203
        }
204
        $filterHtml = [];
205
        $filterHtml[] = '<div class="form-multigroup-item-wizard">';
206
        if (!empty($filterDropDownOptions)) {
207
            $filterHtml[] = '<div class="t3js-formengine-multiselect-filter-container form-multigroup-wrap">';
208
            $filterHtml[] =     '<div class="form-multigroup-item form-multigroup-element">';
209
            $filterHtml[] =         '<select class="form-control input-sm t3js-formengine-multiselect-filter-dropdown">';
210
            $filterHtml[] =             implode(LF, $filterDropDownOptions);
211
            $filterHtml[] =         '</select>';
212
            $filterHtml[] =     '</div>';
213
            $filterHtml[] =     '<div class="form-multigroup-item form-multigroup-element">';
214
            $filterHtml[] =         implode(LF, $filterTextfield);
215
            $filterHtml[] =     '</div>';
216
            $filterHtml[] = '</div>';
217
        } else {
218
            $filterHtml[] = implode(LF, $filterTextfield);
219
        }
220
        $filterHtml[] = '</div>';
221
222
        $classes = [];
223
        $classes[] = 'form-control';
224
        $classes[] = 'tceforms-multiselect';
225
        if ($maxItems === 1) {
226
            $classes[] = 'form-select-no-siblings';
227
        }
228
        $multipleAttribute = '';
229
        if ($maxItems !== 1 && $size !== 1) {
230
            $multipleAttribute = ' multiple="multiple"';
231
        }
232
233
        $fieldInformationResult = $this->renderFieldInformation();
234
        $fieldInformationHtml = $fieldInformationResult['html'];
235
        $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false);
236
237
        [$fieldControlResult, $alternativeControlResult] = $this->renderFieldControl();
238
        $fieldControlHtml = $fieldControlResult['html'];
239
        $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldControlResult, false);
240
        $alternativeFieldControlHtml = $alternativeControlResult['html'];
241
        $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $alternativeControlResult, false);
242
243
        $fieldWizardResult = $this->renderFieldWizard();
244
        $fieldWizardHtml = $fieldWizardResult['html'];
245
        $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false);
246
247
        $selectedOptionsFieldId = StringUtility::getUniqueId('tceforms-multiselect-');
248
        $availableOptionsFieldId = StringUtility::getUniqueId('tceforms-multiselect-');
249
250
        $html = [];
251
        $html[] = '<div class="formengine-field-item t3js-formengine-field-item">';
252
        $html[] =   $fieldInformationHtml;
253
        $html[] =   '<div class="form-wizards-wrap">';
254
        $html[] =       '<div class="form-wizards-element">';
255
        $html[] =           '<input type="hidden" data-formengine-input-name="' . htmlspecialchars($elementName) . '" value="' . (int)$itemCanBeSelectedMoreThanOnce . '" />';
256
        $html[] =           '<div class="form-multigroup-wrap t3js-formengine-field-group">';
257
        $html[] =               '<div class="form-multigroup-item form-multigroup-element">';
258
        $html[] =                   '<label>';
259
        $html[] =                       htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.selected'));
260
        $html[] =                   '</label>';
261
        $html[] =                   '<div class="form-wizards-wrap form-wizards-aside">';
262
        $html[] =                       '<div class="form-wizards-element">';
263
        $html[] =                           '<select';
264
        $html[] =                               ' id="' . $selectedOptionsFieldId . '"';
265
        $html[] =                               ' size="' . $size . '"';
266
        $html[] =                               ' class="' . implode(' ', $classes) . '"';
267
        $html[] =                               $multipleAttribute;
268
        $html[] =                               ' data-formengine-input-name="' . htmlspecialchars($elementName) . '"';
269
        $html[] =                           '>';
270
        $html[] =                               implode(LF, $selectedItemsHtml);
271
        $html[] =                           '</select>';
272
        $html[] =                       '</div>';
273
        $html[] =                       '<div class="form-wizards-items-aside">';
274
        $html[] =                           '<div class="btn-group-vertical">';
275
        if ($maxItems > 1 && $size >= 5) {
276
            $html[] =                           '<a href="#"';
277
            $html[] =                               ' class="btn btn-default t3js-btn-option t3js-btn-moveoption-top"';
278
            $html[] =                               ' data-fieldname="' . htmlspecialchars($elementName) . '"';
279
            $html[] =                               ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.move_to_top')) . '"';
280
            $html[] =                           '>';
281
            $html[] =                               $this->iconFactory->getIcon('actions-move-to-top', Icon::SIZE_SMALL)->render();
282
            $html[] =                           '</a>';
283
        }
284
        if ($maxItems > 1) {
285
            $html[] =                           '<a href="#"';
286
            $html[] =                               ' class="btn btn-default t3js-btn-option t3js-btn-moveoption-up"';
287
            $html[] =                               ' data-fieldname="' . htmlspecialchars($elementName) . '"';
288
            $html[] =                               ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.move_up')) . '"';
289
            $html[] =                           '>';
290
            $html[] =                               $this->iconFactory->getIcon('actions-move-up', Icon::SIZE_SMALL)->render();
291
            $html[] =                           '</a>';
292
            $html[] =                           '<a href="#"';
293
            $html[] =                               ' class="btn btn-default t3js-btn-option t3js-btn-moveoption-down"';
294
            $html[] =                               ' data-fieldname="' . htmlspecialchars($elementName) . '"';
295
            $html[] =                               ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.move_down')) . '"';
296
            $html[] =                           '>';
297
            $html[] =                               $this->iconFactory->getIcon('actions-move-down', Icon::SIZE_SMALL)->render();
298
            $html[] =                           '</a>';
299
        }
300
        if ($maxItems > 1 && $size >= 5) {
301
            $html[] =                           '<a href="#"';
302
            $html[] =                               ' class="btn btn-default t3js-btn-option t3js-btn-moveoption-bottom"';
303
            $html[] =                               ' data-fieldname="' . htmlspecialchars($elementName) . '"';
304
            $html[] =                               ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.move_to_bottom')) . '"';
305
            $html[] =                           '>';
306
            $html[] =                               $this->iconFactory->getIcon('actions-move-to-bottom', Icon::SIZE_SMALL)->render();
307
            $html[] =                           '</a>';
308
        }
309
        $html[] =                                $alternativeFieldControlHtml;
310
        $html[] =                               '<a href="#"';
311
        $html[] =                                   ' class="btn btn-default t3js-btn-option t3js-btn-removeoption"';
312
        $html[] =                                   ' data-fieldname="' . htmlspecialchars($elementName) . '"';
313
        $html[] =                                   ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.remove_selected')) . '"';
314
        $html[] =                               '>';
315
        $html[] =                                   $this->iconFactory->getIcon('actions-selection-delete', Icon::SIZE_SMALL)->render();
316
        $html[] =                               '</a>';
317
        $html[] =                           '</div>';
318
        $html[] =                       '</div>';
319
        $html[] =                   '</div>';
320
        $html[] =               '</div>';
321
        $html[] =               '<div class="form-multigroup-item form-multigroup-element">';
322
        $html[] =                   '<label>';
323
        $html[] =                       htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.items'));
324
        $html[] =                   '</label>';
325
        $html[] =                   '<div class="form-wizards-wrap form-wizards-aside">';
326
        $html[] =                       '<div class="form-wizards-element">';
327
        $html[] =                           implode(LF, $filterHtml);
328
        $html[] =                           '<select';
329
        $html[] =                               ' data-relatedfieldname="' . htmlspecialchars($elementName) . '"';
330
        $html[] =                               ' data-exclusivevalues="' . htmlspecialchars($config['exclusiveKeys']) . '"';
331
        $html[] =                               ' id="' . $availableOptionsFieldId . '"';
332
        $html[] =                               ' data-formengine-input-name="' . htmlspecialchars($elementName) . '"';
333
        $html[] =                               ' class="form-control t3js-formengine-select-itemstoselect"';
334
        $html[] =                               ' size="' . $size . '"';
335
        $html[] =                               ' onchange="' . htmlspecialchars(implode('', $parameterArray['fieldChangeFunc'])) . '"';
336
        $html[] =                               ' data-formengine-validation-rules="' . htmlspecialchars($this->getValidationDataAsJsonString($config)) . '"';
337
        $html[] =                           '>';
338
        $html[] =                               implode(LF, $selectableItemsHtml);
339
        $html[] =                           '</select>';
340
        $html[] =                       '</div>';
341
        if (!empty($fieldControlHtml)) {
342
            $html[] =                       '<div class="form-wizards-items-aside">';
343
            $html[] =                           '<div class="btn-group-vertical">';
344
            $html[] =                               $fieldControlHtml;
345
            $html[] =                           '</div>';
346
            $html[] =                       '</div>';
347
        }
348
        $html[] =                   '</div>';
349
        $html[] =               '</div>';
350
        $html[] =           '</div>';
351
        $html[] =           '<input type="hidden" name="' . htmlspecialchars($elementName) . '" value="' . htmlspecialchars(implode(',', $listOfSelectedValues)) . '" />';
352
        $html[] =       '</div>';
353
        if (!empty($fieldWizardHtml)) {
354
            $html[] = '<div class="form-wizards-items-bottom">';
355
            $html[] = $fieldWizardHtml;
356
            $html[] = '</div>';
357
        }
358
        $html[] =   '</div>';
359
        $html[] = '</div>';
360
361
        $resultArray['requireJsModules'][] = ['TYPO3/CMS/Backend/FormEngine/Element/SelectMultipleSideBySideElement' => '
362
            function(SelectMultipleSideBySideElement) {
363
                new SelectMultipleSideBySideElement(' . GeneralUtility::quoteJSvalue($selectedOptionsFieldId) . ', ' . GeneralUtility::quoteJSvalue($availableOptionsFieldId) . ');
364
            }'
365
        ];
366
367
        $resultArray['html'] = implode(LF, $html);
368
        return $resultArray;
369
    }
370
371
    /**
372
     * Create HTML of a read only multi select. Right side is not
373
     * rendered, but just the left side with the selected items.
374
     *
375
     * @return array
376
     */
377
    protected function renderReadOnly()
378
    {
379
        $languageService = $this->getLanguageService();
380
        $resultArray = $this->initializeResultArray();
381
382
        $parameterArray = $this->data['parameterArray'];
383
        $config = $parameterArray['fieldConf']['config'];
384
        $fieldName = $parameterArray['itemFormElName'];
385
386
        $possibleItems = $config['items'];
387
        $selectedItems = $parameterArray['itemFormElValue'] ?: [];
388
        if (!is_array($selectedItems)) {
389
            $selectedItems = GeneralUtility::trimExplode(',', $selectedItems, true);
390
        }
391
        $selectedItemsCount = count($selectedItems);
392
393
        $autoSizeMax = MathUtility::forceIntegerInRange($config['autoSizeMax'], 0);
394
        $size = 2;
395
        if (isset($config['size'])) {
396
            $size = (int)$config['size'];
397
        }
398
        if ($autoSizeMax >= 1) {
399
            $size = MathUtility::forceIntegerInRange($selectedItemsCount + 1, MathUtility::forceIntegerInRange($size, 1), $autoSizeMax);
400
        }
401
        $multiple = '';
402
        if ($size !== 1) {
403
            $multiple = ' multiple="multiple"';
404
        }
405
406
        $listOfSelectedValues = [];
407
        $optionsHtml = [];
408
        foreach ($selectedItems as $itemValue) {
409
            foreach ($possibleItems as $possibleItem) {
410
                if ($possibleItem[1] == $itemValue) {
411
                    $title = $possibleItem[0];
412
                    $listOfSelectedValues[] = $itemValue;
413
                    $optionsHtml[] = '<option value="' . htmlspecialchars($itemValue) . '" title="' . htmlspecialchars($title) . '">' . htmlspecialchars($title) . '</option>';
414
                    break;
415
                }
416
            }
417
        }
418
419
        $fieldInformationResult = $this->renderFieldInformation();
420
        $fieldInformationHtml = $fieldInformationResult['html'];
421
        $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false);
422
423
        $html = [];
424
        $html[] = '<div class="formengine-field-item t3js-formengine-field-item">';
425
        $html[] =   $fieldInformationHtml;
426
        $html[] =   '<div class="form-wizards-wrap">';
427
        $html[] =       '<div class="form-wizards-element">';
428
        $html[] =           '<label>';
429
        $html[] =               htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.selected'));
430
        $html[] =           '</label>';
431
        $html[] =           '<div class="form-wizards-wrap form-wizards-aside">';
432
        $html[] =               '<div class="form-wizards-element">';
433
        $html[] =                   '<select';
434
        $html[] =                       ' id="' . StringUtility::getUniqueId('tceforms-multiselect-') . '"';
435
        $html[] =                       ' size="' . $size . '"';
436
        $html[] =                       ' class="form-control tceforms-multiselect"';
437
        $html[] =                       $multiple;
438
        $html[] =                       ' data-formengine-input-name="' . htmlspecialchars($fieldName) . '"';
439
        $html[] =                       ' disabled="disabled">';
440
        $html[] =                   '/>';
441
        $html[] =                       implode(LF, $optionsHtml);
442
        $html[] =                   '</select>';
443
        $html[] =               '</div>';
444
        $html[] =           '</div>';
445
        $html[] =           '<input type="hidden" name="' . htmlspecialchars($fieldName) . '" value="' . htmlspecialchars(implode(',', $listOfSelectedValues)) . '" />';
446
        $html[] =       '</div>';
447
        $html[] =   '</div>';
448
        $html[] = '</div>';
449
450
        $resultArray['html'] = implode(LF, $html);
451
        return $resultArray;
452
    }
453
454
    /**
455
     * @return LanguageService
456
     */
457
    protected function getLanguageService()
458
    {
459
        return $GLOBALS['LANG'];
460
    }
461
462
    /**
463
     * @return BackendUserAuthentication
464
     */
465
    protected function getBackendUserAuthentication()
466
    {
467
        return $GLOBALS['BE_USER'];
468
    }
469
}
470