‪TYPO3CMS  11.5
GroupElement.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
18 use TYPO3\CMS\Backend\Form\Behavior\OnFieldChangeTrait;
26 
31 {
32  use OnFieldChangeTrait;
33 
39  protected ‪$defaultFieldInformation = [
40  'tcaDescription' => [
41  'renderType' => 'tcaDescription',
42  ],
43  ];
44 
50  protected ‪$defaultFieldControl = [
51  'elementBrowser' => [
52  'renderType' => 'elementBrowser',
53  ],
54  'insertClipboard' => [
55  'renderType' => 'insertClipboard',
56  'after' => [ 'elementBrowser' ],
57  ],
58  'editPopup' => [
59  'renderType' => 'editPopup',
60  'disabled' => true,
61  'after' => [ 'insertClipboard' ],
62  ],
63  'addRecord' => [
64  'renderType' => 'addRecord',
65  'disabled' => true,
66  'after' => [ 'editPopup' ],
67  ],
68  'listModule' => [
69  'renderType' => 'listModule',
70  'disabled' => true,
71  'after' => [ 'addRecord' ],
72  ],
73  ];
74 
80  protected ‪$defaultFieldWizard = [
81  'tableList' => [
82  'renderType' => 'tableList',
83  ],
84  'recordsOverview' => [
85  'renderType' => 'recordsOverview',
86  'after' => [ 'tableList' ],
87  ],
88  'localizationStateSelector' => [
89  'renderType' => 'localizationStateSelector',
90  'after' => [ 'recordsOverview' ],
91  ],
92  'otherLanguageContent' => [
93  'renderType' => 'otherLanguageContent',
94  'after' => [ 'localizationStateSelector' ],
95  ],
96  'defaultLanguageDifferences' => [
97  'renderType' => 'defaultLanguageDifferences',
98  'after' => [ 'otherLanguageContent' ],
99  ],
100  ];
101 
109  public function ‪render()
110  {
111  $languageService = $this->‪getLanguageService();
112  $backendUser = $this->‪getBackendUserAuthentication();
113  $resultArray = $this->‪initializeResultArray();
114 
115  $table = $this->data['tableName'];
116  $fieldName = $this->data['fieldName'];
117  $row = $this->data['databaseRow'];
118  $parameterArray = $this->data['parameterArray'];
119  $config = $parameterArray['fieldConf']['config'];
120  $elementName = $parameterArray['itemFormElName'];
121  $recordTypeValue = $this->data['recordTypeValue'] ?? null;
122 
123  $selectedItems = $parameterArray['itemFormElValue'];
124  $maxItems = $config['maxitems'];
125 
126  $size = (int)($config['size'] ?? 5);
127  $autoSizeMax = (int)($config['autoSizeMax'] ?? 0);
128  if ($autoSizeMax > 0) {
129  $size = ‪MathUtility::forceIntegerInRange($size, 1);
130  $size = ‪MathUtility::forceIntegerInRange(count($selectedItems) + 1, $size, $autoSizeMax);
131  }
132 
133  $internalType = (string)($config['internal_type'] ?? 'db');
134  $maxTitleLength = $backendUser->uc['titleLen'];
135 
136  $listOfSelectedValues = [];
137  $selectorOptionsHtml = [];
138  if ($internalType === 'folder') {
139  foreach ($selectedItems as $selectedItem) {
140  $folder = $selectedItem['folder'];
141  $listOfSelectedValues[] = $folder;
142  $selectorOptionsHtml[] =
143  '<option value="' . htmlspecialchars($folder) . '" title="' . htmlspecialchars($folder) . '">'
144  . htmlspecialchars($folder)
145  . '</option>';
146  }
147  } elseif ($internalType === 'db') {
148  foreach ($selectedItems as $selectedItem) {
149  $tableWithUid = $selectedItem['table'] . '_' . $selectedItem['uid'];
150  $listOfSelectedValues[] = $tableWithUid;
151  $title = $selectedItem['title'];
152  if (empty($title)) {
153  $title = '[' . $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.no_title') . ']';
154  }
155  $shortenedTitle = GeneralUtility::fixed_lgd_cs($title, $maxTitleLength);
156  $selectorOptionsHtml[] =
157  '<option value="' . htmlspecialchars($tableWithUid) . '" title="' . htmlspecialchars($title) . '">'
158  . htmlspecialchars($this->‪appendValueToLabelInDebugMode($shortenedTitle, $tableWithUid))
159  . '</option>';
160  }
161  } else {
162  throw new \RuntimeException(
163  'Invalid TCA internal_type "' . $internalType . '" on type="group", field "' . $fieldName . '", table "' . $table . '"',
164  1485007097
165  );
166  }
167 
168  $fieldInformationResult = $this->‪renderFieldInformation();
169  $fieldInformationHtml = $fieldInformationResult['html'];
170  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false);
171 
172  if (isset($config['readOnly']) && $config['readOnly']) {
173  // Return early if element is read only
174  $html = [];
175  $html[] = '<div class="formengine-field-item t3js-formengine-field-item">';
176  $html[] = $fieldInformationHtml;
177  $html[] = '<div class="form-wizards-wrap">';
178  $html[] = '<div class="form-wizards-element">';
179  $html[] = '<select';
180  $html[] = ' size="' . $size . '"';
181  $html[] = ' disabled="disabled"';
182  $html[] = ' class="form-select"';
183  $html[] = ($maxItems !== 1 && $size !== 1) ? ' multiple="multiple"' : '';
184  $html[] = '>';
185  $html[] = implode(LF, $selectorOptionsHtml);
186  $html[] = '</select>';
187  $html[] = '</div>';
188  $html[] = '</div>';
189  $html[] = '</div>';
190  $resultArray['html'] = implode(LF, $html);
191  return $resultArray;
192  }
193 
194  // Need some information if in flex form scope for the suggest element
195  $dataStructureIdentifier = '';
196  $flexFormSheetName = '';
197  $flexFormFieldName = '';
198  $flexFormContainerName = '';
199  $flexFormContainerFieldName = '';
200  if ($this->data['processedTca']['columns'][$fieldName]['config']['type'] === 'flex') {
201  $flexFormConfig = $this->data['processedTca']['columns'][$fieldName];
202  $dataStructureIdentifier = $flexFormConfig['config']['dataStructureIdentifier'];
203  if (!isset($flexFormConfig['config']['dataStructureIdentifier'])) {
204  throw new \RuntimeException(
205  'A data structure identifier must be set in [\'config\'] part of a flex form.'
206  . ' This is usually added by TcaFlexPrepare data processor',
207  1485206970
208  );
209  }
210  if (isset($this->data['flexFormSheetName'])) {
211  $flexFormSheetName = $this->data['flexFormSheetName'];
212  }
213  if (isset($this->data['flexFormFieldName'])) {
214  $flexFormFieldName = $this->data['flexFormFieldName'];
215  }
216  if (isset($this->data['flexFormContainerName'])) {
217  $flexFormContainerName = $this->data['flexFormContainerName'];
218  }
219  if (isset($this->data['flexFormContainerFieldName'])) {
220  $flexFormContainerFieldName = $this->data['flexFormContainerFieldName'];
221  }
222  }
223  // Get minimum characters for suggest from TCA and override by TsConfig
224  $suggestMinimumCharacters = 0;
225  if (isset($config['suggestOptions']['default']['minimumCharacters'])) {
226  $suggestMinimumCharacters = (int)$config['suggestOptions']['default']['minimumCharacters'];
227  }
228  if (isset($parameterArray['fieldTSConfig']['suggest.']['default.']['minimumCharacters'])) {
229  $suggestMinimumCharacters = (int)$parameterArray['fieldTSConfig']['suggest.']['default.']['minimumCharacters'];
230  }
231  $suggestMinimumCharacters = $suggestMinimumCharacters > 0 ? $suggestMinimumCharacters : 2;
232 
233  $itemCanBeSelectedMoreThanOnce = !empty($config['multiple']);
234 
235  $showMoveIcons = true;
236  if (isset($config['hideMoveIcons']) && $config['hideMoveIcons']) {
237  $showMoveIcons = false;
238  }
239  $showDeleteControl = true;
240  if (isset($config['hideDeleteIcon']) && $config['hideDeleteIcon']) {
241  $showDeleteControl = false;
242  }
243 
244  $fieldId = ‪StringUtility::getUniqueId('tceforms-multiselect-');
245 
246  $selectorAttributes = [
247  'id' => $fieldId,
248  'data-formengine-input-name' => htmlspecialchars($elementName),
249  'data-maxitems' => (string)$maxItems,
250  'size' => (string)$size,
251  ];
252  $selectorAttributes['class'] = 'form-select';
253  if ($maxItems !== 1 && $size !== 1) {
254  $selectorAttributes['multiple'] = 'multiple';
255  }
256 
257  $fieldControlResult = $this->‪renderFieldControl();
258  $fieldControlHtml = $fieldControlResult['html'];
259  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldControlResult, false);
260 
261  $fieldWizardResult = $this->‪renderFieldWizard();
262  $fieldWizardHtml = $fieldWizardResult['html'];
263  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false);
264 
265  $html = [];
266  $html[] = '<div class="formengine-field-item t3js-formengine-field-item">';
267  $html[] = $fieldInformationHtml;
268  $html[] = '<div class="form-wizards-wrap">';
269  if ($internalType === 'db' && (!isset($config['hideSuggest']) || (bool)$config['hideSuggest'] !== true)) {
270  $html[] = '<div class="form-wizards-items-top">';
271  $html[] = '<div class="autocomplete t3-form-suggest-container">';
272  $html[] = '<div class="input-group">';
273  $html[] = '<span class="input-group-addon">';
274  $html[] = $this->iconFactory->getIcon('actions-search', ‪Icon::SIZE_SMALL)->render();
275  $html[] = '</span>';
276  $html[] = '<input type="search" class="t3-form-suggest form-control"';
277  $html[] = ' placeholder="' . $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.findRecord') . '"';
278  $html[] = ' data-fieldname="' . htmlspecialchars($fieldName) . '"';
279  $html[] = ' data-tablename="' . htmlspecialchars($table) . '"';
280  $html[] = ' data-field="' . htmlspecialchars($elementName) . '"';
281  $html[] = ' data-uid="' . htmlspecialchars($this->data['databaseRow']['uid']) . '"';
282  $html[] = ' data-pid="' . htmlspecialchars($this->data['parentPageRow']['uid'] ?? 0) . '"';
283  $html[] = ' data-fieldtype="' . htmlspecialchars($config['type']) . '"';
284  $html[] = ' data-minchars="' . htmlspecialchars((string)$suggestMinimumCharacters) . '"';
285  $html[] = ' data-datastructureidentifier="' . htmlspecialchars($dataStructureIdentifier) . '"';
286  $html[] = ' data-flexformsheetname="' . htmlspecialchars($flexFormSheetName) . '"';
287  $html[] = ' data-flexformfieldname="' . htmlspecialchars($flexFormFieldName) . '"';
288  $html[] = ' data-flexformcontainername="' . htmlspecialchars($flexFormContainerName) . '"';
289  $html[] = ' data-flexformcontainerfieldname="' . htmlspecialchars($flexFormContainerFieldName) . '"';
290  if ($recordTypeValue !== null && $recordTypeValue !== '') {
291  $html[] = ' data-recordtypevalue="' . htmlspecialchars($recordTypeValue) . '"';
292  }
293  $html[] = '/>';
294  $html[] = '</div>';
295  $html[] = '</div>';
296  $html[] = '</div>';
297  }
298  $html[] = '<div class="form-wizards-element">';
299  $html[] = '<input type="hidden" class="t3js-group-hidden-field" data-formengine-input-name="' . htmlspecialchars($elementName) . '" value="' . $itemCanBeSelectedMoreThanOnce . '" />';
300  $html[] = '<select ' . GeneralUtility::implodeAttributes($selectorAttributes, true) . '>';
301  $html[] = implode(LF, $selectorOptionsHtml);
302  $html[] = '</select>';
303  $html[] = '</div>';
304  if (($maxItems > 1 && $size > 1 && $showMoveIcons) || $showDeleteControl) {
305  $html[] = '<div class="form-wizards-items-aside form-wizards-items-aside--move">';
306  $html[] = '<div class="btn-group-vertical">';
307  if ($maxItems > 1 && $size >= 5 && $showMoveIcons) {
308  $html[] = '<a href="#"';
309  $html[] = ' class="btn btn-default t3js-btn-option t3js-btn-moveoption-top"';
310  $html[] = ' data-fieldname="' . htmlspecialchars($elementName) . '"';
311  $html[] = ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.move_to_top')) . '"';
312  $html[] = '>';
313  $html[] = $this->iconFactory->getIcon('actions-move-to-top', ‪Icon::SIZE_SMALL)->render();
314  $html[] = '</a>';
315  }
316  if ($maxItems > 1 && $size > 1 && $showMoveIcons) {
317  $html[] = '<a href="#"';
318  $html[] = ' class="btn btn-default t3js-btn-option t3js-btn-moveoption-up"';
319  $html[] = ' data-fieldname="' . htmlspecialchars($elementName) . '"';
320  $html[] = ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.move_up')) . '"';
321  $html[] = '>';
322  $html[] = $this->iconFactory->getIcon('actions-move-up', ‪Icon::SIZE_SMALL)->render();
323  $html[] = '</a>';
324  $html[] = '<a href="#"';
325  $html[] = ' class="btn btn-default t3js-btn-option t3js-btn-moveoption-down"';
326  $html[] = ' data-fieldname="' . htmlspecialchars($elementName) . '"';
327  $html[] = ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.move_down')) . '"';
328  $html[] = '>';
329  $html[] = $this->iconFactory->getIcon('actions-move-down', ‪Icon::SIZE_SMALL)->render();
330  $html[] = '</a>';
331  }
332  if ($maxItems > 1 && $size >= 5 && $showMoveIcons) {
333  $html[] = '<a href="#"';
334  $html[] = ' class="btn btn-default t3js-btn-option t3js-btn-moveoption-bottom"';
335  $html[] = ' data-fieldname="' . htmlspecialchars($elementName) . '"';
336  $html[] = ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.move_to_bottom')) . '"';
337  $html[] = '>';
338  $html[] = $this->iconFactory->getIcon('actions-move-to-bottom', ‪Icon::SIZE_SMALL)->render();
339  $html[] = '</a>';
340  }
341  if ($showDeleteControl) {
342  $html[] = '<a href="#"';
343  $html[] = ' class="btn btn-default t3js-btn-option t3js-btn-removeoption t3js-revert-unique"';
344  $html[] = ' data-fieldname="' . htmlspecialchars($elementName) . '"';
345  $html[] = ' data-uid="' . htmlspecialchars($row['uid']) . '"';
346  $html[] = ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.remove_selected')) . '"';
347  $html[] = '>';
348  $html[] = $this->iconFactory->getIcon('actions-selection-delete', ‪Icon::SIZE_SMALL)->render();
349  $html[] = '</a>';
350  }
351  }
352  $html[] = '</div>';
353  $html[] = '</div>';
354  if ($fieldControlHtml !== '') {
355  $html[] = '<div class="form-wizards-items-aside form-wizards-items-aside--field-control">';
356  $html[] = '<div class="btn-group-vertical">';
357  $html[] = $fieldControlHtml;
358  $html[] = '</div>';
359  $html[] = '</div>';
360  }
361  if (!empty($fieldWizardHtml)) {
362  $html[] = '<div class="form-wizards-items-bottom">';
363  $html[] = $fieldWizardHtml;
364  $html[] = '</div>';
365  }
366  $html[] = '</div>';
367 
368  $hiddenElementAttrs = array_merge(
369  [
370  'type' => 'hidden',
371  'name' => $elementName,
372  'data-formengine-validation-rules' => $this->‪getValidationDataAsJsonString($config),
373  'value' => implode(',', $listOfSelectedValues),
374  ],
375  $this->getOnFieldChangeAttrs('change', $parameterArray['fieldChangeFunc'] ?? [])
376  );
377  $html[] = '<input ' . GeneralUtility::implodeAttributes($hiddenElementAttrs, true) . '>';
378  $html[] = '</div>';
379 
380  $resultArray['requireJsModules'][] = ‪JavaScriptModuleInstruction::forRequireJS(
381  'TYPO3/CMS/Backend/FormEngine/Element/GroupElement'
382  )->instance($fieldId);
383 
384  $resultArray['html'] = implode(LF, $html);
385  return $resultArray;
386  }
387 
391  protected function ‪getBackendUserAuthentication()
392  {
393  return ‪$GLOBALS['BE_USER'];
394  }
395 
399  protected function ‪getLanguageService()
400  {
401  return ‪$GLOBALS['LANG'];
402  }
403 }
‪TYPO3\CMS\Core\Imaging\Icon\SIZE_SMALL
‪const SIZE_SMALL
Definition: Icon.php:30
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderFieldInformation
‪array renderFieldInformation()
Definition: AbstractFormElement.php:76
‪TYPO3\CMS\Backend\Form\Element\GroupElement\$defaultFieldWizard
‪array $defaultFieldWizard
Definition: GroupElement.php:76
‪TYPO3\CMS\Backend\Form\AbstractNode\mergeChildReturnIntoExistingResult
‪array mergeChildReturnIntoExistingResult(array $existing, array $childReturn, bool $mergeHtml=true)
Definition: AbstractNode.php:120
‪TYPO3\CMS\Backend\Form\AbstractNode\initializeResultArray
‪array initializeResultArray()
Definition: AbstractNode.php:91
‪TYPO3\CMS\Core\Imaging\Icon
Definition: Icon.php:26
‪TYPO3\CMS\Core\Utility\MathUtility\forceIntegerInRange
‪static int forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:32
‪TYPO3\CMS\Backend\Form\Element\GroupElement\render
‪array render()
Definition: GroupElement.php:105
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement
Definition: AbstractFormElement.php:35
‪TYPO3\CMS\Backend\Form\Element\GroupElement\getBackendUserAuthentication
‪BackendUserAuthentication getBackendUserAuthentication()
Definition: GroupElement.php:387
‪TYPO3\CMS\Core\Page\JavaScriptModuleInstruction
Definition: JavaScriptModuleInstruction.php:23
‪TYPO3\CMS\Backend\Form\Element
Definition: AbstractFormElement.php:16
‪TYPO3\CMS\Core\Page\JavaScriptModuleInstruction\forRequireJS
‪static self forRequireJS(string $name, string $exportName=null)
Definition: JavaScriptModuleInstruction.php:49
‪TYPO3\CMS\Backend\Form\Element\GroupElement
Definition: GroupElement.php:31
‪TYPO3\CMS\Backend\Form\AbstractNode\getValidationDataAsJsonString
‪string getValidationDataAsJsonString(array $config)
Definition: AbstractNode.php:156
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderFieldControl
‪array renderFieldControl()
Definition: AbstractFormElement.php:92
‪TYPO3\CMS\Backend\Form\Element\GroupElement\getLanguageService
‪LanguageService getLanguageService()
Definition: GroupElement.php:395
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\appendValueToLabelInDebugMode
‪string appendValueToLabelInDebugMode($label, $value)
Definition: AbstractFormElement.php:436
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static string getUniqueId($prefix='')
Definition: StringUtility.php:128
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:22
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:42
‪TYPO3\CMS\Backend\Form\Element\GroupElement\$defaultFieldInformation
‪array $defaultFieldInformation
Definition: GroupElement.php:37
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:22
‪TYPO3\CMS\Backend\Form\Element\GroupElement\$defaultFieldControl
‪array $defaultFieldControl
Definition: GroupElement.php:47
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderFieldWizard
‪array renderFieldWizard()
Definition: AbstractFormElement.php:108