‪TYPO3CMS  9.5
SingleFieldContainer.php
Go to the documentation of this file.
1 <?php
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 
24 
34 {
41  public function ‪render()
42  {
43  $backendUser = $this->‪getBackendUserAuthentication();
44  $resultArray = $this->‪initializeResultArray();
45 
46  $table = $this->data['tableName'];
47  $row = $this->data['databaseRow'];
48  $fieldName = $this->data['fieldName'];
49 
50  $parameterArray = [];
51  $parameterArray['fieldConf'] = $this->data['processedTca']['columns'][$fieldName];
52 
53  $isOverlay = false;
54 
55  // This field decides whether the current record is an overlay (as opposed to being a standalone record)
56  // Based on this decision we need to trigger field exclusion or special rendering (like readOnly)
57  if (isset($this->data['processedTca']['ctrl']['transOrigPointerField'])
58  && is_array($this->data['processedTca']['columns'][$this->data['processedTca']['ctrl']['transOrigPointerField']])
59  ) {
60  $parentValue = $row[$this->data['processedTca']['ctrl']['transOrigPointerField']];
62  $isOverlay = (bool)$parentValue;
63  } elseif (is_array($parentValue)) {
64  // This case may apply if the value has been converted to an array by the select or group data provider
65  $isOverlay = !empty($parentValue) ? (bool)$parentValue[0] : false;
66  } else {
67  throw new \InvalidArgumentException(
68  'The given value "' . $parentValue . '" for the original language field ' . $this->data['processedTca']['ctrl']['transOrigPointerField']
69  . ' of table ' . $table . ' is invalid.',
70  1470742770
71  );
72  }
73  }
74 
75  // A couple of early returns in case the field should not be rendered
76  // Check if this field is configured and editable according to exclude fields and other configuration
77  if (// Return if BE-user has no access rights to this field, @todo: another user access rights check!
78  $parameterArray['fieldConf']['exclude'] && !$backendUser->check('non_exclude_fields', $table . ':' . $fieldName)
79  // Return if field should not be rendered in translated records
80  || $isOverlay && empty($parameterArray['fieldConf']['l10n_display']) && $parameterArray['fieldConf']['l10n_mode'] === 'exclude'
82  ) {
83  return $resultArray;
84  }
85 
86  $parameterArray['fieldTSConfig'] = [];
87  if (isset($this->data['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.'])
88  && is_array($this->data['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.'])
89  ) {
90  $parameterArray['fieldTSConfig'] = $this->data['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.'];
91  }
92  if ($parameterArray['fieldTSConfig']['disabled']) {
93  return $resultArray;
94  }
95 
96  // Override fieldConf by fieldTSconfig:
97  $parameterArray['fieldConf']['config'] = ‪FormEngineUtility::overrideFieldConf($parameterArray['fieldConf']['config'], $parameterArray['fieldTSConfig']);
98  $parameterArray['itemFormElName'] = 'data[' . $table . '][' . $row['uid'] . '][' . $fieldName . ']';
99  $parameterArray['itemFormElID'] = 'data_' . $table . '_' . $row['uid'] . '_' . $fieldName;
100  $newElementBaseName = $this->data['elementBaseName'] . '[' . $table . '][' . $row['uid'] . '][' . $fieldName . ']';
101 
102  // The value to show in the form field.
103  $parameterArray['itemFormElValue'] = $row[$fieldName];
104  // Set field to read-only if configured for translated records to show default language content as readonly
105  if ($parameterArray['fieldConf']['l10n_display']
106  && GeneralUtility::inList($parameterArray['fieldConf']['l10n_display'], 'defaultAsReadonly')
107  && $isOverlay
108  ) {
109  $parameterArray['fieldConf']['config']['readOnly'] = true;
110  $parameterArray['itemFormElValue'] = $this->data['defaultLanguageRow'][$fieldName];
111  }
112 
113  if (strpos($this->data['processedTca']['ctrl']['type'], ':') === false) {
114  $typeField = $this->data['processedTca']['ctrl']['type'];
115  } else {
116  $typeField = substr($this->data['processedTca']['ctrl']['type'], 0, strpos($this->data['processedTca']['ctrl']['type'], ':'));
117  }
118  // Create a JavaScript code line which will ask the user to save/update the form due to changing the element.
119  // This is used for eg. "type" fields and others configured with "onChange"
120  if (!empty($this->data['processedTca']['ctrl']['type']) && $fieldName === $typeField
121  || isset($parameterArray['fieldConf']['onChange']) && $parameterArray['fieldConf']['onChange'] === 'reload'
122  ) {
123  if ($backendUser->jsConfirmation(‪JsConfirmation::TYPE_CHANGE)) {
124  $alertMsgOnChange = 'top.TYPO3.Modal.confirm('
125  . 'TYPO3.lang["FormEngine.refreshRequiredTitle"],'
126  . ' TYPO3.lang["FormEngine.refreshRequiredContent"]'
127  . ')'
128  . '.on('
129  . '"button.clicked",'
130  . ' function(e) { if (e.target.name == "ok" && TBE_EDITOR.checkSubmit(-1)) { TBE_EDITOR.submitForm() } top.TYPO3.Modal.dismiss(); }'
131  . ');';
132  } else {
133  $alertMsgOnChange = 'if (TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
134  }
135  } else {
136  $alertMsgOnChange = '';
137  }
138 
139  // JavaScript code for event handlers:
140  $parameterArray['fieldChangeFunc'] = [];
141  $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] = 'TBE_EDITOR.fieldChanged('
142  . GeneralUtility::quoteJSvalue($table) . ','
143  . GeneralUtility::quoteJSvalue($row['uid']) . ','
144  . GeneralUtility::quoteJSvalue($fieldName) . ','
145  . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName'])
146  . ');';
147  if ($alertMsgOnChange) {
148  $parameterArray['fieldChangeFunc']['alert'] = $alertMsgOnChange;
149  }
150 
151  // If this is the child of an inline type and it is the field creating the label
152  if ($this->‪isInlineChildAndLabelField($table, $fieldName)) {
153  $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
154  $inlineStackProcessor->initializeByGivenStructure($this->data['inlineStructure']);
155  $inlineDomObjectId = $inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']);
156  $inlineObjectId = implode(
157  '-',
158  [
159  $inlineDomObjectId,
160  $table,
161  $row['uid']
162  ]
163  );
164  $parameterArray['fieldChangeFunc']['inline'] = 'inline.handleChangedField('
165  . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName']) . ','
166  . GeneralUtility::quoteJSvalue($inlineObjectId)
167  . ');';
168  }
169 
170  // Based on the type of the item, call a render function on a child element
171  $options = ‪$this->data;
172  $options['parameterArray'] = $parameterArray;
173  $options['elementBaseName'] = $newElementBaseName;
174  if (!empty($parameterArray['fieldConf']['config']['renderType'])) {
175  $options['renderType'] = $parameterArray['fieldConf']['config']['renderType'];
176  } else {
177  // Fallback to type if no renderType is given
178  $options['renderType'] = $parameterArray['fieldConf']['config']['type'];
179  }
180  $resultArray = $this->nodeFactory->create($options)->render();
181  return $resultArray;
182  }
183 
193  protected function ‪isInlineChildAndLabelField($table, $field)
194  {
196  $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
197  $inlineStackProcessor->initializeByGivenStructure($this->data['inlineStructure']);
198  $level = $inlineStackProcessor->getStructureLevel(-1);
199  if ($level['config']['foreign_label']) {
200  $label = $level['config']['foreign_label'];
201  } else {
202  $label = $this->data['processedTca']['ctrl']['label'];
203  }
204  return $level['config']['foreign_table'] === $table && $label === $field;
205  }
206 
212  protected function ‪inlineFieldShouldBeSkipped()
213  {
214  $table = $this->data['tableName'];
215  $fieldName = $this->data['fieldName'];
216  $fieldConfig = $this->data['processedTca']['columns'][$fieldName]['config'];
217 
219  $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
220  $inlineStackProcessor->initializeByGivenStructure($this->data['inlineStructure']);
221  $structureDepth = $inlineStackProcessor->getStructureDepth();
222 
223  $skipThisField = false;
224  if ($structureDepth > 0) {
225  $searchArray = [
226  '%OR' => [
227  'config' => [
228  0 => [
229  '%AND' => [
230  'foreign_table' => $table,
231  '%OR' => [
232  '%AND' => [
233  'appearance' => ['useCombination' => true],
234  'foreign_selector' => $fieldName
235  ],
236  'MM' => $fieldConfig['MM']
237  ]
238  ]
239  ],
240  1 => [
241  '%AND' => [
242  'foreign_table' => $fieldConfig['foreign_table'],
243  'foreign_selector' => $fieldConfig['foreign_field']
244  ]
245  ]
246  ]
247  ]
248  ];
249  // Get the parent record from structure stack
250  $level = $inlineStackProcessor->getStructureLevel(-1);
251  // If we have symmetric fields, check on which side we are and hide fields, that are set automatically:
252  if ($this->data['isOnSymmetricSide']) {
253  $searchArray['%OR']['config'][0]['%AND']['%OR']['symmetric_field'] = $fieldName;
254  $searchArray['%OR']['config'][0]['%AND']['%OR']['symmetric_sortby'] = $fieldName;
255  } else {
256  $searchArray['%OR']['config'][0]['%AND']['%OR']['foreign_field'] = $fieldName;
257  $searchArray['%OR']['config'][0]['%AND']['%OR']['foreign_sortby'] = $fieldName;
258  }
259  $skipThisField = $this->‪arrayCompareComplex($level, $searchArray);
260  }
261  return $skipThisField;
262  }
263 
296  protected function ‪arrayCompareComplex($subjectArray, $searchArray, $type = '')
297  {
298  $localMatches = 0;
299  $localEntries = 0;
300  if (is_array($searchArray) && !empty($searchArray)) {
301  // If no type was passed, try to determine
302  if (!$type) {
303  reset($searchArray);
304  $type = key($searchArray);
305  $searchArray = current($searchArray);
306  }
307  // We use '%AND' and '%OR' in uppercase
308  $type = strtoupper($type);
309  // Split regular elements from sub elements
310  foreach ($searchArray as $key => $value) {
311  $localEntries++;
312  // Process a sub-group of OR-conditions
313  if ($key === '%OR') {
314  $localMatches += $this->‪arrayCompareComplex($subjectArray, $value, '%OR') ? 1 : 0;
315  } elseif ($key === '%AND') {
316  $localMatches += $this->‪arrayCompareComplex($subjectArray, $value, '%AND') ? 1 : 0;
317  } elseif (is_array($value) && $this->‪isAssociativeArray($searchArray)) {
318  $localMatches += $this->‪arrayCompareComplex($subjectArray[$key], $value, $type) ? 1 : 0;
319  } elseif (is_array($value)) {
320  $localMatches += $this->‪arrayCompareComplex($subjectArray, $value, $type) ? 1 : 0;
321  } else {
322  if (isset($subjectArray[$key]) && isset($value)) {
323  // Boolean match:
324  if (is_bool($value)) {
325  $localMatches += !($subjectArray[$key] xor $value) ? 1 : 0;
326  } elseif (is_numeric($subjectArray[$key]) && is_numeric($value)) {
327  $localMatches += $subjectArray[$key] == $value ? 1 : 0;
328  } else {
329  $localMatches += $subjectArray[$key] === $value ? 1 : 0;
330  }
331  }
332  }
333  // If one or more matches are required ('OR'), return TRUE after the first successful match
334  if ($type === '%OR' && $localMatches > 0) {
335  return true;
336  }
337  // If all matches are required ('AND') and we have no result after the first run, return FALSE
338  if ($type === '%AND' && $localMatches == 0) {
339  return false;
340  }
341  }
342  }
343  // Return the result for '%AND' (if nothing was checked, TRUE is returned)
344  return $localEntries === $localMatches;
345  }
346 
353  protected function ‪isAssociativeArray($object)
354  {
355  return is_array($object) && !empty($object) && array_keys($object) !== range(0, count($object) - 1);
356  }
357 
361  protected function ‪getBackendUserAuthentication()
362  {
363  return ‪$GLOBALS['BE_USER'];
364  }
365 
369  protected function ‪getLanguageService()
370  {
371  return ‪$GLOBALS['LANG'];
372  }
373 }
‪TYPO3\CMS\Backend\Form\Container\SingleFieldContainer\getBackendUserAuthentication
‪BackendUserAuthentication getBackendUserAuthentication()
Definition: SingleFieldContainer.php:361
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger($var)
Definition: MathUtility.php:73
‪TYPO3\CMS\Backend\Form\AbstractNode\initializeResultArray
‪array initializeResultArray()
Definition: AbstractNode.php:88
‪TYPO3\CMS\Backend\Form\Container\SingleFieldContainer\render
‪array render()
Definition: SingleFieldContainer.php:41
‪TYPO3\CMS\Backend\Form\Container
Definition: AbstractContainer.php:2
‪TYPO3\CMS\Backend\Form\Utility\FormEngineUtility
Definition: FormEngineUtility.php:38
‪TYPO3\CMS\Backend\Form\Container\SingleFieldContainer
Definition: SingleFieldContainer.php:34
‪TYPO3\CMS\Backend\Form\Container\SingleFieldContainer\isAssociativeArray
‪bool isAssociativeArray($object)
Definition: SingleFieldContainer.php:353
‪TYPO3\CMS\Backend\Form\AbstractNode\$data
‪array $data
Definition: AbstractNode.php:40
‪TYPO3\CMS\Backend\Form\Container\AbstractContainer
Definition: AbstractContainer.php:27
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:45
‪TYPO3\CMS\Backend\Form\Container\SingleFieldContainer\isInlineChildAndLabelField
‪bool isInlineChildAndLabelField($table, $field)
Definition: SingleFieldContainer.php:193
‪TYPO3\CMS\Backend\Form\Container\SingleFieldContainer\inlineFieldShouldBeSkipped
‪bool inlineFieldShouldBeSkipped()
Definition: SingleFieldContainer.php:212
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Backend\Form\Container\SingleFieldContainer\getLanguageService
‪LanguageService getLanguageService()
Definition: SingleFieldContainer.php:369
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:21
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:29
‪TYPO3\CMS\Core\Type\Bitmask\JsConfirmation\TYPE_CHANGE
‪const TYPE_CHANGE
Definition: JsConfirmation.php:28
‪TYPO3\CMS\Backend\Form\InlineStackProcessor
Definition: InlineStackProcessor.php:29
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Core\Type\Bitmask\JsConfirmation
Definition: JsConfirmation.php:24
‪TYPO3\CMS\Backend\Form\Container\SingleFieldContainer\arrayCompareComplex
‪bool arrayCompareComplex($subjectArray, $searchArray, $type='')
Definition: SingleFieldContainer.php:296
‪TYPO3\CMS\Backend\Form\Utility\FormEngineUtility\overrideFieldConf
‪static array overrideFieldConf($fieldConfig, $TSconfig)
Definition: FormEngineUtility.php:65