TYPO3 CMS  TYPO3_8-7
AbstractFormElement.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 
26 
30 abstract class AbstractFormElement extends AbstractNode
31 {
37  protected $defaultInputWidth = 30;
38 
44  protected $minimumInputWidth = 10;
45 
51  protected $maxInputWidth = 50;
52 
56  protected $iconFactory;
57 
64  public function __construct(NodeFactory $nodeFactory, array $data)
65  {
66  parent::__construct($nodeFactory, $data);
67  $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
68  }
69 
75  protected function renderFieldInformation(): array
76  {
77  $options = $this->data;
78  $fieldInformation = $this->defaultFieldInformation;
79  $fieldInformationFromTca = $options['parameterArray']['fieldConf']['config']['fieldInformation'] ?? [];
80  ArrayUtility::mergeRecursiveWithOverrule($fieldInformation, $fieldInformationFromTca);
81  $options['renderType'] = 'fieldInformation';
82  $options['renderData']['fieldInformation'] = $fieldInformation;
83  return $this->nodeFactory->create($options)->render();
84  }
85 
91  protected function renderFieldControl(): array
92  {
93  $options = $this->data;
94  $fieldControl = $this->defaultFieldControl;
95  $fieldControlFromTca = $options['parameterArray']['fieldConf']['config']['fieldControl'] ?? [];
96  ArrayUtility::mergeRecursiveWithOverrule($fieldControl, $fieldControlFromTca);
97  $options['renderType'] = 'fieldControl';
98  $options['renderData']['fieldControl'] = $fieldControl;
99  return $this->nodeFactory->create($options)->render();
100  }
101 
107  protected function renderFieldWizard(): array
108  {
109  $options = $this->data;
110  $fieldWizard = $this->defaultFieldWizard;
111  $fieldWizardFromTca = $options['parameterArray']['fieldConf']['config']['fieldWizard'] ?? [];
112  ArrayUtility::mergeRecursiveWithOverrule($fieldWizard, $fieldWizardFromTca);
113  $options['renderType'] = 'fieldWizard';
114  $options['renderData']['fieldWizard'] = $fieldWizard;
115  return $this->nodeFactory->create($options)->render();
116  }
117 
135  protected function hasNullCheckboxButNoPlaceholder(): bool
136  {
137  $hasNullCheckboxNoPlaceholder = false;
138  $parameterArray = $this->data['parameterArray'];
139  $mode = $parameterArray['fieldConf']['config']['mode'] ?? '';
140  if (empty($this->data['flexFormDataStructureIdentifier'])
141  && !empty($parameterArray['fieldConf']['config']['eval'])
142  && GeneralUtility::inList($parameterArray['fieldConf']['config']['eval'], 'null')
143  && ($mode !== 'useOrOverridePlaceholder')
144  ) {
145  $hasNullCheckboxNoPlaceholder = true;
146  }
147  return $hasNullCheckboxNoPlaceholder;
148  }
149 
170  protected function hasNullCheckboxWithPlaceholder(): bool
171  {
172  $hasNullCheckboxWithPlaceholder = false;
173  $parameterArray = $this->data['parameterArray'];
174  $mode = $parameterArray['fieldConf']['config']['mode'] ?? '';
175  if (empty($this->data['flexFormDataStructureIdentifier'])
176  && !empty($parameterArray['fieldConf']['config']['eval'])
177  && GeneralUtility::inList($parameterArray['fieldConf']['config']['eval'], 'null')
178  && ($mode === 'useOrOverridePlaceholder')
179  ) {
180  $hasNullCheckboxWithPlaceholder = true;
181  }
182  return $hasNullCheckboxWithPlaceholder;
183  }
184 
193  protected function formatValue($format, $itemValue, $formatOptions = [])
194  {
195  switch ($format) {
196  case 'date':
197  if ($itemValue) {
198  $option = isset($formatOptions['option']) ? trim($formatOptions['option']) : '';
199  if ($option) {
200  if (isset($formatOptions['strftime']) && $formatOptions['strftime']) {
201  $value = strftime($option, $itemValue);
202  } else {
203  $value = date($option, $itemValue);
204  }
205  } else {
206  $value = date('d-m-Y', $itemValue);
207  }
208  } else {
209  $value = '';
210  }
211  if (isset($formatOptions['appendAge']) && $formatOptions['appendAge']) {
213  $GLOBALS['EXEC_TIME'] - $itemValue,
214  $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.minutesHoursDaysYears')
215  );
216  $value .= ' (' . $age . ')';
217  }
218  $itemValue = $value;
219  break;
220  case 'datetime':
221  // compatibility with "eval" (type "input")
222  if ($itemValue !== '' && !is_null($itemValue)) {
223  $itemValue = BackendUtility::datetime((int)$itemValue);
224  }
225  break;
226  case 'time':
227  // compatibility with "eval" (type "input")
228  if ($itemValue !== '' && !is_null($itemValue)) {
229  $itemValue = BackendUtility::time((int)$itemValue, false);
230  }
231  break;
232  case 'timesec':
233  // compatibility with "eval" (type "input")
234  if ($itemValue !== '' && !is_null($itemValue)) {
235  $itemValue = BackendUtility::time((int)$itemValue);
236  }
237  break;
238  case 'year':
239  // compatibility with "eval" (type "input")
240  if ($itemValue !== '' && !is_null($itemValue)) {
241  $itemValue = date('Y', (int)$itemValue);
242  }
243  break;
244  case 'int':
245  $baseArr = ['dec' => 'd', 'hex' => 'x', 'HEX' => 'X', 'oct' => 'o', 'bin' => 'b'];
246  $base = isset($formatOptions['base']) ? trim($formatOptions['base']) : '';
247  $format = isset($baseArr[$base]) ? $baseArr[$base] : 'd';
248  $itemValue = sprintf('%' . $format, $itemValue);
249  break;
250  case 'float':
251  // default precision
252  $precision = 2;
253  if (isset($formatOptions['precision'])) {
254  $precision = MathUtility::forceIntegerInRange($formatOptions['precision'], 1, 10, $precision);
255  }
256  $itemValue = sprintf('%.' . $precision . 'f', $itemValue);
257  break;
258  case 'number':
259  $format = isset($formatOptions['option']) ? trim($formatOptions['option']) : '';
260  $itemValue = sprintf('%' . $format, $itemValue);
261  break;
262  case 'md5':
263  $itemValue = md5($itemValue);
264  break;
265  case 'filesize':
266  // We need to cast to int here, otherwise empty values result in empty output,
267  // but we expect zero.
268  $value = GeneralUtility::formatSize((int)$itemValue);
269  if (!empty($formatOptions['appendByteSize'])) {
270  $value .= ' (' . $itemValue . ')';
271  }
272  $itemValue = $value;
273  break;
274  case 'user':
275  $func = trim($formatOptions['userFunc']);
276  if ($func) {
277  $params = [
278  'value' => $itemValue,
279  'args' => $formatOptions['userFunc'],
280  'config' => [
281  'type' => 'none',
282  'format' => $format,
283  'format.' => $formatOptions,
284  ],
285  ];
286  $itemValue = GeneralUtility::callUserFunction($func, $params, $this);
287  }
288  break;
289  default:
290  // Do nothing e.g. when $format === ''
291  }
292  return $itemValue;
293  }
294 
301  protected function formMaxWidth($size = 48)
302  {
303  $compensationForLargeDocuments = 1.33;
304  $compensationForFormFields = 12;
305 
306  $size = round($size * $compensationForLargeDocuments);
307  return ceil($size * $compensationForFormFields);
308  }
309 
314  protected function isWizardsDisabled()
315  {
316  return !empty($this->data['disabledWizards']);
317  }
318 
342  protected function renderWizards(
343  $itemKinds = null,
344  $wizConf = null,
345  $table = null,
346  $row = null,
347  $fieldName = null,
348  $PA = null,
349  $itemName = null,
350  $specConf = null,
351  $RTE = null
352  ) {
353  if ($itemKinds !== null) {
354  // Deprecation log if the old $itemsKinds array comes in containing the item HTML - all core elements
355  // deliver null here. If not null, the legacy mode of the method is enabled that wraps the calculated
356  // wizards around given item HTML.
358  }
359  $item = '';
360  if (is_array($itemKinds)) {
361  $item = $itemKinds[0];
362  }
363 
364  if ($wizConf === null) {
365  $wizConf = $this->data['parameterArray']['fieldConf']['config']['wizards'] ?? null;
366  }
367  if ($table === null) {
368  $table = $this->data['tableName'];
369  }
370  if ($row === null) {
371  $row = $this->data['databaseRow'];
372  }
373  if ($fieldName === null) {
374  $fieldName = $this->data['fieldName'];
375  }
376  if ($PA === null) {
377  $PA = $this->data['parameterArray'];
378  }
379  if ($itemName === null) {
380  $itemName = $PA['itemFormElName'];
381  }
382  if ($RTE === null) {
383  $RTE = false;
384  if ((bool)$this->data['parameterArray']['fieldConf']['config']['enableRichtext'] === true) {
385  $RTE = true;
386  }
387  }
388 
389  // Return not changed main item directly if wizards are disabled
390  if (!is_array($wizConf) || $this->isWizardsDisabled()) {
391  if ($itemKinds === null) {
392  return [
393  'fieldControl' => [],
394  'fieldWizard' => [],
395  ];
396  }
397  return $item;
398  }
399 
400  $languageService = $this->getLanguageService();
401 
402  $fieldChangeFunc = $PA['fieldChangeFunc'];
403  $md5ID = 'ID' . GeneralUtility::shortMD5($itemName);
404  $prefixOfFormElName = 'data[' . $table . '][' . $row['uid'] . '][' . $fieldName . ']';
405  $flexFormPath = '';
406  if (GeneralUtility::isFirstPartOfStr($PA['itemFormElName'], $prefixOfFormElName)) {
407  $flexFormPath = str_replace('][', '/', substr($PA['itemFormElName'], strlen($prefixOfFormElName) + 1, -1));
408  }
409 
410  // Add a suffix-value if the item is a selector box with renderType "selectSingleBox":
411  if ($PA['fieldConf']['config']['type'] === 'select' && (int)$PA['fieldConf']['config']['maxitems'] > 1 && $PA['fieldConf']['config']['renderType'] === 'selectSingleBox') {
412  $itemName .= '[]';
413  }
414 
415  $buttonWizards = [];
416  $otherWizards = [];
417  foreach ($wizConf as $wizardIdentifier => $wizardConfiguration) {
418  if (!isset($wizardConfiguration['module']['name']) && isset($wizardConfiguration['script'])) {
419  throw new \InvalidArgumentException('The way registering a wizard in TCA has changed in 6.2 and was removed in CMS 7. '
420  . 'Please set module[name]=module_name instead of using script=path/to/script.php in your TCA. ', 1437750231);
421  }
422 
423  // If an identifier starts with "_", this is a configuration option like _POSITION and not a wizard
424  if ($wizardIdentifier[0] === '_') {
425  continue;
426  }
427 
428  // Sanitize wizard type
429  $wizardConfiguration['type'] = (string)$wizardConfiguration['type'];
430 
431  // Wizards can be shown based on selected "type" of record. If this is the case, the wizard configuration
432  // is set to enableByTypeConfig = 1, and the wizardIdentifier is found in $wizardsEnabledByType
433  $wizardIsEnabled = true;
434  // Disable if wizard is for RTE fields only and the handled field is no RTE field or RTE can not be loaded
435  if (isset($wizardConfiguration['RTEonly']) && (bool)$wizardConfiguration['RTEonly'] && !$RTE) {
436  $wizardIsEnabled = false;
437  }
438  // Disable if wizard is for not-new records only and we're handling a new record
439  if (isset($wizardConfiguration['notNewRecords']) && $wizardConfiguration['notNewRecords'] && !MathUtility::canBeInterpretedAsInteger($row['uid'])) {
440  $wizardIsEnabled = false;
441  }
442  // Wizard types script, colorbox and popup must contain a module name configuration
443  if (!isset($wizardConfiguration['module']['name']) && in_array($wizardConfiguration['type'], ['script', 'colorbox', 'popup'], true)) {
444  $wizardIsEnabled = false;
445  }
446 
447  if (!$wizardIsEnabled) {
448  continue;
449  }
450 
451  // Title / icon:
452  $iTitle = htmlspecialchars($languageService->sL($wizardConfiguration['title']));
453  if (isset($wizardConfiguration['icon'])) {
454  $icon = FormEngineUtility::getIconHtml($wizardConfiguration['icon'], $iTitle, $iTitle);
455  } else {
456  $icon = $iTitle;
457  }
458 
459  switch ($wizardConfiguration['type']) {
460  case 'userFunc':
462  $params = [];
463  $params['params'] = $wizardConfiguration['params'];
464  $params['exampleImg'] = $wizardConfiguration['exampleImg'];
465  $params['table'] = $table;
466  $params['uid'] = $row['uid'];
467  $params['pid'] = $row['pid'];
468  $params['field'] = $fieldName;
469  $params['flexFormPath'] = $flexFormPath;
470  $params['md5ID'] = $md5ID;
471  $params['returnUrl'] = $this->data['returnUrl'];
472 
473  $params['formName'] = 'editform';
474  $params['itemName'] = $itemName;
475  $params['hmac'] = GeneralUtility::hmac($params['formName'] . $params['itemName'], 'wizard_js');
476  $params['fieldChangeFunc'] = $fieldChangeFunc;
477  $params['fieldChangeFuncHash'] = GeneralUtility::hmac(serialize($fieldChangeFunc));
478 
479  $params['item'] = $item;
480  $params['icon'] = $icon;
481  $params['iTitle'] = $iTitle;
482  $params['wConf'] = $wizardConfiguration;
483  $params['row'] = $row;
484  $otherWizards[] = GeneralUtility::callUserFunction($wizardConfiguration['userFunc'], $params, $this);
485  break;
486 
487  case 'script':
489  $params = [];
490  $params['params'] = $wizardConfiguration['params'];
491  $params['exampleImg'] = $wizardConfiguration['exampleImg'];
492  $params['table'] = $table;
493  $params['uid'] = $row['uid'];
494  $params['pid'] = $row['pid'];
495  $params['field'] = $fieldName;
496  $params['flexFormPath'] = $flexFormPath;
497  $params['md5ID'] = $md5ID;
498  $params['returnUrl'] = $this->data['returnUrl'];
499 
500  // Resolving script filename and setting URL.
501  $urlParameters = [];
502  if (isset($wizardConfiguration['module']['urlParameters']) && is_array($wizardConfiguration['module']['urlParameters'])) {
503  $urlParameters = $wizardConfiguration['module']['urlParameters'];
504  }
505  $wScript = BackendUtility::getModuleUrl($wizardConfiguration['module']['name'], $urlParameters);
506  $url = $wScript . (strstr($wScript, '?') ? '' : '?') . GeneralUtility::implodeArrayForUrl('', ['P' => $params]);
507  $buttonWizards[] =
508  '<a class="btn btn-default" href="' . htmlspecialchars($url) . '" onclick="this.blur(); return !TBE_EDITOR.isFormChanged();">'
509  . $icon .
510  '</a>';
511  break;
512 
513  case 'popup':
515  $params = [];
516  $params['params'] = $wizardConfiguration['params'];
517  $params['exampleImg'] = $wizardConfiguration['exampleImg'];
518  $params['table'] = $table;
519  $params['uid'] = $row['uid'];
520  $params['pid'] = $row['pid'];
521  $params['field'] = $fieldName;
522  $params['flexFormPath'] = $flexFormPath;
523  $params['md5ID'] = $md5ID;
524  $params['returnUrl'] = $this->data['returnUrl'];
525 
526  $params['formName'] = 'editform';
527  $params['itemName'] = $itemName;
528  $params['hmac'] = GeneralUtility::hmac($params['formName'] . $params['itemName'], 'wizard_js');
529  $params['fieldChangeFunc'] = $fieldChangeFunc;
530  $params['fieldChangeFuncHash'] = GeneralUtility::hmac(serialize($fieldChangeFunc));
531 
532  // Resolving script filename and setting URL.
533  $urlParameters = [];
534  if (isset($wizardConfiguration['module']['urlParameters']) && is_array($wizardConfiguration['module']['urlParameters'])) {
535  $urlParameters = $wizardConfiguration['module']['urlParameters'];
536  }
537  $wScript = BackendUtility::getModuleUrl($wizardConfiguration['module']['name'], $urlParameters);
538  $url = $wScript . (strstr($wScript, '?') ? '' : '?') . GeneralUtility::implodeArrayForUrl('', ['P' => $params]);
539 
540  $onlyIfSelectedJS = '';
541  if (isset($wizardConfiguration['popup_onlyOpenIfSelected']) && $wizardConfiguration['popup_onlyOpenIfSelected']) {
542  $notSelectedText = $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.noSelItemForEdit');
543  $onlyIfSelectedJS =
544  'if (!TBE_EDITOR.curSelected(' . GeneralUtility::quoteJSvalue($itemName) . ')){' .
545  'alert(' . GeneralUtility::quoteJSvalue($notSelectedText) . ');' .
546  'return false;' .
547  '}';
548  }
549  $aOnClick =
550  'this.blur();' .
551  $onlyIfSelectedJS .
552  'vHWin=window.open(' . GeneralUtility::quoteJSvalue($url) . '+\'&P[currentValue]=\'+TBE_EDITOR.rawurlencode(' .
553  'document.editform[' . GeneralUtility::quoteJSvalue($itemName) . '].value' .
554  ')' .
555  '+\'&P[currentSelectedValues]=\'+TBE_EDITOR.curSelected(' . GeneralUtility::quoteJSvalue($itemName) . '),' .
556  GeneralUtility::quoteJSvalue('popUp' . $md5ID) . ',' .
557  GeneralUtility::quoteJSvalue($wizardConfiguration['JSopenParams']) .
558  ');' .
559  'vHWin.focus();' .
560  'return false;';
561 
562  $buttonWizards[] =
563  '<a class="btn btn-default" href="#" onclick="' . htmlspecialchars($aOnClick) . '">' .
564  $icon .
565  '</a>';
566  break;
567  }
568  }
569 
570  if ($itemKinds === null) {
571  // Return an array with the two wizard types directly if the legacy mode
572  // is not enabled.
573  return [
574  'fieldControl' => $buttonWizards,
575  'fieldWizard' => $otherWizards,
576  ];
577  }
578 
579  // For each rendered wizard, put them together around the item.
580  if (!empty($buttonWizards) || !empty($otherWizards)) {
581  $innerContent = '';
582  if (!empty($buttonWizards)) {
583  $innerContent .= '<div class="btn-group' . ($wizConf['_VERTICAL'] ? ' btn-group-vertical' : '') . '">' . implode('', $buttonWizards) . '</div>';
584  }
585  $innerContent .= implode(' ', $otherWizards);
586 
587  // Position
588  if ($wizConf['_POSITION'] === 'left') {
589  $innerContent = '<div class="form-wizards-items-aside">' . $innerContent . '</div><div class="form-wizards-element">' . $item . '</div>';
590  } elseif ($wizConf['_POSITION'] === 'top') {
591  $innerContent = '<div class="form-wizards-items-top">' . $innerContent . '</div><div class="form-wizards-element">' . $item . '</div>';
592  } elseif ($wizConf['_POSITION'] === 'bottom') {
593  $innerContent = '<div class="form-wizards-element">' . $item . '</div><div class="form-wizards-items-bottom">' . $innerContent . '</div>';
594  } else {
595  $innerContent = '<div class="form-wizards-element">' . $item . '</div><div class="form-wizards-items-aside">' . $innerContent . '</div>';
596  }
597  $item = '
598  <div class="form-wizards-wrap">
599  ' . $innerContent . '
600  </div>';
601  }
602 
603  return $item;
604  }
605 
609  protected function getLanguageService()
610  {
611  return $GLOBALS['LANG'];
612  }
613 }
static isFirstPartOfStr($str, $partStr)
static forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:31
static callUserFunction($funcName, &$params, &$ref, $_='', $errorMode=0)
static getIconHtml($icon, $alt='', $title='')
static calcAge($seconds, $labels='min|hrs|days|yrs|min|hour|day|year')
static hmac($input, $additionalSecret='')
renderWizards( $itemKinds=null, $wizConf=null, $table=null, $row=null, $fieldName=null, $PA=null, $itemName=null, $specConf=null, $RTE=null)
static makeInstance($className,... $constructorArguments)
static time($value, $withSeconds=true)
static implodeArrayForUrl($name, array $theArray, $str='', $skipBlank=false, $rawurlencodeParamName=false)
static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
static formatSize($sizeInBytes, $labels='', $base=0)
__construct(NodeFactory $nodeFactory, array $data)
formatValue($format, $itemValue, $formatOptions=[])
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']