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