2 namespace TYPO3\CMS\Backend\Form\Element
;
5 * This file is part of the TYPO3 CMS project.
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.
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
14 * The TYPO3 project - inspiring people to share!
17 use TYPO3\CMS\Backend\Form\FormEngine
;
18 use TYPO3\CMS\Backend\Form\DataPreprocessor
;
19 use TYPO3\CMS\Backend\Template\DocumentTemplate
;
20 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication
;
21 use TYPO3\CMS\Core\Database\DatabaseConnection
;
22 use TYPO3\CMS\Lang\LanguageService
;
23 use TYPO3\CMS\Core\Utility\GeneralUtility
;
24 use TYPO3\CMS\Backend\Form\Utility\FormEngineUtility
;
25 use TYPO3\CMS\Core\Utility\MathUtility
;
26 use TYPO3\CMS\Backend\Utility\BackendUtility
;
27 use TYPO3\CMS\Backend\Utility\IconUtility
;
28 use TYPO3\CMS\Backend\Form\Wizard\SuggestWizard
;
29 use TYPO3\CMS\Backend\Form\Wizard\ValueSliderWizard
;
30 use TYPO3\CMS\Core\Utility\ArrayUtility
;
31 use TYPO3\CMS\Backend\Form\DatabaseFileIconsHookInterface
;
32 use TYPO3\CMS\Backend\Clipboard\Clipboard
;
35 * Base class for form elements of FormEngine
37 abstract class AbstractFormElement
{
42 protected $formEngine;
45 * A list of global options given from FormEngine to child elements
49 protected $globalOptions = array();
52 * Default width value for a couple of elements like text
56 protected $defaultInputWidth = 30;
59 * Minimum width value for a couple of elements like text
63 protected $minimumInputWidth = 10;
66 * Maximum width value for a couple of elements like text
70 protected $maxInputWidth = 50;
73 * @var \TYPO3\CMS\Backend\Clipboard\Clipboard|NULL
75 protected $clipboard = NULL;
78 * Constructor function, setting the FormEngine
80 * @param FormEngine $formEngine
82 public function __construct(FormEngine
$formEngine) {
83 $this->formEngine
= $formEngine;
87 * Set global options from parent FormEngine instance
89 * @param array $globalOptions Global options like 'readonly' for all elements
90 * @return AbstractFormElement
92 public function setGlobalOptions(array $globalOptions) {
93 $this->globalOptions
= $globalOptions;
98 * Handler for Flex Forms
100 * @param string $table The table name of the record
101 * @param string $field The field name which this element is supposed to edit
102 * @param array $row The record data array where the value(s) for the field can be found
103 * @param array $additionalInformation An array with additional configuration options.
104 * @return string The HTML code for the TCEform field
106 abstract public function render($table, $field, $row, &$additionalInformation);
109 * @return bool TRUE if field is set to read only
111 protected function isGlobalReadonly() {
112 return !empty($this->globalOptions
['renderReadonly']);
116 * @return bool TRUE if wizards are disabled on a global level
118 protected function isWizardsDisabled() {
119 return !empty($this->globalOptions
['disabledWizards']);
123 * @return string URL to return to this entry script
125 protected function getReturnUrl() {
126 return isset($this->globalOptions
['returnUrl']) ?
$this->globalOptions
['returnUrl'] : '';
130 * Returns the max width in pixels for a elements like input and text
132 * @param int $size The abstract size value (1-48)
133 * @return int Maximum width in pixels
135 protected function formMaxWidth($size = 48) {
136 $compensationForLargeDocuments = 1.33;
137 $compensationForFormFields = 12;
139 $size = round($size * $compensationForLargeDocuments);
140 return ceil($size * $compensationForFormFields);
144 * Rendering wizards for form fields.
146 * @param array $itemKinds Array with the real item in the first value, and an alternative item in the second value.
147 * @param array $wizConf The "wizard" key from the config array for the field (from TCA)
148 * @param string $table Table name
149 * @param array $row The record array
150 * @param string $field The field name
151 * @param array $PA Additional configuration array.
152 * @param string $itemName The field name
153 * @param array $specConf Special configuration if available.
154 * @param bool $RTE Whether the RTE could have been loaded.
155 * @return string The new item value.
157 protected function renderWizards($itemKinds, $wizConf, $table, $row, $field, $PA, $itemName, $specConf, $RTE = FALSE) {
158 // Return not changed main item directly if wizards are disabled
159 if (!is_array($wizConf) ||
$this->isWizardsDisabled()) {
160 return $itemKinds[0];
163 $languageService = $this->getLanguageService();
165 $fieldChangeFunc = $PA['fieldChangeFunc'];
166 $item = $itemKinds[0];
167 $fName = '[' . $table . '][' . $row['uid'] . '][' . $field . ']';
168 $md5ID = 'ID' . GeneralUtility
::shortmd5($itemName);
169 $fieldConfig = $PA['fieldConf']['config'];
170 $prefixOfFormElName = 'data[' . $table . '][' . $row['uid'] . '][' . $field . ']';
172 if (GeneralUtility
::isFirstPartOfStr($PA['itemFormElName'], $prefixOfFormElName)) {
173 $flexFormPath = str_replace('][', '/', substr($PA['itemFormElName'], strlen($prefixOfFormElName) +
1, -1));
176 // Manipulate the field name (to be the TRUE form field name) and remove
177 // a suffix-value if the item is a selector box with renderMode "singlebox":
179 if ($PA['fieldConf']['config']['form_type'] == 'select') {
180 // Single select situation:
181 if ($PA['fieldConf']['config']['maxitems'] <= 1) {
183 } elseif ($PA['fieldConf']['config']['renderMode'] == 'singlebox') {
189 // Contains wizard identifiers enabled for this record type, see "special configuration" docs
190 $wizardsEnabledByType = $specConf['wizards']['parameters'];
192 $buttonWizards = array();
193 $otherWizards = array();
194 foreach ($wizConf as $wizardIdentifier => $wizardConfiguration) {
195 // If an identifier starts with "_", this is a configuration option like _POSITION and not a wizard
196 if ($wizardIdentifier[0] === '_') {
200 // Sanitize wizard type
201 $wizardConfiguration['type'] = (string)$wizardConfiguration['type'];
203 // Wizards can be shown based on selected "type" of record. If this is the case, the wizard configuration
204 // is set to enableByTypeConfig = 1, and the wizardIdentifier is found in $wizardsEnabledByType
205 $wizardIsEnabled = TRUE;
207 isset($wizardConfiguration['enableByTypeConfig'])
208 && (bool)$wizardConfiguration['enableByTypeConfig']
209 && (!is_array($wizardsEnabledByType) ||
!in_array($wizardIdentifier, $wizardsEnabledByType))
211 $wizardIsEnabled = FALSE;
213 // Disable if wizard is for RTE fields only and the handled field is no RTE field or RTE can not be loaded
214 if (isset($wizardConfiguration['RTEonly']) && (bool)$wizardConfiguration['RTEonly'] && !$RTE) {
215 $wizardIsEnabled = FALSE;
217 // Disable if wizard is for not-new records only and we're handling a new record
218 if (isset($wizardConfiguration['notNewRecords']) && $wizardConfiguration['notNewRecords'] && !MathUtility
::canBeInterpretedAsInteger($row['uid'])) {
219 $wizardIsEnabled = FALSE;
221 // Wizard types script, colorbox and popup must contain a module name configuration
222 if (!isset($wizardConfiguration['module']['name']) && in_array($wizardConfiguration['type'], array('script', 'colorbox', 'popup'), TRUE)) {
223 $wizardIsEnabled = FALSE;
226 if (!$wizardIsEnabled) {
231 $iTitle = htmlspecialchars($languageService->sL($wizardConfiguration['title']));
232 if (isset($wizardConfiguration['icon'])) {
233 $icon = FormEngineUtility
::getIconHtml($wizardConfiguration['icon'], $iTitle, $iTitle);
238 switch ($wizardConfiguration['type']) {
241 $params['fieldConfig'] = $fieldConfig;
242 $params['params'] = $wizardConfiguration['params'];
243 $params['exampleImg'] = $wizardConfiguration['exampleImg'];
244 $params['table'] = $table;
245 $params['uid'] = $row['uid'];
246 $params['pid'] = $row['pid'];
247 $params['field'] = $field;
248 $params['flexFormPath'] = $flexFormPath;
249 $params['md5ID'] = $md5ID;
250 $params['returnUrl'] = $this->getReturnUrl();
252 $params['formName'] = 'editform';
253 $params['itemName'] = $itemName;
254 $params['hmac'] = GeneralUtility
::hmac($params['formName'] . $params['itemName'], 'wizard_js');
255 $params['fieldChangeFunc'] = $fieldChangeFunc;
256 $params['fieldChangeFuncHash'] = GeneralUtility
::hmac(serialize($fieldChangeFunc));
258 $params['item'] = &$item;
259 $params['icon'] = $icon;
260 $params['iTitle'] = $iTitle;
261 $params['wConf'] = $wizardConfiguration;
262 $params['row'] = $row;
263 $formEngineDummy = new FormEngine
;
264 $otherWizards[] = GeneralUtility
::callUserFunction($wizardConfiguration['userFunc'], $params, $formEngineDummy);
269 // Including the full fieldConfig from TCA may produce too long an URL
270 if ($wizardIdentifier != 'RTE') {
271 $params['fieldConfig'] = $fieldConfig;
273 $params['params'] = $wizardConfiguration['params'];
274 $params['exampleImg'] = $wizardConfiguration['exampleImg'];
275 $params['table'] = $table;
276 $params['uid'] = $row['uid'];
277 $params['pid'] = $row['pid'];
278 $params['field'] = $field;
279 $params['flexFormPath'] = $flexFormPath;
280 $params['md5ID'] = $md5ID;
281 $params['returnUrl'] = $this->getReturnUrl();
283 // Resolving script filename and setting URL.
284 $urlParameters = array();
285 if (isset($wizardConfiguration['module']['urlParameters']) && is_array($wizardConfiguration['module']['urlParameters'])) {
286 $urlParameters = $wizardConfiguration['module']['urlParameters'];
288 $wScript = BackendUtility
::getModuleUrl($wizardConfiguration['module']['name'], $urlParameters, '');
289 $url = $wScript . (strstr($wScript, '?') ?
'' : '?') . GeneralUtility
::implodeArrayForUrl('', array('P' => $params));
291 '<a class="btn btn-default" href="' . htmlspecialchars($url) . '" onclick="this.blur(); return !TBE_EDITOR.isFormChanged();">'
298 $params['fieldConfig'] = $fieldConfig;
299 $params['params'] = $wizardConfiguration['params'];
300 $params['exampleImg'] = $wizardConfiguration['exampleImg'];
301 $params['table'] = $table;
302 $params['uid'] = $row['uid'];
303 $params['pid'] = $row['pid'];
304 $params['field'] = $field;
305 $params['flexFormPath'] = $flexFormPath;
306 $params['md5ID'] = $md5ID;
307 $params['returnUrl'] = $this->getReturnUrl();
309 $params['formName'] = 'editform';
310 $params['itemName'] = $itemName;
311 $params['hmac'] = GeneralUtility
::hmac($params['formName'] . $params['itemName'], 'wizard_js');
312 $params['fieldChangeFunc'] = $fieldChangeFunc;
313 $params['fieldChangeFuncHash'] = GeneralUtility
::hmac(serialize($fieldChangeFunc));
315 // Resolving script filename and setting URL.
316 $urlParameters = array();
317 if (isset($wizardConfiguration['module']['urlParameters']) && is_array($wizardConfiguration['module']['urlParameters'])) {
318 $urlParameters = $wizardConfiguration['module']['urlParameters'];
320 $wScript = BackendUtility
::getModuleUrl($wizardConfiguration['module']['name'], $urlParameters, '');
321 $url = $wScript . (strstr($wScript, '?') ?
'' : '?') . GeneralUtility
::implodeArrayForUrl('', array('P' => $params));
323 $onlyIfSelectedJS = '';
324 if (isset($wizardConfiguration['popup_onlyOpenIfSelected']) && $wizardConfiguration['popup_onlyOpenIfSelected']) {
325 $notSelectedText = $languageService->sL('LLL:EXT:lang/locallang_core.xlf:mess.noSelItemForEdit');
327 'if (!TBE_EDITOR.curSelected(\'' . $itemName . $listFlag . '\')){' .
328 'alert(' . GeneralUtility
::quoteJSvalue($notSelectedText) . ');' .
335 'vHWin=window.open(' .
336 '\'' . $url . '\'+\'&P[currentValue]=\'+TBE_EDITOR.rawurlencode(' .
337 'document.editform[\'' . $itemName . '\'].value,200' .
339 '+\'&P[currentSelectedValues]=\'+TBE_EDITOR.curSelected(\'' . $itemName . $listFlag . '\'),' .
340 '\'popUp' . $md5ID . '\',' .
341 '\'' . $wizardConfiguration['JSopenParams'] . '\'' .
347 '<a class="btn btn-default" href="#" onclick="' . htmlspecialchars($aOnClick) . '">' .
354 $params['fieldConfig'] = $fieldConfig;
355 $params['params'] = $wizardConfiguration['params'];
356 $params['exampleImg'] = $wizardConfiguration['exampleImg'];
357 $params['table'] = $table;
358 $params['uid'] = $row['uid'];
359 $params['pid'] = $row['pid'];
360 $params['field'] = $field;
361 $params['flexFormPath'] = $flexFormPath;
362 $params['md5ID'] = $md5ID;
363 $params['returnUrl'] = $this->getReturnUrl();
365 $params['formName'] = 'editform';
366 $params['itemName'] = $itemName;
367 $params['hmac'] = GeneralUtility
::hmac($params['formName'] . $params['itemName'], 'wizard_js');
368 $params['fieldChangeFunc'] = $fieldChangeFunc;
369 $params['fieldChangeFuncHash'] = GeneralUtility
::hmac(serialize($fieldChangeFunc));
371 // Resolving script filename and setting URL.
372 $urlParameters = array();
373 if (isset($wizardConfiguration['module']['urlParameters']) && is_array($wizardConfiguration['module']['urlParameters'])) {
374 $urlParameters = $wizardConfiguration['module']['urlParameters'];
376 $wScript = BackendUtility
::getModuleUrl($wizardConfiguration['module']['name'], $urlParameters, '');
377 $url = $wScript . (strstr($wScript, '?') ?
'' : '?') . GeneralUtility
::implodeArrayForUrl('', array('P' => $params));
381 'vHWin=window.open(' .
382 '\'' . $url . '\'+\'&P[currentValue]=\'+TBE_EDITOR.rawurlencode(' .
383 'document.editform[\'' . $itemName . '\'].value,200' .
385 '+\'&P[currentSelectedValues]=\'+TBE_EDITOR.curSelected(\'' . $itemName . $listFlag . '\'),' .
386 '\'popUp' . $md5ID . '\',' .
387 '\'' . $wizardConfiguration['JSopenParams'] . '\'' .
392 $dim = GeneralUtility
::intExplode('x', $wizardConfiguration['dim']);
393 $dX = MathUtility
::forceIntegerInRange($dim[0], 1, 200, 20);
394 $dY = MathUtility
::forceIntegerInRange($dim[1], 1, 200, 20);
395 $color = $PA['itemFormElValue'] ?
' bgcolor="' . htmlspecialchars($PA['itemFormElValue']) . '"' : '';
396 $skinImg = IconUtility
::skinImg(
398 $PA['itemFormElValue'] === '' ?
'gfx/colorpicker_empty.png' : 'gfx/colorpicker.png',
399 'width="' . $dX . '" height="' . $dY . '"' . BackendUtility
::titleAltAttrib(trim($iTitle . ' ' . $PA['itemFormElValue'])) . ' border="0"'
402 '<table border="0" id="' . $md5ID . '"' . $color . ' style="' . htmlspecialchars($wizardConfiguration['tableStyle']) . '">' .
405 '<a class="btn btn-default" href="#" onclick="' . htmlspecialchars($aOnClick) . '">' . '<img ' . $skinImg . '>' . '</a>' .
413 $params['fieldConfig'] = $fieldConfig;
414 $params['field'] = $field;
415 $params['flexFormPath'] = $flexFormPath;
416 $params['md5ID'] = $md5ID;
417 $params['itemName'] = $itemName;
418 $params['fieldChangeFunc'] = $fieldChangeFunc;
419 $params['wConf'] = $wizardConfiguration;
420 $params['row'] = $row;
422 /** @var ValueSliderWizard $wizard */
423 $wizard = GeneralUtility
::makeInstance(ValueSliderWizard
::class);
424 $otherWizards[] = $wizard->renderWizard($params);
428 $fieldValue = array('config' => $wizardConfiguration);
429 $TSconfig = FormEngineUtility
::getTSconfigForTableRow($table, $row);
430 $TSconfig[$field] = $TSconfig[$field]['wizards.'][$wizardIdentifier . '.'];
431 $selItems = FormEngineUtility
::addSelectOptionsToItemArray(FormEngineUtility
::initItemArray($fieldValue), $fieldValue, $TSconfig, $field);
432 // Process items by a user function:
433 if (!empty($wizardConfiguration['itemsProcFunc'])) {
434 $funcConfig = !empty($wizardConfiguration['itemsProcFunc.']) ?
$wizardConfiguration['itemsProcFunc.'] : array();
435 $dataPreprocessor = GeneralUtility
::makeInstance(DataPreprocessor
::class);
436 $selItems = $dataPreprocessor->procItems($selItems, $funcConfig, $wizardConfiguration, $table, $row, $field);
439 $options[] = '<option>' . $iTitle . '</option>';
440 foreach ($selItems as $p) {
441 $options[] = '<option value="' . htmlspecialchars($p[1]) . '">' . htmlspecialchars($p[0]) . '</option>';
443 if ($wizardConfiguration['mode'] == 'append') {
444 $assignValue = 'document.editform[\'' . $itemName . '\'].value=\'\'+this.options[this.selectedIndex].value+document.editform[\'' . $itemName . '\'].value';
445 } elseif ($wizardConfiguration['mode'] == 'prepend') {
446 $assignValue = 'document.editform[\'' . $itemName . '\'].value+=\'\'+this.options[this.selectedIndex].value';
448 $assignValue = 'document.editform[\'' . $itemName . '\'].value=this.options[this.selectedIndex].value';
452 ' id="' . str_replace('.', '', uniqid('tceforms-select-', TRUE)) . '"' .
453 ' class="form-control tceforms-select tceforms-wizardselect"' .
454 ' name="_WIZARD' . $fName . '"' .
455 ' onchange="' . htmlspecialchars($assignValue . ';this.blur();this.selectedIndex=0;' . implode('', $fieldChangeFunc)) . '"'.
457 implode('', $options) .
461 if (!empty($PA['fieldTSConfig']['suggest.']['default.']['hide'])) {
464 /** @var SuggestWizard $suggestWizard */
465 $suggestWizard = GeneralUtility
::makeInstance(SuggestWizard
::class);
466 $otherWizards[] = $suggestWizard->renderSuggestSelector($PA['itemFormElName'], $table, $field, $row, $PA);
470 // Hide the real form element?
471 if (is_array($wizardConfiguration['hideParent']) ||
$wizardConfiguration['hideParent']) {
472 // Setting the item to a hidden-field.
473 $item = $itemKinds[1];
474 if (is_array($wizardConfiguration['hideParent'])) {
475 // NoneElement does not access formEngine properties, use a dummy for decoupling
476 $formEngineDummy = new FormEngine
;
477 /** @var NoneElement $noneElement */
478 $noneElement = GeneralUtility
::makeInstance(NoneElement
::class, $formEngineDummy);
479 $elementConfiguration = array(
480 'fieldConf' => array(
481 'config' => $wizardConfiguration['hideParent'],
483 'itemFormElValue' => $PA['itemFormElValue'],
485 $item .= $noneElement->render('', '', '', $elementConfiguration);
490 // For each rendered wizard, put them together around the item.
491 if (!empty($buttonWizards) ||
!empty($otherWizards)) {
492 if ($wizConf['_HIDDENFIELD']) {
493 $item = $itemKinds[1];
497 if (!empty($buttonWizards)) {
498 $innerContent .= '<div class="btn-group' . ($wizConf['_VERTICAL'] ?
' btn-group-vertical' : '') . '">' . implode('', $buttonWizards) . '</div>';
500 $innerContent .= implode(' ', $otherWizards);
503 $classes = array('form-wizards-wrap');
504 if ($wizConf['_POSITION'] === 'left') {
505 $classes[] = 'form-wizards-aside';
506 $innerContent = '<div class="form-wizards-items">' . $innerContent . '</div><div class="form-wizards-element">' . $item . '</div>';
507 } elseif ($wizConf['_POSITION'] === 'top') {
508 $classes[] = 'form-wizards-top';
509 $innerContent = '<div class="form-wizards-items">' . $innerContent . '</div><div class="form-wizards-element">' . $item . '</div>';
510 } elseif ($wizConf['_POSITION'] === 'bottom') {
511 $classes[] = 'form-wizards-bottom';
512 $innerContent = '<div class="form-wizards-element">' . $item . '</div><div class="form-wizards-items">' . $innerContent . '</div>';
514 $classes[] = 'form-wizards-aside';
515 $innerContent = '<div class="form-wizards-element">' . $item . '</div><div class="form-wizards-items">' . $innerContent . '</div>';
518 <div class="' . implode(' ', $classes) . '">
519 ' . $innerContent . '
527 * Prints the selector box form-field for the db/file/select elements (multiple)
529 * @param string $fName Form element name
530 * @param string $mode Mode "db", "file" (internal_type for the "group" type) OR blank (then for the "select" type)
531 * @param string $allowed Commalist of "allowed
532 * @param array $itemArray The array of items. For "select" and "group"/"file" this is just a set of value. For "db" its an array of arrays with table/uid pairs.
533 * @param string $selector Alternative selector box.
534 * @param array $params An array of additional parameters, eg: "size", "info", "headers" (array with "selector" and "items"), "noBrowser", "thumbnails
535 * @param string $onFocus On focus attribute string
536 * @param string $table (optional) Table name processing for
537 * @param string $field (optional) Field of table name processing for
538 * @param string $uid (optional) uid of table record processing for
539 * @param array $config (optional) The TCA field config
540 * @return string The form fields for the selection.
541 * @throws \UnexpectedValueException
543 protected function dbFileIcons($fName, $mode, $allowed, $itemArray, $selector = '', $params = array(), $onFocus = '', $table = '', $field = '', $uid = '', $config = array()) {
544 $languageService = $this->getLanguageService();
546 if ($this->isGlobalReadonly() ||
$params['readOnly']) {
547 $disabled = ' disabled="disabled"';
553 // Creating <option> elements:
554 if (is_array($itemArray)) {
555 $itemArrayC = count($itemArray);
558 foreach ($itemArray as $pp) {
559 $pRec = BackendUtility
::getRecordWSOL($pp['table'], $pp['id']);
560 if (is_array($pRec)) {
561 $pTitle = BackendUtility
::getRecordTitle($pp['table'], $pRec, FALSE, TRUE);
562 $pUid = $pp['table'] . '_' . $pp['id'];
564 $title = htmlspecialchars($pTitle);
565 $opt[] = '<option value="' . htmlspecialchars($pUid) . '" title="' . $title . '">' . $title . '</option>';
569 case 'file_reference':
572 foreach ($itemArray as $item) {
573 $itemParts = explode('|', $item);
574 $uidList[] = ($pUid = ($pTitle = $itemParts[0]));
575 $title = htmlspecialchars(rawurldecode($itemParts[1]));
576 $opt[] = '<option value="' . htmlspecialchars(rawurldecode($itemParts[0])) . '" title="' . $title . '">' . $title . '</option>';
580 foreach ($itemArray as $pp) {
581 $pParts = explode('|', $pp);
582 $uidList[] = ($pUid = ($pTitle = $pParts[0]));
583 $title = htmlspecialchars(rawurldecode($pParts[0]));
584 $opt[] = '<option value="' . htmlspecialchars(rawurldecode($pParts[0])) . '" title="' . $title . '">' . $title . '</option>';
588 foreach ($itemArray as $pp) {
589 $pParts = explode('|', $pp, 2);
590 $uidList[] = ($pUid = $pParts[0]);
591 $pTitle = $pParts[1];
592 $title = htmlspecialchars(rawurldecode($pTitle));
593 $opt[] = '<option value="' . htmlspecialchars(rawurldecode($pUid)) . '" title="' . $title . '">' . $title . '</option>';
597 // Create selector box of the options
598 $sSize = $params['autoSizeMax']
599 ? MathUtility
::forceIntegerInRange($itemArrayC +
1, MathUtility
::forceIntegerInRange($params['size'], 1), $params['autoSizeMax'])
602 $isMultiple = $params['maxitems'] != 1 && $params['size'] != 1;
603 $selector = '<select id="' . str_replace('.', '', uniqid('tceforms-multiselect-', TRUE)) . '" '
604 . ($params['noList'] ?
'style="display: none"' : 'size="' . $sSize . '" class="form-control tceforms-multiselect"')
605 . ($isMultiple ?
' multiple="multiple"' : '')
606 . ' name="' . $fName . '_list" ' . $onFocus . $params['style'] . $disabled . '>' . implode('', $opt)
613 $rOnClickInline = '';
614 if (!$params['readOnly'] && !$params['noList']) {
615 if (!$params['noBrowser']) {
616 // Check against inline uniqueness
617 /** @var InlineElement $inline */
618 $inline = $this->globalOptions
['inline'];
619 $inlineParent = $inline->getStructureLevel(-1);
620 $aOnClickInline = '';
621 if (is_array($inlineParent) && $inlineParent['uid']) {
622 if ($inlineParent['config']['foreign_table'] == $table && $inlineParent['config']['foreign_unique'] == $field) {
623 $objectPrefix = $inline->inlineNames
['object'] . InlineElement
::Structure_Separator
. $table;
624 $aOnClickInline = $objectPrefix . '|inline.checkUniqueElement|inline.setUniqueElement';
625 $rOnClickInline = 'inline.revertUnique(\'' . $objectPrefix . '\',null,\'' . $uid . '\');';
628 if (is_array($config['appearance']) && isset($config['appearance']['elementBrowserType'])) {
629 $elementBrowserType = $config['appearance']['elementBrowserType'];
631 $elementBrowserType = $mode;
633 if (is_array($config['appearance']) && isset($config['appearance']['elementBrowserAllowed'])) {
634 $elementBrowserAllowed = $config['appearance']['elementBrowserAllowed'];
636 $elementBrowserAllowed = $allowed;
638 $aOnClick = 'setFormValueOpenBrowser(\'' . $elementBrowserType . '\',\''
639 . ($fName . '|||' . $elementBrowserAllowed . '|' . $aOnClickInline) . '\'); return false;';
642 onclick="' . htmlspecialchars($aOnClick) . '"
643 class="btn btn-default"
644 title="' . htmlspecialchars($languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.browse_' . ($mode == 'db' ?
'db' : 'file'))) . '">
645 ' . IconUtility
::getSpriteIcon('actions-insert-record') . '
648 if (!$params['dontShowMoveIcons']) {
652 class="btn btn-default t3-btn-moveoption-top"
653 data-fieldname="' . $fName . '"
654 title="' . htmlspecialchars($languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.move_to_top')) . '">
655 ' . IconUtility
::getSpriteIcon('actions-move-to-top') . '
661 class="btn btn-default t3-btn-moveoption-up"
662 data-fieldname="' . $fName . '"
663 title="' . htmlspecialchars($languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.move_up')) . '">
664 ' . IconUtility
::getSpriteIcon('actions-move-up') . '
668 class="btn btn-default t3-btn-moveoption-down"
669 data-fieldname="' . $fName . '"
670 title="' . htmlspecialchars($languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.move_down')) . '">
671 ' . IconUtility
::getSpriteIcon('actions-move-down') . '
676 class="btn btn-default t3-btn-moveoption-bottom"
677 data-fieldname="' . $fName . '"
678 title="' . htmlspecialchars($languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.move_to_bottom')) . '">
679 ' . IconUtility
::getSpriteIcon('actions-move-to-bottom') . '
683 $clipElements = $this->getClipboardElements($allowed, $mode);
684 if (count($clipElements)) {
686 foreach ($clipElements as $elValue) {
688 list($itemTable, $itemUid) = explode('|', $elValue);
689 $recordTitle = BackendUtility
::getRecordTitle($itemTable, BackendUtility
::getRecordWSOL($itemTable, $itemUid));
690 $itemTitle = GeneralUtility
::quoteJSvalue($recordTitle);
691 $elValue = $itemTable . '_' . $itemUid;
693 // 'file', 'file_reference' and 'folder' mode
694 $itemTitle = 'unescape(\'' . rawurlencode(basename($elValue)) . '\')';
696 $aOnClick .= 'setFormValueFromBrowseWin(\'' . $fName . '\',unescape(\''
697 . rawurlencode(str_replace('%20', ' ', $elValue)) . '\'),' . $itemTitle . ',' . $itemTitle . ');';
699 $aOnClick .= 'return false;';
702 onclick="' . htmlspecialchars($aOnClick) . '"
703 title="' . htmlspecialchars(sprintf($languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.clipInsert_' . ($mode == 'db' ?
'db' : 'file')), count($clipElements))) . '">
704 ' . IconUtility
::getSpriteIcon('actions-document-paste-into') . '
708 if (!$params['readOnly'] && !$params['noDelete']) {
711 class="btn btn-default t3-btn-removeoption"
712 onClick="' . $rOnClickInline . '"
713 data-fieldname="' . $fName . '"
714 title="' . htmlspecialchars($languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.remove_selected')) . '">
715 ' . IconUtility
::getSpriteIcon('actions-selection-delete') . '
721 if ($params['thumbnails'] && $params['allowed']) {
722 // In case we have thumbnails, check if only images are allowed.
723 // In this case, render them below the field, instead of to the right
724 $allowedExtensionList = $params['allowed'];
725 $imageExtensionList = GeneralUtility
::trimExplode(',', strtolower($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']), TRUE);
727 foreach ($allowedExtensionList as $allowedExtension) {
728 if (!ArrayUtility
::inArray($imageExtensionList, $allowedExtension)) {
735 if (is_array($params['thumbnails']) && !empty($params['thumbnails'])) {
737 $thumbnails .= '<ul class="list-inline">';
738 foreach ($params['thumbnails'] as $thumbnail) {
739 $thumbnails .= '<li><span class="thumbnail">' . $thumbnail['image'] . '</span></li>';
741 $thumbnails .= '</ul>';
743 $thumbnails .= '<div class="table-fit"><table class="table table-white"><tbody>';
744 foreach ($params['thumbnails'] as $thumbnail) {
747 <td class="col-icon">
748 ' . ($config['internal_type'] === 'db'
749 ?
$this->getControllerDocumentTemplate()->wrapClickMenuOnIcon($thumbnail['image'], $thumbnail['table'], $thumbnail['uid'], 1, '', '+copy,info,edit,view')
750 : $thumbnail['image']) . '
752 <td class="col-title">
753 ' . ($config['internal_type'] === 'db'
754 ?
$this->getControllerDocumentTemplate()->wrapClickMenuOnIcon($thumbnail['name'], $thumbnail['table'], $thumbnail['uid'], 1, '', '+copy,info,edit,view')
755 : $thumbnail['name']) . '
756 ' . ($config['internal_type'] === 'db' ?
'' : ' <span class="text-muted">[' . $thumbnail['uid'] . ']</span>') . '
761 $thumbnails .= '</tbody></table></div>';
767 if (is_array($params['allowedTables']) && !empty($params['allowedTables'])) {
768 $allowedTables .= '<div class="help-block">';
769 foreach ($params['allowedTables'] as $key => $item) {
770 if (is_array($item)) {
771 $allowedTables .= '<a href="#" onClick="' . htmlspecialchars($item['onClick']) . '" class="btn btn-default">' . $item['icon'] . ' ' . htmlspecialchars($item['name']) . '</a> ';
772 } elseif($key === 'name') {
773 $allowedTables .= '<span>' . htmlspecialchars($item) . '</span> ';
776 $allowedTables .= '</div>';
780 if (is_array($params['allowed']) && !empty($params['allowed'])) {
781 foreach ($params['allowed'] as $item) {
782 $allowedList .= '<span class="label label-success">' . strtoupper($item) . '</span> ';
786 $disallowedList = '';
787 if (is_array($params['disallowed']) && !empty($params['disallowed'])) {
788 foreach ($params['disallowed'] as $item) {
789 $disallowedList .= '<span class="label label-danger">' . strtoupper($item) . '</span> ';
793 $rightbox = ($params['rightbox'] ?
: '');
795 // Hook: dbFileIcons_postProcess (requested by FAL-team for use with the "fal" extension)
796 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tceforms.php']['dbFileIcons'])) {
797 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tceforms.php']['dbFileIcons'] as $classRef) {
798 $hookObject = GeneralUtility
::getUserObj($classRef);
799 if (!$hookObject instanceof DatabaseFileIconsHookInterface
) {
800 throw new \
UnexpectedValueException('$hookObject must implement interface ' . DatabaseFileIconsHookInterface
::class, 1290167704);
802 $additionalParams = array(
804 'allowed' => $allowed,
805 'itemArray' => $itemArray,
806 'onFocus' => $onFocus,
810 'config' => $GLOBALS['TCA'][$table]['columns'][$field]
812 $hookObject->dbFileIcons_postProcess($params, $selector, $thumbnails, $icons, $rightbox, $fName, $uidList, $additionalParams, $this);
818 ' . ($params['headers']['selector'] ?
'<label>' . $params['headers']['selector'] . '</label>' : '') . '
819 <div class="form-wizards-wrap form-wizards-aside">
820 <div class="form-wizards-element">
822 ' . (!$params['noList'] && !empty($allowedTables) ?
$allowedTables : '') . '
823 ' . (!$params['noList'] && (!empty($allowedList) ||
!empty($disallowedList))
824 ?
'<div class="help-block">' . $allowedList . $disallowedList . ' </div>'
827 ' . (!empty($icons['L']) ?
'<div class="form-wizards-items"><div class="btn-group-vertical">' . implode('', $icons['L']) . '</div></div>' : '' ) . '
828 ' . (!empty($icons['R']) ?
'<div class="form-wizards-items"><div class="btn-group-vertical">' . implode('', $icons['R']) . '</div></div>' : '' ) . '
833 <div class="form-multigroup-wrap t3js-formengine-field-group">
834 <div class="form-multigroup-item form-multigroup-element">' . $str . '</div>
835 <div class="form-multigroup-item form-multigroup-element">
836 ' . ($params['headers']['items'] ?
'<label>' . $params['headers']['items'] . '</label>' : '') . '
837 ' . ($params['headers']['selectorbox'] ?
'<div class="form-multigroup-item-wizard">' . $params['headers']['selectorbox'] . '</div>' : '') . '
845 // Creating the hidden field which contains the actual value as a comma list.
846 $str .= '<input type="hidden" name="' . $fName . '" value="' . htmlspecialchars(implode(',', $uidList)) . '" />';
851 * Returns array of elements from clipboard to insert into GROUP element box.
853 * @param string $allowed Allowed elements, Eg "pages,tt_content", "gif,jpg,jpeg,png
854 * @param string $mode Mode of relations: "db" or "file
855 * @return array Array of elements in values (keys are insignificant), if none found, empty array.
857 protected function getClipboardElements($allowed, $mode) {
858 if (!is_object($this->clipboard
)) {
859 $this->clipboard
= GeneralUtility
::makeInstance(Clipboard
::class);
860 $this->clipboard
->initializeClipboard();
865 case 'file_reference':
868 $elFromTable = $this->clipboard
->elFromTable('_FILE');
869 $allowedExts = GeneralUtility
::trimExplode(',', $allowed, TRUE);
870 // If there are a set of allowed extensions, filter the content:
872 foreach ($elFromTable as $elValue) {
873 $pI = pathinfo($elValue);
874 $ext = strtolower($pI['extension']);
875 if (in_array($ext, $allowedExts)) {
876 $output[] = $elValue;
880 // If all is allowed, insert all: (This does NOT respect any disallowed extensions,
881 // but those will be filtered away by the backend TCEmain)
882 $output = $elFromTable;
886 $allowedTables = GeneralUtility
::trimExplode(',', $allowed, TRUE);
887 // All tables allowed for relation:
888 if (trim($allowedTables[0]) === '*') {
889 $output = $this->clipboard
->elFromTable('');
891 // Only some tables, filter them:
892 foreach ($allowedTables as $tablename) {
893 $elFromTable = $this->clipboard
->elFromTable($tablename);
894 $output = array_merge($output, $elFromTable);
897 $output = array_keys($output);
905 * @return LanguageService
907 protected function getLanguageService() {
908 return $GLOBALS['LANG'];
912 * @return BackendUserAuthentication
914 protected function getBackendUserAuthentication() {
915 return $GLOBALS['BE_USER'];
919 * @return DatabaseConnection
921 protected function getDatabaseConnection() {
922 return $GLOBALS['TYPO3_DB'];
926 * @return DocumentTemplate
928 protected function getDocumentTemplate() {
929 return $GLOBALS['TBE_TEMPLATE'];
933 * @return DocumentTemplate
935 protected function getControllerDocumentTemplate() {
936 // $GLOBALS['SOBE'] might be any kind of PHP class (controller most of the times)
937 // These classes do not inherit from any common class, but they all seem to have a "doc" member
938 return $GLOBALS['SOBE']->doc
;