‪TYPO3CMS  11.5
PaletteAndSingleContainer.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\Utility\BackendUtility;
22 
33 {
39  protected ‪$resultArray = [];
40 
46  public function ‪render()
47  {
48  $languageService = $this->‪getLanguageService();
49 
50  /*
51  * The first code block creates a target structure array to later create the final
52  * HTML string. The single fields and sub containers are rendered here already and
53  * other parts of the return array from children except html are accumulated in
54  * $this->resultArray
55  *
56  $targetStructure = array(
57  0 => array(
58  'type' => 'palette',
59  'fieldName' => 'palette1',
60  'fieldLabel' => 'palette1',
61  'elements' => array(
62  0 => array(
63  'type' => 'single',
64  'fieldName' => 'paletteName',
65  'fieldLabel' => 'element1',
66  'fieldHtml' => 'element1',
67  ),
68  1 => array(
69  'type' => 'linebreak',
70  ),
71  2 => array(
72  'type' => 'single',
73  'fieldName' => 'paletteName',
74  'fieldLabel' => 'element2',
75  'fieldHtml' => 'element2',
76  ),
77  ),
78  'paletteDescription' => 'palette1Description',
79  ),
80  1 => array(
81  'type' => 'single',
82  'fieldName' => 'element3',
83  'fieldLabel' => 'element3',
84  'fieldHtml' => 'element3',
85  ),
86  2 => array(
87  'type' => 'palette2',
88  'fieldName' => 'palette2',
89  'fieldLabel' => '', // Palette label is optional
90  'elements' => array(
91  0 => array(
92  'type' => 'single',
93  'fieldName' => 'element4',
94  'fieldLabel' => 'element4',
95  'fieldHtml' => 'element4',
96  ),
97  1 => array(
98  'type' => 'linebreak',
99  ),
100  2 => array(
101  'type' => 'single',
102  'fieldName' => 'element5',
103  'fieldLabel' => 'element5',
104  'fieldHtml' => 'element5',
105  ),
106  ),
107  'paletteDescription' => '', // Palette description is optional
108  ),
109  );
110  */
111 
112  // Create an intermediate structure of rendered sub elements and elements nested in palettes
113  $targetStructure = [];
114  $mainStructureCounter = -1;
115  $fieldsArray = $this->data['fieldsArray'];
116  $this->resultArray = $this->‪initializeResultArray();
117  foreach ($fieldsArray as $fieldString) {
118  $fieldConfiguration = $this->‪explodeSingleFieldShowItemConfiguration($fieldString);
119  $fieldName = $fieldConfiguration['fieldName'];
120  if ($fieldName === '--palette--') {
121  $paletteElementArray = $this->‪createPaletteContentArray($fieldConfiguration['paletteName']);
122  if (!empty($paletteElementArray)) {
123  $mainStructureCounter++;
124 
125  // If there is no label in ['types']['aType']['showitem'] for this palette: "--palette--;;aPalette",
126  // then use ['palettes']['aPalette']['label'] if given.
127  $paletteLabel = $fieldConfiguration['fieldLabel'];
128  if ($paletteLabel === null && !empty($this->data['processedTca']['palettes'][$fieldConfiguration['paletteName']]['label'])) {
129  $paletteLabel = $this->data['processedTca']['palettes'][$fieldConfiguration['paletteName']]['label'];
130  }
131 
132  // Get description of palette.
133  $paletteDescription = $this->data['processedTca']['palettes'][$fieldConfiguration['paletteName']]['description'] ?? '';
134 
135  $targetStructure[$mainStructureCounter] = [
136  'type' => 'palette',
137  'fieldName' => $fieldConfiguration['paletteName'],
138  'fieldLabel' => $languageService->sL($paletteLabel),
139  'elements' => $paletteElementArray,
140  'paletteDescription' => $languageService->sL($paletteDescription),
141  ];
142  }
143  } else {
144  if (!is_array($this->data['processedTca']['columns'][$fieldName] ?? null)) {
145  continue;
146  }
147 
148  $options = ‪$this->data;
149  $options['fieldName'] = $fieldName;
150 
151  $options['renderType'] = 'singleFieldContainer';
152  $childResultArray = $this->nodeFactory->create($options)->render();
153 
154  if (!empty($childResultArray['html'])) {
155  $mainStructureCounter++;
156  $fieldLabel = '';
157  if (!empty($this->data['processedTca']['columns'][$fieldName]['label'])) {
158  $fieldLabel = $this->data['processedTca']['columns'][$fieldName]['label'];
159  }
160  $targetStructure[$mainStructureCounter] = [
161  'type' => 'single',
162  'fieldName' => $fieldConfiguration['fieldName'],
163  'fieldLabel' => $fieldLabel,
164  'fieldHtml' => $childResultArray['html'],
165  ];
166  }
167 
168  $this->resultArray = $this->‪mergeChildReturnIntoExistingResult($this->resultArray, $childResultArray, false);
169  }
170  }
171 
172  // Compile final content
173  $content = [];
174  foreach ($targetStructure as $element) {
175  if ($element['type'] === 'palette') {
176  $paletteName = $element['fieldName'];
177  $paletteElementsHtml = $this->‪renderInnerPaletteContent($element);
178 
179  $isHiddenPalette = !empty($this->data['processedTca']['palettes'][$paletteName]['isHiddenPalette']);
180 
181  $paletteElementsHtml = '<div class="row">' . $paletteElementsHtml . '</div>';
182 
183  $content[] = $this->‪fieldSetWrap(
184  $paletteElementsHtml,
185  $isHiddenPalette,
186  $element['fieldLabel'],
187  $element['paletteDescription']
188  );
189  } else {
190  // Return raw HTML only in case of user element with no wrapping requested
191  if ($this->‪isUserNoTableWrappingField($element)) {
192  $content[] = $element['fieldHtml'];
193  } else {
194  $content[] = $this->‪fieldSetWrap($this->‪wrapSingleFieldContentWithLabelAndOuterDiv($element));
195  }
196  }
197  }
198 
199  $finalResultArray = ‪$this->resultArray;
200  $finalResultArray['html'] = implode(LF, $content);
201  return $finalResultArray;
202  }
203 
210  protected function ‪createPaletteContentArray($paletteName)
211  {
212  // palette needs a palette name reference, otherwise it does not make sense to try rendering of it
213  if (empty($paletteName) || empty($this->data['processedTca']['palettes'][$paletteName]['showitem'])) {
214  return [];
215  }
216 
217  $resultStructure = [];
218  $foundRealElement = false; // Set to true if not only line breaks were rendered
219  $fieldsArray = ‪GeneralUtility::trimExplode(',', $this->data['processedTca']['palettes'][$paletteName]['showitem'], true);
220  foreach ($fieldsArray as $fieldString) {
221  $fieldArray = $this->‪explodeSingleFieldShowItemConfiguration($fieldString);
222  $fieldName = $fieldArray['fieldName'];
223  if ($fieldName === '--linebreak--') {
224  $resultStructure[] = [
225  'type' => 'linebreak',
226  ];
227  } else {
228  if (!is_array($this->data['processedTca']['columns'][$fieldName] ?? null)) {
229  continue;
230  }
231  $options = ‪$this->data;
232  $options['fieldName'] = $fieldName;
233 
234  $options['renderType'] = 'singleFieldContainer';
235  $singleFieldContentArray = $this->nodeFactory->create($options)->render();
236 
237  if (!empty($singleFieldContentArray['html'])) {
238  $foundRealElement = true;
239  $fieldLabel = '';
240  if (!empty($this->data['processedTca']['columns'][$fieldName]['label'])) {
241  $fieldLabel = $this->data['processedTca']['columns'][$fieldName]['label'];
242  }
243  $resultStructure[] = [
244  'type' => 'single',
245  'fieldName' => $fieldName,
246  'fieldLabel' => $fieldLabel,
247  'fieldHtml' => $singleFieldContentArray['html'],
248  ];
249  }
250  $this->resultArray = $this->‪mergeChildReturnIntoExistingResult($this->resultArray, $singleFieldContentArray, false);
251  }
252  }
253 
254  if ($foundRealElement) {
255  return $resultStructure;
256  }
257  return [];
258  }
259 
266  protected function ‪renderInnerPaletteContent(array $elementArray)
267  {
268  // Group fields
269  $groupedFields = [];
270  $row = 0;
271  $lastLineWasLinebreak = true;
272  foreach ($elementArray['elements'] as $element) {
273  if ($element['type'] === 'linebreak') {
274  if (!$lastLineWasLinebreak) {
275  $row++;
276  $groupedFields[$row][] = $element;
277  $row++;
278  $lastLineWasLinebreak = true;
279  }
280  } else {
281  $lastLineWasLinebreak = false;
282  $groupedFields[$row][] = $element;
283  }
284  }
285 
286  $result = [];
287  // Process fields
288  foreach ($groupedFields as ‪$fields) {
289  $numberOfItems = count(‪$fields);
290  $colWidth = (int)floor(12 / $numberOfItems);
291  // Column class calculation
292  $colClass = 'col-md-12';
293  $colClear = [];
294  if ($colWidth == 6) {
295  $colClass = 'col col-sm-6';
296  $colClear = [
297  2 => 'd-sm-block d-md-none',
298  ];
299  } elseif ($colWidth === 4) {
300  $colClass = 'col col-sm-4';
301  $colClear = [
302  3 => 'd-sm-block d-md-none',
303  ];
304  } elseif ($colWidth === 3) {
305  $colClass = 'col col-sm-6 col-md-3';
306  $colClear = [
307  2 => 'd-sm-block d-md-none',
308  4 => 'd-sm-block d-md-block d-xl-none',
309  ];
310  } elseif ($colWidth <= 2) {
311  $colClass = 'checkbox-column col col-sm-6 col-md-3 col-lg-2';
312  $colClear = [
313  2 => 'd-sm-block',
314  4 => 'd-sm-block d-md-none',
315  6 => 'd-sm-block d-md-block d-lg-none',
316  ];
317  }
318 
319  // Render fields
320  for ($counter = 0; $counter < $numberOfItems; $counter++) {
321  $element = ‪$fields[$counter];
322  if ($element['type'] === 'linebreak') {
323  if ($counter !== $numberOfItems) {
324  $result[] = '<div class="clearfix"></div>';
325  }
326  } else {
327  $result[] = $this->‪wrapSingleFieldContentWithLabelAndOuterDiv($element, [$colClass]);
328 
329  // Breakpoints
330  if ($counter + 1 < $numberOfItems && !empty($colClear)) {
331  foreach ($colClear as $rowBreakAfter => $clearClass) {
332  if (($counter + 1) % $rowBreakAfter === 0) {
333  $result[] = '<div class="clearfix ' . $clearClass . '"></div>';
334  }
335  }
336  }
337  }
338  }
339  }
340 
341  return implode(LF, $result);
342  }
343 
353  protected function ‪fieldSetWrap($content, $paletteHidden = false, $label = '', $description = '')
354  {
355  $fieldSetClass = 'form-section';
356  if ($paletteHidden) {
357  $fieldSetClass .= ' hide';
358  }
359 
360  $result = [];
361  $result[] = '<fieldset class="' . $fieldSetClass . '">';
362 
363  if (!empty($label)) {
364  $result[] = '<h4 class="form-section-headline">' . htmlspecialchars($label) . '</h4>';
365  }
366 
367  if (!empty($description)) {
368  $result[] = '<p class="form-section-description text-muted">' . htmlspecialchars($description) . '</p>';
369  }
370 
371  $result[] = $content;
372  $result[] = '</fieldset>';
373  return implode(LF, $result);
374  }
375 
383  protected function ‪wrapSingleFieldContentWithLabelAndOuterDiv(array $element, array $additionalPaletteClasses = [])
384  {
385  $fieldName = $element['fieldName'];
386 
387  $paletteFieldClasses = [
388  'form-group',
389  't3js-formengine-validation-marker',
390  't3js-formengine-palette-field',
391  ];
392  foreach ($additionalPaletteClasses as $class) {
393  $paletteFieldClasses[] = $class;
394  }
395 
396  $label = BackendUtility::wrapInHelp($this->data['tableName'], $fieldName, htmlspecialchars($element['fieldLabel']));
397 
398  if ($this->‪getBackendUser()->shallDisplayDebugInformation()) {
399  $label .= '<code>[' . htmlspecialchars($fieldName) . ']</code>';
400  }
401 
402  $content = [];
403  $content[] = '<div class="' . implode(' ', $paletteFieldClasses) . '">';
404  $content[] = '<label class="t3js-formengine-label">';
405  $content[] = $label;
406  $content[] = '</label>';
407  $content[] = $element['fieldHtml'];
408  $content[] = '</div>';
409 
410  return implode(LF, $content);
411  }
412 
419  protected function ‪isUserNoTableWrappingField($element)
420  {
421  $fieldName = $element['fieldName'];
422  if (
423  $this->data['processedTca']['columns'][$fieldName]['config']['type'] === 'user'
424  && !empty($this->data['processedTca']['columns'][$fieldName]['config']['noTableWrapping'])
425  ) {
426  return true;
427  }
428  return false;
429  }
430 
434  protected function ‪getLanguageService()
435  {
436  return ‪$GLOBALS['LANG'];
437  }
438 
444  protected function ‪getBackendUser()
445  {
446  return ‪$GLOBALS['BE_USER'];
447  }
448 }
‪TYPO3\CMS\Backend\Form\Container\PaletteAndSingleContainer\createPaletteContentArray
‪array createPaletteContentArray($paletteName)
Definition: PaletteAndSingleContainer.php:209
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:999
‪TYPO3\CMS\Backend\Form\Container\PaletteAndSingleContainer\getBackendUser
‪BackendUserAuthentication getBackendUser()
Definition: PaletteAndSingleContainer.php:443
‪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\Backend\Form\Container\PaletteAndSingleContainer\getLanguageService
‪LanguageService getLanguageService()
Definition: PaletteAndSingleContainer.php:433
‪TYPO3\CMS\Backend\Form\Container\PaletteAndSingleContainer\renderInnerPaletteContent
‪string renderInnerPaletteContent(array $elementArray)
Definition: PaletteAndSingleContainer.php:265
‪TYPO3\CMS\Backend\Form\Container
Definition: AbstractContainer.php:16
‪$fields
‪$fields
Definition: pages.php:5
‪TYPO3\CMS\Backend\Form\Container\AbstractContainer\explodeSingleFieldShowItemConfiguration
‪array explodeSingleFieldShowItemConfiguration($field)
Definition: AbstractContainer.php:90
‪TYPO3\CMS\Backend\Form\AbstractNode\$data
‪array $data
Definition: AbstractNode.php:43
‪TYPO3\CMS\Backend\Form\Container\AbstractContainer
Definition: AbstractContainer.php:28
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪TYPO3\CMS\Backend\Form\Container\PaletteAndSingleContainer\$resultArray
‪array $resultArray
Definition: PaletteAndSingleContainer.php:38
‪TYPO3\CMS\Backend\Form\Container\PaletteAndSingleContainer\fieldSetWrap
‪string fieldSetWrap($content, $paletteHidden=false, $label='', $description='')
Definition: PaletteAndSingleContainer.php:352
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:42
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Backend\Form\Container\PaletteAndSingleContainer\isUserNoTableWrappingField
‪bool isUserNoTableWrappingField($element)
Definition: PaletteAndSingleContainer.php:418
‪TYPO3\CMS\Backend\Form\Container\PaletteAndSingleContainer
Definition: PaletteAndSingleContainer.php:33
‪TYPO3\CMS\Backend\Form\Container\PaletteAndSingleContainer\render
‪array render()
Definition: PaletteAndSingleContainer.php:45
‪TYPO3\CMS\Backend\Form\Container\PaletteAndSingleContainer\wrapSingleFieldContentWithLabelAndOuterDiv
‪string wrapSingleFieldContentWithLabelAndOuterDiv(array $element, array $additionalPaletteClasses=[])
Definition: PaletteAndSingleContainer.php:382