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