TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
backend/Classes/Form/Element/AbstractFormElement.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Backend\Form\Element;
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 
36 
40 abstract class AbstractFormElement extends AbstractNode
41 {
47  protected $defaultInputWidth = 30;
48 
54  protected $minimumInputWidth = 10;
55 
61  protected $maxInputWidth = 50;
62 
66  protected $clipboard = null;
67 
71  protected $nodeFactory;
72 
79  public function __construct(NodeFactory $nodeFactory, array $data)
80  {
81  parent::__construct($nodeFactory, $data);
82  $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
83  // @todo: this must vanish as soon as elements are clean
84  $this->nodeFactory = $nodeFactory;
85  }
86 
90  protected function isWizardsDisabled()
91  {
92  return !empty($this->data['disabledWizards']);
93  }
94 
101  protected function formMaxWidth($size = 48)
102  {
103  $compensationForLargeDocuments = 1.33;
104  $compensationForFormFields = 12;
105 
106  $size = round($size * $compensationForLargeDocuments);
107  return ceil($size * $compensationForFormFields);
108  }
109 
113  protected $iconFactory;
114 
131  protected function renderWizards($itemKinds, $wizConf, $table, $row, $field, $PA, $itemName, $specConf, $RTE = false)
132  {
133  // Return not changed main item directly if wizards are disabled
134  if (!is_array($wizConf) || $this->isWizardsDisabled()) {
135  return $itemKinds[0];
136  }
137 
138  $languageService = $this->getLanguageService();
139 
140  $fieldChangeFunc = $PA['fieldChangeFunc'];
141  $item = $itemKinds[0];
142  $md5ID = 'ID' . GeneralUtility::shortMD5($itemName);
143  $prefixOfFormElName = 'data[' . $table . '][' . $row['uid'] . '][' . $field . ']';
144  $flexFormPath = '';
145  if (GeneralUtility::isFirstPartOfStr($PA['itemFormElName'], $prefixOfFormElName)) {
146  $flexFormPath = str_replace('][', '/', substr($PA['itemFormElName'], strlen($prefixOfFormElName) + 1, -1));
147  }
148 
149  // Add a suffix-value if the item is a selector box with renderType "selectSingleBox":
150  if ($PA['fieldConf']['config']['type'] === 'select' && (int)$PA['fieldConf']['config']['maxitems'] > 1 && $PA['fieldConf']['config']['renderType'] === 'selectSingleBox') {
151  $itemName .= '[]';
152  }
153 
154  // Contains wizard identifiers enabled for this record type, see "special configuration" docs
155  $wizardsEnabledByType = $specConf['wizards']['parameters'];
156 
157  $buttonWizards = [];
158  $otherWizards = [];
159  foreach ($wizConf as $wizardIdentifier => $wizardConfiguration) {
160  if (!isset($wizardConfiguration['module']['name']) && isset($wizardConfiguration['script'])) {
161  throw new \InvalidArgumentException('The way registering a wizard in TCA has changed in 6.2 and was removed in CMS 7. '
162  . 'Please set module[name]=module_name instead of using script=path/to/script.php in your TCA. ', 1437750231);
163  }
164 
165  // If an identifier starts with "_", this is a configuration option like _POSITION and not a wizard
166  if ($wizardIdentifier[0] === '_') {
167  continue;
168  }
169 
170  // Sanitize wizard type
171  $wizardConfiguration['type'] = (string)$wizardConfiguration['type'];
172 
173  // Wizards can be shown based on selected "type" of record. If this is the case, the wizard configuration
174  // is set to enableByTypeConfig = 1, and the wizardIdentifier is found in $wizardsEnabledByType
175  $wizardIsEnabled = true;
176  if (
177  isset($wizardConfiguration['enableByTypeConfig'])
178  && (bool)$wizardConfiguration['enableByTypeConfig']
179  && (!is_array($wizardsEnabledByType) || !in_array($wizardIdentifier, $wizardsEnabledByType))
180  ) {
181  $wizardIsEnabled = false;
182  }
183  // Disable if wizard is for RTE fields only and the handled field is no RTE field or RTE can not be loaded
184  if (isset($wizardConfiguration['RTEonly']) && (bool)$wizardConfiguration['RTEonly'] && !$RTE) {
185  $wizardIsEnabled = false;
186  }
187  // Disable if wizard is for not-new records only and we're handling a new record
188  if (isset($wizardConfiguration['notNewRecords']) && $wizardConfiguration['notNewRecords'] && !MathUtility::canBeInterpretedAsInteger($row['uid'])) {
189  $wizardIsEnabled = false;
190  }
191  // Wizard types script, colorbox and popup must contain a module name configuration
192  if (!isset($wizardConfiguration['module']['name']) && in_array($wizardConfiguration['type'], ['script', 'colorbox', 'popup'], true)) {
193  $wizardIsEnabled = false;
194  }
195 
196  if (!$wizardIsEnabled) {
197  continue;
198  }
199 
200  // Title / icon:
201  $iTitle = htmlspecialchars($languageService->sL($wizardConfiguration['title']));
202  if (isset($wizardConfiguration['icon'])) {
203  $icon = FormEngineUtility::getIconHtml($wizardConfiguration['icon'], $iTitle, $iTitle);
204  } else {
205  $icon = $iTitle;
206  }
207 
208  switch ($wizardConfiguration['type']) {
209  case 'userFunc':
210  $params = [];
211  $params['params'] = $wizardConfiguration['params'];
212  $params['exampleImg'] = $wizardConfiguration['exampleImg'];
213  $params['table'] = $table;
214  $params['uid'] = $row['uid'];
215  $params['pid'] = $row['pid'];
216  $params['field'] = $field;
217  $params['flexFormPath'] = $flexFormPath;
218  $params['md5ID'] = $md5ID;
219  $params['returnUrl'] = $this->data['returnUrl'];
220 
221  $params['formName'] = 'editform';
222  $params['itemName'] = $itemName;
223  $params['hmac'] = GeneralUtility::hmac($params['formName'] . $params['itemName'], 'wizard_js');
224  $params['fieldChangeFunc'] = $fieldChangeFunc;
225  $params['fieldChangeFuncHash'] = GeneralUtility::hmac(serialize($fieldChangeFunc));
226 
227  $params['item'] = &$item;
228  $params['icon'] = $icon;
229  $params['iTitle'] = $iTitle;
230  $params['wConf'] = $wizardConfiguration;
231  $params['row'] = $row;
232  $otherWizards[] = GeneralUtility::callUserFunction($wizardConfiguration['userFunc'], $params, $this);
233  break;
234 
235  case 'script':
236  $params = [];
237  $params['params'] = $wizardConfiguration['params'];
238  $params['exampleImg'] = $wizardConfiguration['exampleImg'];
239  $params['table'] = $table;
240  $params['uid'] = $row['uid'];
241  $params['pid'] = $row['pid'];
242  $params['field'] = $field;
243  $params['flexFormPath'] = $flexFormPath;
244  $params['md5ID'] = $md5ID;
245  $params['returnUrl'] = $this->data['returnUrl'];
246 
247  // Resolving script filename and setting URL.
248  $urlParameters = [];
249  if (isset($wizardConfiguration['module']['urlParameters']) && is_array($wizardConfiguration['module']['urlParameters'])) {
250  $urlParameters = $wizardConfiguration['module']['urlParameters'];
251  }
252  $wScript = BackendUtility::getModuleUrl($wizardConfiguration['module']['name'], $urlParameters, '');
253  $url = $wScript . (strstr($wScript, '?') ? '' : '?') . GeneralUtility::implodeArrayForUrl('', ['P' => $params]);
254  $buttonWizards[] =
255  '<a class="btn btn-default" href="' . htmlspecialchars($url) . '" onclick="this.blur(); return !TBE_EDITOR.isFormChanged();">'
256  . $icon .
257  '</a>';
258  break;
259 
260  case 'popup':
261  $params = [];
262  $params['params'] = $wizardConfiguration['params'];
263  $params['exampleImg'] = $wizardConfiguration['exampleImg'];
264  $params['table'] = $table;
265  $params['uid'] = $row['uid'];
266  $params['pid'] = $row['pid'];
267  $params['field'] = $field;
268  $params['flexFormPath'] = $flexFormPath;
269  $params['md5ID'] = $md5ID;
270  $params['returnUrl'] = $this->data['returnUrl'];
271 
272  $params['formName'] = 'editform';
273  $params['itemName'] = $itemName;
274  $params['hmac'] = GeneralUtility::hmac($params['formName'] . $params['itemName'], 'wizard_js');
275  $params['fieldChangeFunc'] = $fieldChangeFunc;
276  $params['fieldChangeFuncHash'] = GeneralUtility::hmac(serialize($fieldChangeFunc));
277 
278  // Resolving script filename and setting URL.
279  $urlParameters = [];
280  if (isset($wizardConfiguration['module']['urlParameters']) && is_array($wizardConfiguration['module']['urlParameters'])) {
281  $urlParameters = $wizardConfiguration['module']['urlParameters'];
282  }
283  $wScript = BackendUtility::getModuleUrl($wizardConfiguration['module']['name'], $urlParameters, '');
284  $url = $wScript . (strstr($wScript, '?') ? '' : '?') . GeneralUtility::implodeArrayForUrl('', ['P' => $params]);
285 
286  $onlyIfSelectedJS = '';
287  if (isset($wizardConfiguration['popup_onlyOpenIfSelected']) && $wizardConfiguration['popup_onlyOpenIfSelected']) {
288  $notSelectedText = $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.noSelItemForEdit');
289  $onlyIfSelectedJS =
290  'if (!TBE_EDITOR.curSelected(' . GeneralUtility::quoteJSvalue($itemName) . ')){' .
291  'alert(' . GeneralUtility::quoteJSvalue($notSelectedText) . ');' .
292  'return false;' .
293  '}';
294  }
295  $aOnClick =
296  'this.blur();' .
297  $onlyIfSelectedJS .
298  'vHWin=window.open(' . GeneralUtility::quoteJSvalue($url) . '+\'&P[currentValue]=\'+TBE_EDITOR.rawurlencode(' .
299  'document.editform[' . GeneralUtility::quoteJSvalue($itemName) . '].value,300' .
300  ')' .
301  '+\'&P[currentSelectedValues]=\'+TBE_EDITOR.curSelected(' . GeneralUtility::quoteJSvalue($itemName) . '),' .
302  GeneralUtility::quoteJSvalue('popUp' . $md5ID) . ',' .
303  GeneralUtility::quoteJSvalue($wizardConfiguration['JSopenParams']) .
304  ');' .
305  'vHWin.focus();' .
306  'return false;';
307 
308  $buttonWizards[] =
309  '<a class="btn btn-default" href="#" onclick="' . htmlspecialchars($aOnClick) . '">' .
310  $icon .
311  '</a>';
312  break;
313 
314  case 'slider':
315  $params = [];
316  $params['fieldConfig'] = $PA['fieldConf']['config'];
317  $params['field'] = $field;
318  $params['table'] = $table;
319  $params['flexFormPath'] = $flexFormPath;
320  $params['md5ID'] = $md5ID;
321  $params['itemName'] = $itemName;
322  $params['wConf'] = $wizardConfiguration;
323  $params['row'] = $row;
324 
326  $wizard = GeneralUtility::makeInstance(ValueSliderWizard::class);
327  $otherWizards[] = $wizard->renderWizard($params);
328  break;
329 
330  case 'select':
331  // The select wizard is a select drop down added to the main element. It provides all the functionality
332  // that select items can do for us, so we process this element via data processing.
333  // @todo: This should be embedded in an own provider called in the main data group to not handle this on the fly here
334 
335  // Select wizard page TS can be set in TCEFORM."table"."field".wizards."wizardName"
336  $pageTsConfig = [];
337  if (isset($this->data['pageTsConfig']['TCEFORM.'][$table . '.'][$field . '.']['wizards.'][$wizardIdentifier . '.'])
338  && is_array($this->data['pageTsConfig']['TCEFORM.'][$table . '.'][$field . '.']['wizards.'][$wizardIdentifier . '.'])
339  ) {
340  $pageTsConfig['TCEFORM.']['dummySelectWizard.'][$wizardIdentifier . '.'] = $this->data['pageTsConfig']['TCEFORM.'][$table . '.'][$field . '.']['wizards.'][$wizardIdentifier . '.'];
341  }
342  $selectWizardDataInput = [
343  'tableName' => 'dummySelectWizard',
344  'command' => 'edit',
345  'pageTsConfig' => $pageTsConfig,
346  'processedTca' => [
347  'ctrl' => [],
348  'columns' => [
349  $wizardIdentifier => [
350  'type' => 'select',
351  'renderType' => 'selectSingle',
352  'config' => $wizardConfiguration,
353  ],
354  ],
355  ],
356  ];
358  $formDataGroup = GeneralUtility::makeInstance(OnTheFly::class);
359  $formDataGroup->setProviderList([ TcaSelectItems::class ]);
361  $formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup);
362  $compilerResult = $formDataCompiler->compile($selectWizardDataInput);
363  $selectWizardItems = $compilerResult['processedTca']['columns'][$wizardIdentifier]['config']['items'];
364 
365  $options = [];
366  $options[] = '<option>' . $iTitle . '</option>';
367  foreach ($selectWizardItems as $selectWizardItem) {
368  $options[] = '<option value="' . htmlspecialchars($selectWizardItem[1]) . '">' . htmlspecialchars($selectWizardItem[0]) . '</option>';
369  }
370  if ($wizardConfiguration['mode'] == 'append') {
371  $assignValue = 'document.querySelectorAll(' . GeneralUtility::quoteJSvalue('[data-formengine-input-name="' . $itemName . '"]') . ')[0].value=\'\'+this.options[this.selectedIndex].value+document.editform[' . GeneralUtility::quoteJSvalue($itemName) . '].value';
372  } elseif ($wizardConfiguration['mode'] == 'prepend') {
373  $assignValue = 'document.querySelectorAll(' . GeneralUtility::quoteJSvalue('[data-formengine-input-name="' . $itemName . '"]') . ')[0].value+=\'\'+this.options[this.selectedIndex].value';
374  } else {
375  $assignValue = 'document.querySelectorAll(' . GeneralUtility::quoteJSvalue('[data-formengine-input-name="' . $itemName . '"]') . ')[0].value=this.options[this.selectedIndex].value';
376  }
377  $otherWizards[] =
378  '<select' .
379  ' id="' . StringUtility::getUniqueId('tceforms-select-') . '"' .
380  ' class="form-control tceforms-select tceforms-wizardselect"' .
381  ' onchange="' . htmlspecialchars($assignValue . ';this.blur();this.selectedIndex=0;' . implode('', $fieldChangeFunc)) . '"' .
382  '>' .
383  implode('', $options) .
384  '</select>';
385  break;
386  case 'suggest':
387  if (!empty($PA['fieldTSConfig']['suggest.']['default.']['hide'])) {
388  break;
389  }
390  // The suggest wizard needs to know if we're in flex form scope to use the dataStructureIdentifier.
391  // If so, add the processedTca of the flex config as wizard argument.
392  $flexFormConfig = [];
393  if ($this->data['processedTca']['columns'][$field]['config']['type'] === 'flex') {
394  $flexFormConfig = $this->data['processedTca']['columns'][$field];
395  }
397  $suggestWizard = GeneralUtility::makeInstance(SuggestWizard::class);
398  $otherWizards[] = $suggestWizard->renderSuggestSelector($PA['itemFormElName'], $table, $field, $row, $PA, $flexFormConfig);
399  break;
400  }
401  }
402 
403  // For each rendered wizard, put them together around the item.
404  if (!empty($buttonWizards) || !empty($otherWizards)) {
405  $innerContent = '';
406  if (!empty($buttonWizards)) {
407  $innerContent .= '<div class="btn-group' . ($wizConf['_VERTICAL'] ? ' btn-group-vertical' : '') . '">' . implode('', $buttonWizards) . '</div>';
408  }
409  $innerContent .= implode(' ', $otherWizards);
410 
411  // Position
412  $classes = ['form-wizards-wrap'];
413  if ($wizConf['_POSITION'] === 'left') {
414  $classes[] = 'form-wizards-aside';
415  $innerContent = '<div class="form-wizards-items">' . $innerContent . '</div><div class="form-wizards-element">' . $item . '</div>';
416  } elseif ($wizConf['_POSITION'] === 'top') {
417  $classes[] = 'form-wizards-top';
418  $innerContent = '<div class="form-wizards-items">' . $innerContent . '</div><div class="form-wizards-element">' . $item . '</div>';
419  } elseif ($wizConf['_POSITION'] === 'bottom') {
420  $classes[] = 'form-wizards-bottom';
421  $innerContent = '<div class="form-wizards-element">' . $item . '</div><div class="form-wizards-items">' . $innerContent . '</div>';
422  } else {
423  $classes[] = 'form-wizards-aside';
424  $innerContent = '<div class="form-wizards-element">' . $item . '</div><div class="form-wizards-items">' . $innerContent . '</div>';
425  }
426  $item = '
427  <div class="' . implode(' ', $classes) . '">
428  ' . $innerContent . '
429  </div>';
430  }
431 
432  return $item;
433  }
434 
453  protected function dbFileIcons($fName, $mode, $allowed, $itemArray, $selector = '', $params = [], $_ = null, $table = '', $field = '', $uid = '', $config = [])
454  {
455  $languageService = $this->getLanguageService();
456  $disabled = '';
457  if ($params['readOnly']) {
458  $disabled = ' disabled="disabled"';
459  }
460  // INIT
461  $uidList = [];
462  $opt = [];
463  $itemArrayC = 0;
464  // Creating <option> elements:
465  if (is_array($itemArray)) {
466  $itemArrayC = count($itemArray);
467  switch ($mode) {
468  case 'db':
469  foreach ($itemArray as $pp) {
470  $pRec = BackendUtility::getRecordWSOL($pp['table'], $pp['id']);
471  if (is_array($pRec)) {
472  $pTitle = BackendUtility::getRecordTitle($pp['table'], $pRec, false, true);
473  $pUid = $pp['table'] . '_' . $pp['id'];
474  $uidList[] = $pUid;
475  $title = htmlspecialchars($pTitle);
476  $opt[] = '<option value="' . htmlspecialchars($pUid) . '" title="' . $title . '">' . $title . '</option>';
477  }
478  }
479  break;
480  case 'file_reference':
481 
482  case 'file':
483  foreach ($itemArray as $item) {
484  $itemParts = explode('|', $item);
485  $uidList[] = ($pUid = ($pTitle = $itemParts[0]));
486  $title = htmlspecialchars(rawurldecode($itemParts[1]));
487  $opt[] = '<option value="' . htmlspecialchars(rawurldecode($itemParts[0])) . '" title="' . $title . '">' . $title . '</option>';
488  }
489  break;
490  case 'folder':
491  foreach ($itemArray as $pp) {
492  $pParts = explode('|', $pp);
493  $uidList[] = ($pUid = ($pTitle = $pParts[0]));
494  $title = htmlspecialchars(rawurldecode($pParts[0]));
495  $opt[] = '<option value="' . htmlspecialchars(rawurldecode($pParts[0])) . '" title="' . $title . '">' . $title . '</option>';
496  }
497  break;
498  default:
499  foreach ($itemArray as $pp) {
500  $pParts = explode('|', $pp, 2);
501  $uidList[] = ($pUid = $pParts[0]);
502  $pTitle = $pParts[1];
503  $title = htmlspecialchars(rawurldecode($pTitle));
504  $opt[] = '<option value="' . htmlspecialchars(rawurldecode($pUid)) . '" title="' . $title . '">' . $title . '</option>';
505  }
506  }
507  }
508  // Create selector box of the options
509  $sSize = $params['autoSizeMax']
510  ? MathUtility::forceIntegerInRange($itemArrayC + 1, MathUtility::forceIntegerInRange($params['size'], 1), $params['autoSizeMax'])
511  : $params['size'];
512  if (!$selector) {
513  $maxItems = (int)($params['maxitems'] ?? 0);
514  $size = (int)($params['size'] ?? 0);
515  $classes = ['form-control', 'tceforms-multiselect'];
516  if ($maxItems === 1) {
517  $classes[] = 'form-select-no-siblings';
518  }
519  $isMultiple = $maxItems !== 1 && $size !== 1;
520  $selector = '<select id="' . StringUtility::getUniqueId('tceforms-multiselect-') . '" '
521  . ($params['noList'] ? 'style="display: none"' : 'size="' . $sSize . '" class="' . implode(' ', $classes) . '"')
522  . ($isMultiple ? ' multiple="multiple"' : '')
523  . ' data-formengine-input-name="' . htmlspecialchars($fName) . '" ' . $this->getValidationDataAsDataAttribute($config) . $params['style'] . $disabled . '>' . implode('', $opt)
524  . '</select>';
525  }
526  $icons = [
527  'L' => [],
528  'R' => []
529  ];
530  $rOnClickInline = '';
531  if (!$params['readOnly'] && !$params['noList']) {
532  if (!$params['noBrowser']) {
533  // Check against inline uniqueness
535  $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
536  $inlineStackProcessor->initializeByGivenStructure($this->data['inlineStructure']);
537  $aOnClickInline = '';
538  if ($this->data['isInlineChild'] && $this->data['inlineParentUid']) {
539  if ($this->data['inlineParentConfig']['foreign_table'] === $table
540  && $this->data['inlineParentConfig']['foreign_unique'] === $field
541  ) {
542  $objectPrefix = $inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']) . '-' . $table;
543  $aOnClickInline = $objectPrefix . '|inline.checkUniqueElement|inline.setUniqueElement';
544  $rOnClickInline = 'inline.revertUnique(' . GeneralUtility::quoteJSvalue($objectPrefix) . ',null,' . GeneralUtility::quoteJSvalue($uid) . ');';
545  }
546  }
547  if (is_array($config['appearance']) && isset($config['appearance']['elementBrowserType'])) {
548  $elementBrowserType = $config['appearance']['elementBrowserType'];
549  } else {
550  $elementBrowserType = $mode;
551  }
552  if (is_array($config['appearance']) && isset($config['appearance']['elementBrowserAllowed'])) {
553  $elementBrowserAllowed = $config['appearance']['elementBrowserAllowed'];
554  } else {
555  $elementBrowserAllowed = $allowed;
556  }
557  $aOnClick = 'setFormValueOpenBrowser(' . GeneralUtility::quoteJSvalue($elementBrowserType) . ','
558  . GeneralUtility::quoteJSvalue(($fName . '|||' . $elementBrowserAllowed . '|' . $aOnClickInline)) . '); return false;';
559  $icons['R'][] = '
560  <a href="#"
561  onclick="' . htmlspecialchars($aOnClick) . '"
562  class="btn btn-default"
563  title="' . htmlspecialchars($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.browse_' . ($mode == 'db' ? 'db' : 'file'))) . '">
564  ' . $this->iconFactory->getIcon('actions-insert-record', Icon::SIZE_SMALL)->render() . '
565  </a>';
566  }
567  if (!$params['dontShowMoveIcons']) {
568  if ($sSize >= 5) {
569  $icons['L'][] = '
570  <a href="#"
571  class="btn btn-default t3js-btn-moveoption-top"
572  data-fieldname="' . $fName . '"
573  title="' . htmlspecialchars($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.move_to_top')) . '">
574  ' . $this->iconFactory->getIcon('actions-move-to-top', Icon::SIZE_SMALL)->render() . '
575  </a>';
576  }
577  $icons['L'][] = '
578  <a href="#"
579  class="btn btn-default t3js-btn-moveoption-up"
580  data-fieldname="' . $fName . '"
581  title="' . htmlspecialchars($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.move_up')) . '">
582  ' . $this->iconFactory->getIcon('actions-move-up', Icon::SIZE_SMALL)->render() . '
583  </a>';
584  $icons['L'][] = '
585  <a href="#"
586  class="btn btn-default t3js-btn-moveoption-down"
587  data-fieldname="' . $fName . '"
588  title="' . htmlspecialchars($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.move_down')) . '">
589  ' . $this->iconFactory->getIcon('actions-move-down', Icon::SIZE_SMALL)->render() . '
590  </a>';
591  if ($sSize >= 5) {
592  $icons['L'][] = '
593  <a href="#"
594  class="btn btn-default t3js-btn-moveoption-bottom"
595  data-fieldname="' . $fName . '"
596  title="' . htmlspecialchars($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.move_to_bottom')) . '">
597  ' . $this->iconFactory->getIcon('actions-move-to-bottom', Icon::SIZE_SMALL)->render() . '
598  </a>';
599  }
600  }
601  $clipElements = $this->getClipboardElements($allowed, $mode);
602  if (!empty($clipElements)) {
603  $aOnClick = '';
604  foreach ($clipElements as $elValue) {
605  if ($mode == 'db') {
606  list($itemTable, $itemUid) = explode('|', $elValue);
607  $recordTitle = BackendUtility::getRecordTitle($itemTable, BackendUtility::getRecordWSOL($itemTable, $itemUid));
608  $itemTitle = GeneralUtility::quoteJSvalue($recordTitle);
609  $elValue = $itemTable . '_' . $itemUid;
610  } else {
611  // 'file', 'file_reference' and 'folder' mode
612  $itemTitle = 'unescape(' . GeneralUtility::quoteJSvalue(rawurlencode(basename($elValue))) . ')';
613  }
614  $aOnClick .= 'setFormValueFromBrowseWin(' . GeneralUtility::quoteJSvalue($fName) . ',unescape('
615  . GeneralUtility::quoteJSvalue(rawurlencode(str_replace('%20', ' ', $elValue))) . '),' . $itemTitle . ',' . $itemTitle . ');';
616  }
617  $aOnClick .= 'return false;';
618  $icons['R'][] = '
619  <a href="#"
620  class="btn btn-default"
621  onclick="' . htmlspecialchars($aOnClick) . '"
622  title="' . htmlspecialchars(sprintf($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.clipInsert_' . ($mode == 'db' ? 'db' : 'file')), count($clipElements))) . '">
623  ' . $this->iconFactory->getIcon('actions-document-paste-into', Icon::SIZE_SMALL)->render() . '
624  </a>';
625  }
626  }
627  if (!$params['readOnly'] && !$params['noDelete']) {
628  $icons['L'][] = '
629  <a href="#"
630  class="btn btn-default t3js-btn-removeoption"
631  onClick="' . $rOnClickInline . '"
632  data-fieldname="' . $fName . '"
633  title="' . htmlspecialchars($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.remove_selected')) . '">
634  ' . $this->iconFactory->getIcon('actions-selection-delete', Icon::SIZE_SMALL)->render() . '
635  </a>';
636  }
637 
638  // Thumbnails
639  $imagesOnly = false;
640  if ($params['thumbnails'] && $params['allowed']) {
641  // In case we have thumbnails, check if only images are allowed.
642  // In this case, render them below the field, instead of to the right
643  $allowedExtensionList = $params['allowed'];
644  $imageExtensionList = GeneralUtility::trimExplode(',', strtolower($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']), true);
645  $imagesOnly = true;
646  foreach ($allowedExtensionList as $allowedExtension) {
647  if (!ArrayUtility::inArray($imageExtensionList, $allowedExtension)) {
648  $imagesOnly = false;
649  break;
650  }
651  }
652  }
653  $thumbnails = '';
654  if (is_array($params['thumbnails']) && !empty($params['thumbnails'])) {
655  if ($imagesOnly) {
656  $thumbnails .= '<ul class="list-inline">';
657  foreach ($params['thumbnails'] as $thumbnail) {
658  $thumbnails .= '<li><span class="thumbnail">' . $thumbnail['image'] . '</span></li>';
659  }
660  $thumbnails .= '</ul>';
661  } else {
662  $thumbnails .= '<div class="table-fit"><table class="table table-white"><tbody>';
663  foreach ($params['thumbnails'] as $thumbnail) {
664  $thumbnails .= '
665  <tr>
666  <td class="col-icon">
667  ' . ($config['internal_type'] === 'db'
668  ? BackendUtility::wrapClickMenuOnIcon($thumbnail['image'], $thumbnail['table'], $thumbnail['uid'], 1, '', '+copy,info,edit,view')
669  : $thumbnail['image']) . '
670  </td>
671  <td class="col-title">
672  ' . ($config['internal_type'] === 'db'
673  ? BackendUtility::wrapClickMenuOnIcon($thumbnail['name'], $thumbnail['table'], $thumbnail['uid'], 1, '', '+copy,info,edit,view')
674  : $thumbnail['name']) . '
675  ' . ($config['internal_type'] === 'db' ? ' <span class="text-muted">[' . $thumbnail['uid'] . ']</span>' : '') . '
676  </td>
677  </tr>
678  ';
679  }
680  $thumbnails .= '</tbody></table></div>';
681  }
682  }
683 
684  // Allowed Tables
685  $allowedTables = '';
686  if (is_array($params['allowedTables']) && !empty($params['allowedTables']) && !$params['hideAllowedTables']) {
687  $allowedTables .= '<div class="help-block">';
688  foreach ($params['allowedTables'] as $key => $item) {
689  if (is_array($item)) {
690  if (empty($params['readOnly'])) {
691  $allowedTables .= '<a href="#" onClick="' . htmlspecialchars($item['onClick']) . '" class="btn btn-default">' . $item['icon'] . ' ' . htmlspecialchars($item['name']) . '</a> ';
692  } else {
693  $allowedTables .= '<span>' . htmlspecialchars($item['name']) . '</span> ';
694  }
695  } elseif ($key === 'name') {
696  $allowedTables .= '<span>' . htmlspecialchars($item) . '</span> ';
697  }
698  }
699  $allowedTables .= '</div>';
700  }
701  // Allowed
702  $allowedList = '';
703  if (is_array($params['allowed']) && !empty($params['allowed'])) {
704  foreach ($params['allowed'] as $item) {
705  $allowedList .= '<span class="label label-success">' . strtoupper($item) . '</span> ';
706  }
707  }
708  // Disallowed
709  $disallowedList = '';
710  if (is_array($params['disallowed']) && !empty($params['disallowed'])) {
711  foreach ($params['disallowed'] as $item) {
712  $disallowedList .= '<span class="label label-danger">' . strtoupper($item) . '</span> ';
713  }
714  }
715  // Rightbox
716  $rightbox = ($params['rightbox'] ?: '');
717 
718  // Hook: dbFileIcons_postProcess (requested by FAL-team for use with the "fal" extension)
719  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tceforms.php']['dbFileIcons'])) {
720  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tceforms.php']['dbFileIcons'] as $classRef) {
721  $hookObject = GeneralUtility::getUserObj($classRef);
722  if (!$hookObject instanceof DatabaseFileIconsHookInterface) {
723  throw new \UnexpectedValueException($classRef . ' must implement interface ' . DatabaseFileIconsHookInterface::class, 1290167704);
724  }
725  $additionalParams = [
726  'mode' => $mode,
727  'allowed' => $allowed,
728  'itemArray' => $itemArray,
729  'table' => $table,
730  'field' => $field,
731  'uid' => $uid,
732  'config' => $GLOBALS['TCA'][$table]['columns'][$field]
733  ];
734  $hookObject->dbFileIcons_postProcess($params, $selector, $thumbnails, $icons, $rightbox, $fName, $uidList, $additionalParams, $this);
735  }
736  }
737 
738  // Output
739  $str = '
740  ' . ($params['headers']['selector'] ? '<label>' . $params['headers']['selector'] . '</label>' : '') . '
741  <div class="form-wizards-wrap form-wizards-aside">
742  <div class="form-wizards-element">
743  ' . $selector . '
744  ' . (!$params['noList'] && !empty($allowedTables) ? $allowedTables : '') . '
745  ' . (!$params['noList'] && (!empty($allowedList) || !empty($disallowedList))
746  ? '<div class="help-block">' . $allowedList . $disallowedList . ' </div>'
747  : '') . '
748  </div>
749  ' . (!empty($icons['L']) ? '<div class="form-wizards-items"><div class="btn-group-vertical">' . implode('', $icons['L']) . '</div></div>' : '') . '
750  ' . (!empty($icons['R']) ? '<div class="form-wizards-items"><div class="btn-group-vertical">' . implode('', $icons['R']) . '</div></div>' : '') . '
751  </div>
752  ';
753  if ($rightbox) {
754  $str = '
755  <div class="form-multigroup-wrap t3js-formengine-field-group">
756  <div class="form-multigroup-item form-multigroup-element">' . $str . '</div>
757  <div class="form-multigroup-item form-multigroup-element">
758  ' . ($params['headers']['items'] ? '<label>' . $params['headers']['items'] . '</label>' : '') . '
759  ' . ($params['headers']['selectorbox'] ? '<div class="form-multigroup-item-wizard">' . $params['headers']['selectorbox'] . '</div>' : '') . '
760  ' . $rightbox . '
761  </div>
762  </div>
763  ';
764  }
765  $str .= $thumbnails;
766 
767  // Creating the hidden field which contains the actual value as a comma list.
768  $str .= '<input type="hidden" name="' . $fName . '" value="' . htmlspecialchars(implode(',', $uidList)) . '" />';
769  return $str;
770  }
771 
779  protected function getClipboardElements($allowed, $mode)
780  {
781  if (!is_object($this->clipboard)) {
782  $this->clipboard = GeneralUtility::makeInstance(Clipboard::class);
783  $this->clipboard->initializeClipboard();
784  }
785 
786  $output = [];
787  switch ($mode) {
788  case 'file_reference':
789 
790  case 'file':
791  $elFromTable = $this->clipboard->elFromTable('_FILE');
792  $allowedExts = GeneralUtility::trimExplode(',', $allowed, true);
793  // If there are a set of allowed extensions, filter the content:
794  if ($allowedExts) {
795  foreach ($elFromTable as $elValue) {
796  $pI = pathinfo($elValue);
797  $ext = strtolower($pI['extension']);
798  if (in_array($ext, $allowedExts)) {
799  $output[] = $elValue;
800  }
801  }
802  } else {
803  // If all is allowed, insert all: (This does NOT respect any disallowed extensions,
804  // but those will be filtered away by the backend DataHandler)
805  $output = $elFromTable;
806  }
807  break;
808  case 'db':
809  $allowedTables = GeneralUtility::trimExplode(',', $allowed, true);
810  // All tables allowed for relation:
811  if (trim($allowedTables[0]) === '*') {
812  $output = $this->clipboard->elFromTable('');
813  } else {
814  // Only some tables, filter them:
815  foreach ($allowedTables as $tablename) {
816  $elFromTable = $this->clipboard->elFromTable($tablename);
817  $output = array_merge($output, $elFromTable);
818  }
819  }
820  $output = array_keys($output);
821  break;
822  }
823 
824  return $output;
825  }
826 
830  protected function getLanguageService()
831  {
832  return $GLOBALS['LANG'];
833  }
834 }
static getRecordWSOL($table, $uid, $fields= '*', $where= '', $useDeleteClause=true, $unsetMovePointers=false)
static getIconHtml($icon, $alt= '', $title= '')
static isFirstPartOfStr($str, $partStr)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
static implodeArrayForUrl($name, array $theArray, $str= '', $skipBlank=false, $rawurlencodeParamName=false)
static getRecordTitle($table, $row, $prep=false, $forceResult=true)
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static makeInstance($className,...$constructorArguments)
static forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:31
static wrapClickMenuOnIcon($content, $table, $uid=0, $listFrame=true, $addParams= '', $enDisItems= '', $returnTagParameters=false)
static hmac($input, $additionalSecret= '')
static callUserFunction($funcName, &$params, &$ref, $_= '', $errorMode=0)