Revert "[TASK] Rebuild the calcAge functionality"
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / FormEngine.php
1 <?php
2 namespace TYPO3\CMS\Backend\Form;
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
17 use TYPO3\CMS\Backend\Form\Element\InlineElement;
18 use TYPO3\CMS\Backend\Utility\BackendUtility;
19 use TYPO3\CMS\Backend\Utility\IconUtility;
20 use TYPO3\CMS\Core\Html\HtmlParser;
21 use TYPO3\CMS\Core\Html\RteHtmlParser;
22 use TYPO3\CMS\Core\Messaging\FlashMessage;
23 use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
24 use TYPO3\CMS\Core\Messaging\FlashMessageService;
25 use TYPO3\CMS\Core\Resource\ProcessedFile;
26 use TYPO3\CMS\Core\Resource\ResourceFactory;
27 use TYPO3\CMS\Core\Utility\ArrayUtility;
28 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
29 use TYPO3\CMS\Core\Utility\GeneralUtility;
30 use TYPO3\CMS\Core\Utility\MathUtility;
31 use TYPO3\CMS\Core\Utility\PathUtility;
32
33 /**
34 * 'TCEforms' - Class for creating the backend editing forms.
35 *
36 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
37 * @coauthor René Fritz <r.fritz@colorcube.de>
38 */
39 class FormEngine {
40
41 // variables not commented yet.... (do so...)
42 /**
43 * @todo Define visibility
44 */
45 public $palFieldArr = array();
46
47 /**
48 * @todo Define visibility
49 */
50 public $disableWizards = 0;
51
52 /**
53 * @todo Define visibility
54 */
55 public $isPalettedoc = 0;
56
57 /**
58 * @todo Define visibility
59 */
60 public $paletteMargin = 1;
61
62 /**
63 * @todo Define visibility
64 */
65 public $defStyle = '';
66
67 /**
68 * @todo Define visibility
69 */
70 public $cachedTSconfig = array();
71
72 /**
73 * @todo Define visibility
74 */
75 public $cachedTSconfig_fieldLevel = array();
76
77 /**
78 * @todo Define visibility
79 */
80 public $cachedLanguageFlag = array();
81
82 /**
83 * @todo Define visibility
84 */
85 public $cachedAdditionalPreviewLanguages = NULL;
86
87 /**
88 * Cache for the real PID of a record. The array key consists for a combinded string "<table>:<uid>:<pid>".
89 * The value is an array with two values: first is the real PID of a record, second is the PID value for TSconfig.
90 *
91 * @var array
92 */
93 protected $cache_getTSCpid;
94
95 /**
96 * @todo Define visibility
97 */
98 public $transformedRow = array();
99
100 /**
101 * @todo Define visibility
102 */
103 public $extJSCODE = '';
104
105 /**
106 * @todo Define visibility
107 */
108 public $printNeededJS = array();
109
110 /**
111 * @todo Define visibility
112 */
113 public $hiddenFieldAccum = array();
114
115 /**
116 * @todo Define visibility
117 */
118 public $TBE_EDITOR_fieldChanged_func = '';
119
120 /**
121 * @todo Define visibility
122 */
123 public $loadMD5_JS = 1;
124
125 // Something unique...
126 /**
127 * @todo Define visibility
128 */
129 public $prevBorderStyle = '[nothing here...]';
130
131 // If set direct upload fields will be shown
132 /**
133 * @todo Define visibility
134 */
135 public $allowUpload = 0;
136
137 // Array where records in the default language is stored. (processed by transferdata)
138 /**
139 * @todo Define visibility
140 */
141 public $defaultLanguageData = array();
142
143 // Array where records in the default language is stored (raw without any processing. used for making diff)
144 /**
145 * @todo Define visibility
146 */
147 public $defaultLanguageData_diff = array();
148
149 /**
150 * @todo Define visibility
151 */
152 public $additionalPreviewLanguageData = array();
153
154 // EXTERNAL, static
155 // Set this to the 'backPath' pointing back to the typo3 admin directory from the script where this form is displayed.
156 /**
157 * @todo Define visibility
158 */
159 public $backPath = '';
160
161 // Alternative return URL path (default is \TYPO3\CMS\Core\Utility\GeneralUtility::linkThisScript())
162 /**
163 * @todo Define visibility
164 */
165 public $returnUrl = '';
166
167 // Can be set to point to a field name in the form which will be set to '1' when the form is submitted with a *save* button. This way the recipient script can determine that the form was submitted for save and not "close" for example.
168 /**
169 * @todo Define visibility
170 */
171 public $doSaveFieldName = '';
172
173 // Can be set TRUE/FALSE to whether palettes (secondary options) are in the topframe or in form. TRUE means they are NOT IN-form. So a collapsed palette is one, which is shown in the top frame, not in the page.
174 /**
175 * @todo Define visibility
176 */
177 public $palettesCollapsed = 0;
178
179 // If set, the RTE is disabled (from form display, eg. by checkbox in the bottom of the page!)
180 /**
181 * @todo Define visibility
182 */
183 public $disableRTE = 0;
184
185 // If FALSE, then all CSH will be disabled, regardless of settings in $this->edit_showFieldHelp
186 /**
187 * @todo Define visibility
188 */
189 public $globalShowHelp = 1;
190
191 // If TRUE, the forms are rendering only localization relevant fields of the records.
192 /**
193 * @todo Define visibility
194 */
195 public $localizationMode = '';
196
197 // Overrule the field order set in TCA[types][showitem], eg for tt_content this value, 'bodytext,image', would make first the 'bodytext' field, then the 'image' field (if set for display)... and then the rest in the old order.
198 /**
199 * @todo Define visibility
200 */
201 public $fieldOrder = '';
202
203 // If set to FALSE, palettes will NEVER be rendered.
204 /**
205 * @todo Define visibility
206 */
207 public $doPrintPalette = 1;
208
209 /**
210 * Set to initialized clipboard object; Then the element browser will offer a link to paste in records from clipboard.
211 *
212 * @var \TYPO3\CMS\Backend\Clipboard\Clipboard
213 * @todo Define visibility
214 */
215 public $clipObj = FALSE;
216
217 // Enable click menu on reference icons.
218 /**
219 * @todo Define visibility
220 */
221 public $enableClickMenu = FALSE;
222
223 // Enable Tab Menus.
224 /**
225 * @todo Define visibility
226 */
227 public $enableTabMenu = FALSE;
228
229 // When enabled all fields are rendered non-editable.
230 /**
231 * @todo Define visibility
232 */
233 public $renderReadonly = FALSE;
234
235 // Form field width compensation: Factor of "size=12" to "style="width: 12*9.58px" for form field widths of style-aware browsers
236 /**
237 * @todo Define visibility
238 */
239 public $form_rowsToStylewidth = 9.58;
240
241 /**
242 * Value that gets added for style="width: ...px" for textareas compared to input fields.
243 *
244 * @var integer
245 */
246 protected $form_additionalTextareaStyleWidth = 23;
247
248 // Form field width compensation: Compensation for large documents, doc-tab (editing)
249 /**
250 * @todo Define visibility
251 */
252 public $form_largeComp = 1.33;
253
254 // The number of chars expected per row when the height of a text area field is automatically calculated based on the number of characters found in the field content.
255 /**
256 * @todo Define visibility
257 */
258 public $charsPerRow = 40;
259
260 // The maximum abstract value for textareas
261 /**
262 * @todo Define visibility
263 */
264 public $maxTextareaWidth = 48;
265
266 // The maximum abstract value for input fields
267 /**
268 * @todo Define visibility
269 */
270 public $maxInputWidth = 48;
271
272 // Default style for the selector boxes used for multiple items in "select" and "group" types.
273 /**
274 * @todo Define visibility
275 */
276 public $defaultMultipleSelectorStyle = 'width:310px;';
277
278 // INTERNAL, static
279 // The string to prepend formfield names with.
280 /**
281 * @todo Define visibility
282 */
283 public $prependFormFieldNames = 'data';
284
285 // The string to prepend commands for tcemain::process_cmdmap with.
286 /**
287 * @todo Define visibility
288 */
289 public $prependCmdFieldNames = 'cmd';
290
291 // The string to prepend FILE form field names with.
292 /**
293 * @todo Define visibility
294 */
295 public $prependFormFieldNames_file = 'data_files';
296
297 /**
298 * The string to prepend form field names that are active (not NULL).
299 *
300 * @var string
301 */
302 protected $prependFormFieldNamesActive = 'control[active]';
303
304 // The name attribute of the form.
305 /**
306 * @todo Define visibility
307 */
308 public $formName = 'editform';
309
310 // Whitelist that allows TCA field configuration to be overridden by TSconfig, @see overrideFieldConf()
311 /**
312 * @todo Define visibility
313 */
314 public $allowOverrideMatrix = array();
315
316 // INTERNAL, dynamic
317 // Set by readPerms() (caching)
318 /**
319 * @todo Define visibility
320 */
321 public $perms_clause = '';
322
323 // Set by readPerms() (caching-flag)
324 /**
325 * @todo Define visibility
326 */
327 public $perms_clause_set = 0;
328
329 // Used to indicate the mode of CSH (Context Sensitive Help), whether it should be icons-only ('icon') or not at all (blank).
330 /**
331 * @todo Define visibility
332 */
333 public $edit_showFieldHelp = '';
334
335 /**
336 * @var bool
337 */
338 public $edit_docModuleUpload = FALSE;
339
340 // Loaded with info about the browser when class is instantiated.
341 /**
342 * @todo Define visibility
343 */
344 public $clientInfo = array();
345
346 // TRUE, if RTE is possible for the current user (based on result from BE_USER->isRTE())
347 /**
348 * @todo Define visibility
349 */
350 public $RTEenabled = 0;
351
352 // If $this->RTEenabled was FALSE, you can find the reasons listed in this array which is filled with reasons why the RTE could not be loaded)
353 /**
354 * @todo Define visibility
355 */
356 public $RTEenabled_notReasons = '';
357
358 // Counter that is incremented before an RTE is created. Can be used for unique ids etc.
359 /**
360 * @todo Define visibility
361 */
362 public $RTEcounter = 0;
363
364 // Contains current color scheme
365 /**
366 * @todo Define visibility
367 */
368 public $colorScheme;
369
370 // Contains current class scheme
371 /**
372 * @todo Define visibility
373 */
374 public $classScheme;
375
376 // Contains the default color scheme
377 /**
378 * @todo Define visibility
379 */
380 public $defColorScheme;
381
382 // Contains the default class scheme
383 /**
384 * @todo Define visibility
385 */
386 public $defClassScheme;
387
388 // Contains field style values
389 /**
390 * @todo Define visibility
391 */
392 public $fieldStyle;
393
394 // Contains border style values.
395 /**
396 * @todo Define visibility
397 */
398 public $borderStyle;
399
400 // An accumulation of messages from the class.
401 /**
402 * @todo Define visibility
403 */
404 public $commentMessages = array();
405
406 // INTERNAL, templates
407 // Total wrapping for the table rows.
408 /**
409 * @todo Define visibility
410 */
411 public $totalWrap = '<hr />|<hr />';
412
413 // Field template
414 /**
415 * @todo Define visibility
416 */
417 public $fieldTemplate = '<strong>###FIELD_NAME###</strong><br />###FIELD_ITEM###<hr />';
418
419 /**
420 * Template subpart for palette fields.
421 *
422 * @var string
423 */
424 protected $paletteFieldTemplate;
425
426 // Wrapping template code for a section
427 /**
428 * @todo Define visibility
429 */
430 public $sectionWrap = '';
431
432 // Template for palette headers
433 /**
434 * @todo Define visibility
435 */
436 public $palFieldTemplateHeader = '';
437
438 // Template for palettes
439 /**
440 * @todo Define visibility
441 */
442 public $palFieldTemplate = '';
443
444 // INTERNAL, working memory
445 // Set to the fields NOT to display, if any.
446 /**
447 * @todo Define visibility
448 */
449 public $excludeElements = '';
450
451 // During rendering of forms this will keep track of which palettes has already been rendered (so they are not rendered twice by mistake)
452 /**
453 * @todo Define visibility
454 */
455 public $palettesRendered = array();
456
457 // This array of fields will be set as hidden-fields instead of rendered normally! For instance palette fields edited in the top frame are set as hidden fields since the main form has to submit the values. The top frame actually just sets the value in the main form!
458 /**
459 * @todo Define visibility
460 */
461 public $hiddenFieldListArr = array();
462
463 // Used to register input-field names, which are required. (Done during rendering of the fields). This information is then used later when the JavaScript is made.
464 /**
465 * @todo Define visibility
466 */
467 public $requiredFields = array();
468
469 // Used to register input-field names, which are required an have additional requirements (e.g. like a date/time must be positive integer). The information of this array is merged with $this->requiredFields later.
470 /**
471 * @todo Define visibility
472 */
473 public $requiredAdditional = array();
474
475 // Used to register the min and max number of elements for selectorboxes where that apply (in the "group" type for instance)
476 /**
477 * @todo Define visibility
478 */
479 public $requiredElements = array();
480
481 // Used to determine where $requiredFields or $requiredElements are nested (in Tabs or IRRE)
482 /**
483 * @todo Define visibility
484 */
485 public $requiredNested = array();
486
487 // Keeps track of the rendering depth of nested records.
488 /**
489 * @todo Define visibility
490 */
491 public $renderDepth = 0;
492
493 // Color scheme buffer.
494 /**
495 * @todo Define visibility
496 */
497 public $savedSchemes = array();
498
499 // holds the path an element is nested in (e.g. required for RTEhtmlarea)
500 /**
501 * @todo Define visibility
502 */
503 public $dynNestedStack = array();
504
505 // Internal, registers for user defined functions etc.
506 // Additional HTML code, printed before the form.
507 /**
508 * @todo Define visibility
509 */
510 public $additionalCode_pre = array();
511
512 // Additional JavaScript, printed before the form
513 /**
514 * @todo Define visibility
515 */
516 public $additionalJS_pre = array();
517
518 // Additional JavaScript printed after the form
519 /**
520 * @todo Define visibility
521 */
522 public $additionalJS_post = array();
523
524 // Additional JavaScript executed on submit; If you set "OK" variable it will raise an error about RTEs not being loaded and offer to block further submission.
525 /**
526 * @todo Define visibility
527 */
528 public $additionalJS_submit = array();
529
530 // Additional JavaScript executed when section element is deleted. This is necessary, for example, to correctly clean up HTMLArea RTE (bug #8232)
531 /**
532 * @todo Define visibility
533 */
534 public $additionalJS_delete = array();
535
536 /**
537 * @var \TYPO3\CMS\Backend\Form\Element\InlineElement
538 * @todo Define visibility
539 */
540 public $inline;
541
542 // Array containing hook class instances called once for a form
543 /**
544 * @todo Define visibility
545 */
546 public $hookObjectsMainFields = array();
547
548 // Array containing hook class instances called for each field
549 /**
550 * @todo Define visibility
551 */
552 public $hookObjectsSingleField = array();
553
554 // Rows gettings inserted into the alt_doc headers (when called from alt_doc.php)
555 /**
556 * @todo Define visibility
557 */
558 public $extraFormHeaders = array();
559
560 public $templateFile = '';
561
562 /**
563 * @var integer
564 */
565 protected $multiSelectFilterCount = 0;
566
567 /**
568 * @var \TYPO3\CMS\Backend\Form\Element\SuggestElement
569 */
570 protected $suggest;
571
572 // Form templates, relative to typo3 directory
573 /**
574 * Constructor function, setting internal variables, loading the styles used.
575 *
576 * @todo Define visibility
577 */
578 public function __construct() {
579 $this->clientInfo = GeneralUtility::clientInfo();
580 $this->RTEenabled = $GLOBALS['BE_USER']->isRTE();
581 if (!$this->RTEenabled) {
582 $this->RTEenabled_notReasons = implode(LF, $GLOBALS['BE_USER']->RTE_errors);
583 $this->commentMessages[] = 'RTE NOT ENABLED IN SYSTEM due to:' . LF . $this->RTEenabled_notReasons;
584 }
585 // Default color+class scheme
586 $this->defColorScheme = array(
587 $GLOBALS['SOBE']->doc->bgColor,
588 // Background for the field AND palette
589 GeneralUtility::modifyHTMLColorAll($GLOBALS['SOBE']->doc->bgColor, -20),
590 // Background for the field header
591 GeneralUtility::modifyHTMLColorAll($GLOBALS['SOBE']->doc->bgColor, -10),
592 // Background for the palette field header
593 'black',
594 // Field header font color
595 '#666666'
596 );
597 $this->defColorScheme = array();
598 // Override / Setting defaults from TBE_STYLES array
599 $this->resetSchemes();
600 // Setting the current colorScheme to default.
601 $this->defColorScheme = $this->colorScheme;
602 $this->defClassScheme = $this->classScheme;
603 // Define whitelist that allows TCA field configuration to be overridden by TSconfig, @see overrideFieldConf():
604 $this->allowOverrideMatrix = array(
605 'input' => array('size', 'max', 'readOnly'),
606 'text' => array('cols', 'rows', 'wrap', 'readOnly'),
607 'check' => array('cols', 'showIfRTE', 'readOnly'),
608 'select' => array('size', 'autoSizeMax', 'maxitems', 'minitems', 'readOnly', 'treeConfig'),
609 'group' => array('size', 'autoSizeMax', 'max_size', 'show_thumbs', 'maxitems', 'minitems', 'disable_controls', 'readOnly'),
610 'inline' => array('appearance', 'behaviour', 'foreign_label', 'foreign_selector', 'foreign_unique', 'maxitems', 'minitems', 'size', 'autoSizeMax', 'symmetric_label', 'readOnly')
611 );
612 // Create instance of InlineElement only if this a non-IRRE-AJAX call:
613 if (!isset($GLOBALS['ajaxID']) || strpos($GLOBALS['ajaxID'], 'TYPO3\\CMS\\Backend\\Form\\Element\\InlineElement::') !== 0) {
614 $this->inline = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Form\\Element\\InlineElement');
615 }
616 // Create instance of \TYPO3\CMS\Backend\Form\Element\SuggestElement only if this a non-Suggest-AJAX call:
617 if (!isset($GLOBALS['ajaxID']) || strpos($GLOBALS['ajaxID'], 'TYPO3\\CMS\\Backend\\Form\\Element\\SuggestElement::') !== 0) {
618 $this->suggest = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Form\\Element\\SuggestElement');
619 }
620 // Prepare user defined objects (if any) for hooks which extend this function:
621 $this->hookObjectsMainFields = array();
622 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tceforms.php']['getMainFieldsClass'])) {
623 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tceforms.php']['getMainFieldsClass'] as $classRef) {
624 $this->hookObjectsMainFields[] = GeneralUtility::getUserObj($classRef);
625 }
626 }
627 $this->hookObjectsSingleField = array();
628 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tceforms.php']['getSingleFieldClass'])) {
629 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tceforms.php']['getSingleFieldClass'] as $classRef) {
630 $this->hookObjectsSingleField[] = GeneralUtility::getUserObj($classRef);
631 }
632 }
633 $this->templateFile = 'sysext/backend/Resources/Private/Templates/FormEngine.html';
634 }
635
636 /**
637 * Initialize various internal variables.
638 *
639 * @return void
640 * @todo Define visibility
641 */
642 public function initDefaultBEmode() {
643 $this->prependFormFieldNames = 'data';
644 $this->formName = 'editform';
645 $this->setNewBEDesign();
646 $this->edit_showFieldHelp = $GLOBALS['BE_USER']->uc['edit_showFieldHelp'];
647 $this->edit_docModuleUpload = (bool)$GLOBALS['BE_USER']->uc['edit_docModuleUpload'];
648 $this->inline->init($this);
649 $this->suggest->init($this);
650 }
651
652 /*******************************************************
653 *
654 * Rendering the forms, fields etc
655 *
656 *******************************************************/
657 /**
658 * Will return the TCEform element for just a single field from a record.
659 * The field must be listed in the currently displayed fields (as found in [types][showitem]) for the record.
660 * This also means that the $table/$row supplied must be complete so the list of fields to show can be found correctly
661 *
662 * @param string $table The table name
663 * @param array $row The record from the table for which to render a field.
664 * @param string $theFieldToReturn The field name to return the TCEform element for.
665 * @return string HTML output
666 * @see getMainFields()
667 * @todo Define visibility
668 */
669 public function getSoloField($table, $row, $theFieldToReturn) {
670 if ($GLOBALS['TCA'][$table]) {
671 $typeNum = $this->getRTypeNum($table, $row);
672 if ($GLOBALS['TCA'][$table]['types'][$typeNum]) {
673 $itemList = $GLOBALS['TCA'][$table]['types'][$typeNum]['showitem'];
674 if ($itemList) {
675 $fields = GeneralUtility::trimExplode(',', $itemList, TRUE);
676 $excludeElements = ($this->excludeElements = $this->getExcludeElements($table, $row, $typeNum));
677 foreach ($fields as $fieldInfo) {
678 $parts = explode(';', $fieldInfo);
679 $theField = trim($parts[0]);
680 if (!in_array($theField, $excludeElements) && (string)$theField === (string)$theFieldToReturn) {
681 if ($GLOBALS['TCA'][$table]['columns'][$theField]) {
682 $sField = $this->getSingleField($table, $theField, $row, $parts[1], 1, $parts[3], $parts[2]);
683 return $sField['ITEM'];
684 }
685 }
686 }
687 }
688 }
689 }
690 }
691
692 /**
693 * Based on the $table and $row of content, this displays the complete TCEform for the record.
694 * The input-$row is required to be preprocessed if necessary by eg.
695 * the \TYPO3\CMS\Backend\Form\DataPreprocessor class. For instance the RTE content
696 * should be transformed through this class first.
697 *
698 * @param string $table The table name
699 * @param array $row The record from the table for which to render a field.
700 * @param integer $depth Depth level
701 * @param array $overruleTypesArray Overrule types array. Can be used to override the showitem etc. configuration for the TCA types of the table. Can contain all settings which are possible in the TCA 'types' section. See e.g. $TCA['tt_content']['types'].
702 * @return string HTML output
703 * @see getSoloField()
704 * @todo Define visibility
705 */
706 public function getMainFields($table, array $row, $depth = 0, array $overruleTypesArray = array()) {
707 $this->renderDepth = $depth;
708 // Init vars:
709 $out_array = array(array());
710 $out_array_meta = array(
711 array(
712 'title' => $this->getLL('l_generalTab')
713 )
714 );
715 $out_pointer = 0;
716 $out_sheet = 0;
717 $this->palettesRendered = array();
718 $this->palettesRendered[$this->renderDepth][$table] = array();
719 // Hook: getMainFields_preProcess (requested by Thomas Hempel for use with the "dynaflex" extension)
720 foreach ($this->hookObjectsMainFields as $hookObj) {
721 if (method_exists($hookObj, 'getMainFields_preProcess')) {
722 $hookObj->getMainFields_preProcess($table, $row, $this);
723 }
724 }
725 if ($GLOBALS['TCA'][$table]) {
726 // Get dividers2tabs setting from TCA of the current table:
727 $dividers2tabs = &$GLOBALS['TCA'][$table]['ctrl']['dividers2tabs'];
728 // Load the description content for the table.
729 if ($this->edit_showFieldHelp || $this->doLoadTableDescr($table)) {
730 $GLOBALS['LANG']->loadSingleTableDescription($table);
731 }
732 // Get the current "type" value for the record.
733 $typeNum = $this->getRTypeNum($table, $row);
734 // Find the list of fields to display:
735 if ($GLOBALS['TCA'][$table]['types'][$typeNum]) {
736 $itemList = $GLOBALS['TCA'][$table]['types'][$typeNum]['showitem'];
737 if (is_array($overruleTypesArray) && isset($overruleTypesArray[$typeNum]['showitem'])) {
738 $itemList = $overruleTypesArray[$typeNum]['showitem'];
739 }
740 // If such a list existed...
741 if ($itemList) {
742 // Explode the field list and possibly rearrange the order of the fields, if configured for
743 $fields = GeneralUtility::trimExplode(',', $itemList, TRUE);
744 if ($this->fieldOrder) {
745 $fields = $this->rearrange($fields);
746 }
747 // Get excluded fields, added fiels and put it together:
748 $excludeElements = ($this->excludeElements = $this->getExcludeElements($table, $row, $typeNum));
749 $fields = $this->mergeFieldsWithAddedFields($fields, $this->getFieldsToAdd($table, $row, $typeNum), $table);
750 // If TCEforms will render a tab menu in the next step, push the name to the tab stack:
751 $tabIdentString = '';
752 $tabIdentStringMD5 = '';
753 if (strstr($itemList, '--div--') !== FALSE && $this->enableTabMenu && $dividers2tabs) {
754 $tabIdentString = 'TCEforms:' . $table . ':' . $row['uid'];
755 $tabIdentStringMD5 = $GLOBALS['TBE_TEMPLATE']->getDynTabMenuId($tabIdentString);
756 // Remember that were currently working on the general tab:
757 if (isset($fields[0]) && strpos($fields[0], '--div--') !== 0) {
758 $this->pushToDynNestedStack('tab', $tabIdentStringMD5 . '-1');
759 }
760 }
761 // Traverse the fields to render:
762 $cc = 0;
763 foreach ($fields as $fieldInfo) {
764 // Exploding subparts of the field configuration:
765 $parts = explode(';', $fieldInfo);
766 // Getting the style information out:
767 $color_style_parts = GeneralUtility::trimExplode('-', $parts[4]);
768 if ($color_style_parts[0] !== '') {
769 $this->setColorScheme($GLOBALS['TBE_STYLES']['colorschemes'][(int)$color_style_parts[0]]);
770 }
771 if ($color_style_parts[1] !== '') {
772 $this->fieldStyle = $GLOBALS['TBE_STYLES']['styleschemes'][(int)$color_style_parts[1]];
773 if (!isset($this->fieldStyle)) {
774 $this->fieldStyle = $GLOBALS['TBE_STYLES']['styleschemes'][0];
775 }
776 }
777 if ($color_style_parts[2] !== '') {
778 $this->wrapBorder($out_array[$out_sheet], $out_pointer);
779 $this->borderStyle = $GLOBALS['TBE_STYLES']['borderschemes'][(int)$color_style_parts[2]];
780 if (!isset($this->borderStyle)) {
781 $this->borderStyle = $GLOBALS['TBE_STYLES']['borderschemes'][0];
782 }
783 }
784 // Render the field:
785 $theField = $parts[0];
786 if (!in_array($theField, $excludeElements)) {
787 if ($GLOBALS['TCA'][$table]['columns'][$theField]) {
788 $sFieldPal = '';
789 if ($parts[2] && !isset($this->palettesRendered[$this->renderDepth][$table][$parts[2]])) {
790 $sFieldPal = $this->getPaletteFields($table, $row, $parts[2]);
791 $this->palettesRendered[$this->renderDepth][$table][$parts[2]] = 1;
792 }
793 $sField = $this->getSingleField($table, $theField, $row, $parts[1], 0, $parts[3], $parts[2]);
794 if ($sField) {
795 $sField .= $sFieldPal;
796 }
797 $out_array[$out_sheet][$out_pointer] .= $sField;
798 } elseif ($theField == '--div--') {
799 if ($cc > 0) {
800 $out_array[$out_sheet][$out_pointer] .= $this->getDivider();
801 if ($this->enableTabMenu && $dividers2tabs) {
802 $this->wrapBorder($out_array[$out_sheet], $out_pointer);
803 // Remove last tab entry from the dynNestedStack:
804 $out_sheet++;
805 // Remove the previous sheet from stack (if any):
806 $this->popFromDynNestedStack('tab', $tabIdentStringMD5 . '-' . $out_sheet);
807 // Remember on which sheet we're currently working:
808 $this->pushToDynNestedStack('tab', $tabIdentStringMD5 . '-' . ($out_sheet + 1));
809 $out_array[$out_sheet] = array();
810 $out_array_meta[$out_sheet]['title'] = $this->sL($parts[1]);
811 // Register newline for Tab
812 $out_array_meta[$out_sheet]['newline'] = $parts[2] == 'newline';
813 }
814 } else {
815 // Setting alternative title for "General" tab if "--div--" is the very first element.
816 $out_array_meta[$out_sheet]['title'] = $this->sL($parts[1]);
817 // Only add the first tab to the dynNestedStack if there are more tabs:
818 if ($tabIdentString && strpos($itemList, '--div--', strlen($fieldInfo))) {
819 $this->pushToDynNestedStack('tab', $tabIdentStringMD5 . '-1');
820 }
821 }
822 } elseif ($theField == '--palette--') {
823 if ($parts[2] && !isset($this->palettesRendered[$this->renderDepth][$table][$parts[2]])) {
824 // Render a 'header' if not collapsed
825 if ($GLOBALS['TCA'][$table]['palettes'][$parts[2]]['canNotCollapse'] && $parts[1]) {
826 $out_array[$out_sheet][$out_pointer] .= $this->getPaletteFields($table, $row, $parts[2], $this->sL($parts[1]));
827 } else {
828 $out_array[$out_sheet][$out_pointer] .= $this->getPaletteFields($table, $row, $parts[2], '', '', $this->sL($parts[1]));
829 }
830 $this->palettesRendered[$this->renderDepth][$table][$parts[2]] = 1;
831 }
832 }
833 }
834 $cc++;
835 }
836 }
837 }
838 }
839 // Hook: getMainFields_postProcess (requested by Thomas Hempel for use with the "dynaflex" extension)
840 foreach ($this->hookObjectsMainFields as $hookObj) {
841 if (method_exists($hookObj, 'getMainFields_postProcess')) {
842 $hookObj->getMainFields_postProcess($table, $row, $this);
843 }
844 }
845 // Wrapping a border around it all:
846 $this->wrapBorder($out_array[$out_sheet], $out_pointer);
847 // Resetting styles:
848 $this->resetSchemes();
849 // Rendering Main palettes, if any
850 $mParr = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$table]['ctrl']['mainpalette']);
851 $i = 0;
852 if (count($mParr)) {
853 foreach ($mParr as $mP) {
854 if (!isset($this->palettesRendered[$this->renderDepth][$table][$mP])) {
855 $temp_palettesCollapsed = $this->palettesCollapsed;
856 $this->palettesCollapsed = 0;
857 $label = $i == 0 ? $this->getLL('l_generalOptions') : $this->getLL('l_generalOptions_more');
858 $out_array[$out_sheet][$out_pointer] .= $this->getPaletteFields($table, $row, $mP, $label);
859 $this->palettesCollapsed = $temp_palettesCollapsed;
860 $this->palettesRendered[$this->renderDepth][$table][$mP] = 1;
861 }
862 $this->wrapBorder($out_array[$out_sheet], $out_pointer);
863 $i++;
864 if ($this->renderDepth) {
865 $this->renderDepth--;
866 }
867 }
868 }
869 // Return the imploded $out_array:
870 // There were --div-- dividers around...
871 if ($out_sheet > 0) {
872 // Create parts array for the tab menu:
873 $parts = array();
874 foreach ($out_array as $idx => $sheetContent) {
875 $content = implode('', $sheetContent);
876 if ($content) {
877 // Wrap content (row) with table-tag, otherwise tab/sheet will be disabled (see getdynTabMenu() )
878 $content = '<table border="0" cellspacing="0" cellpadding="0" width="100%">' . $content . '</table>';
879 }
880 $parts[$idx] = array(
881 'label' => $out_array_meta[$idx]['title'],
882 'content' => $content,
883 'newline' => $out_array_meta[$idx]['newline']
884 );
885 }
886 if (count($parts) > 1) {
887 // Unset the current level of tab menus:
888 $this->popFromDynNestedStack('tab', $tabIdentStringMD5 . '-' . ($out_sheet + 1));
889 $dividersToTabsBehaviour = isset($GLOBALS['TCA'][$table]['ctrl']['dividers2tabs']) ? $GLOBALS['TCA'][$table]['ctrl']['dividers2tabs'] : 1;
890 $output = $this->getDynTabMenu($parts, $tabIdentString, $dividersToTabsBehaviour);
891 } else {
892 // If there is only one tab/part there is no need to wrap it into the dynTab code
893 $output = isset($parts[0]) ? trim($parts[0]['content']) : '';
894 }
895 $output = '
896 <tr>
897 <td colspan="2">
898 ' . $output . '
899 </td>
900 </tr>';
901 } else {
902 // Only one tab, so just implode and wrap the background image (= tab container) around:
903 $output = implode('', $out_array[$out_sheet]);
904 $output = '<div class="typo3-dyntabmenu-divs">' . $output . '</div>';
905 }
906
907 return $output;
908 }
909
910 /**
911 * Will return the TCEform elements for a pre-defined list of fields.
912 * Notice that this will STILL use the configuration found in the list [types][showitem] for those fields which are found there. So ideally the list of fields given as argument to this function should also be in the current [types][showitem] list of the record.
913 * Used for displaying forms for the frontend edit icons for instance.
914 *
915 * @param string $table The table name
916 * @param array $row The record array.
917 * @param string $list Commalist of fields from the table. These will be shown in the specified order in a form.
918 * @return string TCEform elements in a string.
919 * @todo Define visibility
920 */
921 public function getListedFields($table, $row, $list) {
922 if ($this->edit_showFieldHelp || $this->doLoadTableDescr($table)) {
923 $GLOBALS['LANG']->loadSingleTableDescription($table);
924 }
925 $out = '';
926 $types_fieldConfig = BackendUtility::getTCAtypes($table, $row, 1);
927 $editFieldList = array_unique(GeneralUtility::trimExplode(',', $list, TRUE));
928 foreach ($editFieldList as $theFieldC) {
929 list($theField, $palFields) = preg_split('/\\[|\\]/', $theFieldC);
930 $theField = trim($theField);
931 $palFields = trim($palFields);
932 if ($GLOBALS['TCA'][$table]['columns'][$theField]) {
933 $parts = GeneralUtility::trimExplode(';', $types_fieldConfig[$theField]['origString']);
934 // Don't sent palette pointer - there are no options anyways for a field-list.
935 $sField = $this->getSingleField($table, $theField, $row, $parts[1], 0, $parts[3], 0);
936 $out .= $sField;
937 } elseif ($theField == '--div--') {
938 $out .= $this->getDivider();
939 }
940 if ($palFields) {
941 $out .= $this->getPaletteFields($table, $row, '', '', implode(',', GeneralUtility::trimExplode('|', $palFields, TRUE)));
942 }
943 }
944 return $out;
945 }
946
947 /**
948 * Creates a palette (collection of secondary options).
949 *
950 * @param string $table The table name
951 * @param array $row The row array
952 * @param string $palette The palette number/pointer
953 * @param string $header Header string for the palette (used when in-form). If not set, no header item is made.
954 * @param string $itemList Optional alternative list of fields for the palette
955 * @param string $collapsedHeader Optional Link text for activating a palette (when palettes does not have another form element to belong to).
956 * @return string HTML code.
957 * @todo Define visibility
958 */
959 public function getPaletteFields($table, $row, $palette, $header = '', $itemList = '', $collapsedHeader = NULL) {
960 if (!$this->doPrintPalette) {
961 return '';
962 }
963 $out = '';
964 $parts = $this->loadPaletteElements($table, $row, $palette, $itemList);
965 // Put palette together if there are fields in it:
966 if (count($parts)) {
967 $realFields = 0;
968 foreach ($parts as $part) {
969 if ($part['NAME'] !== '--linebreak--') {
970 $realFields++;
971 }
972 }
973 if ($realFields > 0) {
974 if ($header) {
975 $out .= $this->intoTemplate(array('HEADER' => htmlspecialchars($header)), $this->palFieldTemplateHeader);
976 }
977 $collapsed = $this->isPalettesCollapsed($table, $palette);
978 // Check if the palette is a hidden palette
979 $isHiddenPalette = !empty($GLOBALS['TCA'][$table]['palettes'][$palette]['isHiddenPalette']);
980 $thePalIcon = '';
981 if ($collapsed && $collapsedHeader !== NULL && !$isHiddenPalette) {
982 list($thePalIcon, ) = $this->wrapOpenPalette(IconUtility::getSpriteIcon('actions-system-options-view', array('title' => htmlspecialchars($this->getLL('l_moreOptions')))), $table, $row, $palette, 1);
983 $thePalIcon = '<span style="margin-left: 20px;">' . $thePalIcon . $collapsedHeader . '</span>';
984 }
985 $paletteHtml = $this->wrapPaletteField($this->printPalette($parts), $table, $row, $palette, $collapsed);
986 $out .= $this->intoTemplate(array('PALETTE' => $thePalIcon . $paletteHtml), $this->palFieldTemplate);
987 }
988 }
989 return $out;
990 }
991
992 /**
993 * Returns the form HTML code for a database table field.
994 *
995 * @param string $table The table name
996 * @param string $field The field name
997 * @param array $row The record to edit from the database table.
998 * @param string $altName Alternative field name label to show.
999 * @param boolean $palette Set this if the field is on a palette (in top frame), otherwise not. (if set, field will render as a hidden field).
1000 * @param string $extra The "extra" options from "Part 4" of the field configurations found in the "types" "showitem" list. Typically parsed by $this->getSpecConfFromString() in order to get the options as an associative array.
1001 * @param integer $pal The palette pointer.
1002 * @return mixed String (normal) or array (palettes)
1003 * @todo Define visibility
1004 */
1005 public function getSingleField($table, $field, $row, $altName = '', $palette = FALSE, $extra = '', $pal = 0) {
1006 // Hook: getSingleField_preProcess
1007 foreach ($this->hookObjectsSingleField as $hookObj) {
1008 if (method_exists($hookObj, 'getSingleField_preProcess')) {
1009 $hookObj->getSingleField_preProcess($table, $field, $row, $altName, $palette, $extra, $pal, $this);
1010 }
1011 }
1012 $out = '';
1013 $PA = array();
1014 $PA['altName'] = $altName;
1015 $PA['palette'] = $palette;
1016 $PA['extra'] = $extra;
1017 $PA['pal'] = $pal;
1018 // Get the TCA configuration for the current field:
1019 $PA['fieldConf'] = $GLOBALS['TCA'][$table]['columns'][$field];
1020 $PA['fieldConf']['config']['form_type'] = $PA['fieldConf']['config']['form_type'] ?: $PA['fieldConf']['config']['type'];
1021
1022 // Using "form_type" locally in this script
1023 $skipThisField = $this->inline->skipField($table, $field, $row, $PA['fieldConf']['config']);
1024
1025 // Evaluate display condition
1026 $displayConditionResult = TRUE;
1027 if (is_array($PA['fieldConf']) && $PA['fieldConf']['displayCond'] && is_array($row)) {
1028 /** @var $elementConditionMatcher \TYPO3\CMS\Backend\Form\ElementConditionMatcher */
1029 $elementConditionMatcher = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Form\\ElementConditionMatcher');
1030 $displayConditionResult = $elementConditionMatcher->match($PA['fieldConf']['displayCond'], $row);
1031 }
1032 // Check if this field is configured and editable (according to excludefields + other configuration)
1033 if (
1034 is_array($PA['fieldConf'])
1035 && !$skipThisField
1036 && (!$PA['fieldConf']['exclude'] || $GLOBALS['BE_USER']->check('non_exclude_fields', $table . ':' . $field))
1037 && $PA['fieldConf']['config']['form_type'] != 'passthrough'
1038 && ($this->RTEenabled || !$PA['fieldConf']['config']['showIfRTE'])
1039 && $displayConditionResult
1040 && (!$GLOBALS['TCA'][$table]['ctrl']['languageField'] || $PA['fieldConf']['l10n_display'] || ($PA['fieldConf']['l10n_mode'] !== 'exclude') || $row[$GLOBALS['TCA'][$table]['ctrl']['languageField']] <= 0)
1041 && (!$GLOBALS['TCA'][$table]['ctrl']['languageField'] || !$this->localizationMode || $this->localizationMode === $PA['fieldConf']['l10n_cat'])
1042 ) {
1043 // Fetching the TSconfig for the current table/field. This includes the $row which means that
1044 $PA['fieldTSConfig'] = $this->setTSconfig($table, $row, $field);
1045 // If the field is NOT disabled from TSconfig (which it could have been) then render it
1046 if (!$PA['fieldTSConfig']['disabled']) {
1047 // Override fieldConf by fieldTSconfig:
1048 $PA['fieldConf']['config'] = $this->overrideFieldConf($PA['fieldConf']['config'], $PA['fieldTSConfig']);
1049 // Init variables:
1050 $PA['itemFormElName'] = $this->prependFormFieldNames . '[' . $table . '][' . $row['uid'] . '][' . $field . ']';
1051 // Form field name, in case of file uploads
1052 $PA['itemFormElName_file'] = $this->prependFormFieldNames_file . '[' . $table . '][' . $row['uid'] . '][' . $field . ']';
1053 // Form field name, to activate elements
1054 // If the "eval" list contains "null", elements can be deactivated which results in storing NULL to database
1055 $PA['itemFormElNameActive'] = $this->prependFormFieldNamesActive . '[' . $table . '][' . $row['uid'] . '][' . $field . ']';
1056 // The value to show in the form field.
1057 $PA['itemFormElValue'] = $row[$field];
1058 $PA['itemFormElID'] = $this->prependFormFieldNames . '_' . $table . '_' . $row['uid'] . '_' . $field;
1059 // Set field to read-only if configured for translated records to show default language content as readonly
1060 if ($PA['fieldConf']['l10n_display'] && GeneralUtility::inList($PA['fieldConf']['l10n_display'], 'defaultAsReadonly') && $row[$GLOBALS['TCA'][$table]['ctrl']['languageField']] > 0) {
1061 $PA['fieldConf']['config']['readOnly'] = TRUE;
1062 $PA['itemFormElValue'] = $this->defaultLanguageData[$table . ':' . $row['uid']][$field];
1063 }
1064 if (strpos($GLOBALS['TCA'][$table]['ctrl']['type'], ':') === FALSE) {
1065 $typeField = $GLOBALS['TCA'][$table]['ctrl']['type'];
1066 } else {
1067 $typeField = substr($GLOBALS['TCA'][$table]['ctrl']['type'], 0, strpos($GLOBALS['TCA'][$table]['ctrl']['type'], ':'));
1068 }
1069 // Create a JavaScript code line which will ask the user to save/update the form due to changing the element. This is used for eg. "type" fields and others configured with "requestUpdate"
1070 if (
1071 !empty($GLOBALS['TCA'][$table]['ctrl']['type'])
1072 && $field === $typeField
1073 || !empty($GLOBALS['TCA'][$table]['ctrl']['requestUpdate'])
1074 && GeneralUtility::inList(str_replace(' ', '', $GLOBALS['TCA'][$table]['ctrl']['requestUpdate']), $field)
1075 ) {
1076 if ($GLOBALS['BE_USER']->jsConfirmation(1)) {
1077 $alertMsgOnChange = 'if (confirm(TBE_EDITOR.labels.onChangeAlert) && TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
1078 } else {
1079 $alertMsgOnChange = 'if (TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
1080 }
1081 } else {
1082 $alertMsgOnChange = '';
1083 }
1084 // Render as a hidden field?
1085 if (in_array($field, $this->hiddenFieldListArr)) {
1086 $this->hiddenFieldAccum[] = '<input type="hidden" name="' . $PA['itemFormElName'] . '" value="' . htmlspecialchars($PA['itemFormElValue']) . '" />';
1087 } else {
1088 // Render as a normal field:
1089 // If the field is NOT a palette field, then we might create an icon which links to a palette for the field, if one exists.
1090 if (!$PA['palette']) {
1091 $paletteFields = $this->loadPaletteElements($table, $row, $PA['pal']);
1092 if ($PA['pal'] && $this->isPalettesCollapsed($table, $PA['pal']) && count($paletteFields)) {
1093 list($thePalIcon, $palJSfunc) = $this->wrapOpenPalette(IconUtility::getSpriteIcon('actions-system-options-view', array('title' => htmlspecialchars($this->getLL('l_moreOptions')))), $table, $row, $PA['pal'], 1);
1094 } else {
1095 $thePalIcon = '';
1096 $palJSfunc = '';
1097 }
1098 }
1099 // onFocus attribute to add to the field:
1100 $PA['onFocus'] = $palJSfunc && !$GLOBALS['BE_USER']->uc['dontShowPalettesOnFocusInAB'] ? ' onfocus="' . htmlspecialchars($palJSfunc) . '"' : '';
1101 $PA['label'] = $PA['altName'] ?: $PA['fieldConf']['label'];
1102 $PA['label'] = $PA['fieldTSConfig']['label'] ?: $PA['label'];
1103 $PA['label'] = $PA['fieldTSConfig']['label.'][$GLOBALS['LANG']->lang] ?: $PA['label'];
1104 $PA['label'] = $this->sL($PA['label']);
1105 // JavaScript code for event handlers:
1106 $PA['fieldChangeFunc'] = array();
1107 $PA['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] = 'TBE_EDITOR.fieldChanged(\'' . $table . '\',\'' . $row['uid'] . '\',\'' . $field . '\',\'' . $PA['itemFormElName'] . '\');';
1108 $PA['fieldChangeFunc']['alert'] = $alertMsgOnChange;
1109 // If this is the child of an inline type and it is the field creating the label
1110 if ($this->inline->isInlineChildAndLabelField($table, $field)) {
1111 $inlineObjectId = implode(InlineElement::Structure_Separator, array(
1112 $this->inline->inlineNames['object'],
1113 $table,
1114 $row['uid']
1115 ));
1116 $PA['fieldChangeFunc']['inline'] = 'inline.handleChangedField(\'' . $PA['itemFormElName'] . '\',\'' . $inlineObjectId . '\');';
1117 }
1118 // Based on the type of the item, call a render function:
1119 $item = $this->getSingleField_SW($table, $field, $row, $PA);
1120 // Add language + diff
1121 if ($PA['fieldConf']['l10n_display'] && (GeneralUtility::inList($PA['fieldConf']['l10n_display'], 'hideDiff') || GeneralUtility::inList($PA['fieldConf']['l10n_display'], 'defaultAsReadonly'))) {
1122 $renderLanguageDiff = FALSE;
1123 } else {
1124 $renderLanguageDiff = TRUE;
1125 }
1126 if ($renderLanguageDiff) {
1127 $item = $this->renderDefaultLanguageContent($table, $field, $row, $item);
1128 $item = $this->renderDefaultLanguageDiff($table, $field, $row, $item);
1129 }
1130 // If the record has been saved and the "linkTitleToSelf" is set, we make the field name into a link, which will load ONLY this field in alt_doc.php
1131 $label = htmlspecialchars($PA['label'], ENT_COMPAT, 'UTF-8', FALSE);
1132 if (MathUtility::canBeInterpretedAsInteger($row['uid']) && $PA['fieldTSConfig']['linkTitleToSelf'] && !GeneralUtility::_GP('columnsOnly')) {
1133 $lTTS_url = $this->backPath . 'alt_doc.php?edit[' . $table . '][' . $row['uid'] . ']=edit&columnsOnly=' . $field . '&returnUrl=' . rawurlencode($this->thisReturnUrl());
1134 $label = '<a href="' . htmlspecialchars($lTTS_url) . '">' . $label . '</a>';
1135 }
1136
1137 if (isset($PA['fieldConf']['config']['mode']) && $PA['fieldConf']['config']['mode'] == 'useOrOverridePlaceholder') {
1138 $placeholder = $this->getPlaceholderValue($table, $field, $PA['fieldConf']['config'], $row);
1139 $onChange = 'typo3form.fieldTogglePlaceholder(' . GeneralUtility::quoteJSvalue($PA['itemFormElName']) . ', !this.checked)';
1140 $checked = $PA['itemFormElValue'] === NULL ? '' : ' checked="checked"';
1141
1142 $this->additionalJS_post[] = 'typo3form.fieldTogglePlaceholder('
1143 . GeneralUtility::quoteJSvalue($PA['itemFormElName']) . ', ' . ($checked ? 'false' : 'true') . ');';
1144
1145 $item = '<div class="t3-form-field-placeholder-override">'
1146 . '<span class="t3-tceforms-placeholder-override-checkbox">' .
1147 '<input type="hidden" name="' . htmlspecialchars($PA['itemFormElNameActive']) . '" value="0" />' .
1148 '<input type="checkbox" name="' . htmlspecialchars($PA['itemFormElNameActive']) . '" value="1" id="tce-forms-textfield-use-override-' . $field . '-' . $row['uid'] . '" onchange="' . htmlspecialchars($onChange) . '"' . $checked . ' />' .
1149 '<label for="tce-forms-textfield-use-override-' . $field . '-' . $row['uid'] . '">' .
1150 sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.placeholder.override'),
1151 BackendUtility::getRecordTitlePrep($placeholder, 20)) . '</label>' .
1152 '</span>'
1153 . '<div class="t3-form-placeholder-placeholder">' . $this->getSingleField_typeNone_render(
1154 $PA['fieldConf']['config'], GeneralUtility::fixed_lgd_cs($placeholder, 30)
1155 ) . '</div>'
1156 . '<div class="t3-form-placeholder-formfield">' . $item . '</div>'
1157 . '</div>';
1158 }
1159
1160 // Wrap the label with help text
1161 $PA['label'] = ($label = BackendUtility::wrapInHelp($table, $field, $label));
1162 // Create output value:
1163 if ($PA['fieldConf']['config']['form_type'] == 'user' && $PA['fieldConf']['config']['noTableWrapping']) {
1164 $out = $item;
1165 } elseif ($PA['palette']) {
1166 // Array:
1167 $out = array(
1168 'NAME' => $label,
1169 'ID' => $row['uid'],
1170 'FIELD' => $field,
1171 'TABLE' => $table,
1172 'ITEM' => $item,
1173 'ITEM_DISABLED' => ($this->isDisabledNullValueField($table, $field, $row, $PA) ? ' disabled' : ''),
1174 'ITEM_NULLVALUE' => $this->renderNullValueWidget($table, $field, $row, $PA),
1175 );
1176 $out = $this->addUserTemplateMarkers($out, $table, $field, $row, $PA);
1177 } else {
1178 // String:
1179 $out = array(
1180 'NAME' => $label,
1181 'ITEM' => $item,
1182 'TABLE' => $table,
1183 'ID' => $row['uid'],
1184 'PAL_LINK_ICON' => $thePalIcon,
1185 'FIELD' => $field,
1186 'ITEM_DISABLED' => ($this->isDisabledNullValueField($table, $field, $row, $PA) ? ' disabled' : ''),
1187 'ITEM_NULLVALUE' => $this->renderNullValueWidget($table, $field, $row, $PA),
1188 );
1189 $out = $this->addUserTemplateMarkers($out, $table, $field, $row, $PA);
1190 // String:
1191 $out = $this->intoTemplate($out);
1192 }
1193 }
1194 } else {
1195 $this->commentMessages[] = $this->prependFormFieldNames . '[' . $table . '][' . $row['uid'] . '][' . $field . ']: Disabled by TSconfig';
1196 }
1197 }
1198 // Hook: getSingleField_postProcess
1199 foreach ($this->hookObjectsSingleField as $hookObj) {
1200 if (method_exists($hookObj, 'getSingleField_postProcess')) {
1201 $hookObj->getSingleField_postProcess($table, $field, $row, $out, $PA, $this);
1202 }
1203 }
1204 // Return value (string or array)
1205 return $out;
1206 }
1207
1208 /**
1209 * Rendering a single item for the form
1210 *
1211 * @param string $table Table name of record
1212 * @param string $field Fieldname to render
1213 * @param array $row The record
1214 * @param array $PA Parameters array containing a lot of stuff. Value by Reference!
1215 * @return string Returns the item as HTML code to insert
1216 * @access private
1217 * @see getSingleField(), getSingleField_typeFlex_draw()
1218 * @todo Define visibility
1219 */
1220 public function getSingleField_SW($table, $field, $row, &$PA) {
1221 $PA['fieldConf']['config']['form_type'] = $PA['fieldConf']['config']['form_type'] ?: $PA['fieldConf']['config']['type'];
1222 // Using "form_type" locally in this script
1223 // Hook: getSingleField_beforeRender
1224 foreach ($this->hookObjectsSingleField as $hookObject) {
1225 if (method_exists($hookObject, 'getSingleField_beforeRender')) {
1226 $hookObject->getSingleField_beforeRender($table, $field, $row, $PA);
1227 }
1228 }
1229 switch ($PA['fieldConf']['config']['form_type']) {
1230 case 'input':
1231 $item = $this->getSingleField_typeInput($table, $field, $row, $PA);
1232 break;
1233 case 'text':
1234 $item = $this->getSingleField_typeText($table, $field, $row, $PA);
1235 break;
1236 case 'check':
1237 $item = $this->getSingleField_typeCheck($table, $field, $row, $PA);
1238 break;
1239 case 'radio':
1240 $item = $this->getSingleField_typeRadio($table, $field, $row, $PA);
1241 break;
1242 case 'select':
1243 $item = $this->getSingleField_typeSelect($table, $field, $row, $PA);
1244 break;
1245 case 'group':
1246 $item = $this->getSingleField_typeGroup($table, $field, $row, $PA);
1247 break;
1248 case 'inline':
1249 $item = $this->inline->getSingleField_typeInline($table, $field, $row, $PA);
1250 break;
1251 case 'none':
1252 $item = $this->getSingleField_typeNone($table, $field, $row, $PA);
1253 break;
1254 case 'user':
1255 $item = $this->getSingleField_typeUser($table, $field, $row, $PA);
1256 break;
1257 case 'flex':
1258 $item = $this->getSingleField_typeFlex($table, $field, $row, $PA);
1259 break;
1260 default:
1261 $item = $this->getSingleField_typeUnknown($table, $field, $row, $PA);
1262 }
1263 return $item;
1264 }
1265
1266 /**********************************************************
1267 *
1268 * Rendering of each TCEform field type
1269 *
1270 ************************************************************/
1271 /**
1272 * Generation of TCEform elements of the type "input"
1273 * This will render a single-line input form field, possibly with various control/validation features
1274 *
1275 * @param string $table The table name of the record
1276 * @param string $field The field name which this element is supposed to edit
1277 * @param array $row The record data array where the value(s) for the field can be found
1278 * @param array $PA An array with additional configuration options.
1279 * @return string The HTML code for the TCEform field
1280 * @todo Define visibility
1281 */
1282 public function getSingleField_typeInput($table, $field, $row, &$PA) {
1283 $config = $PA['fieldConf']['config'];
1284 $specConf = $this->getSpecConfFromString($PA['extra'], $PA['fieldConf']['defaultExtras']);
1285 $size = MathUtility::forceIntegerInRange($config['size'] ? $config['size'] : 30, 5, $this->maxInputWidth);
1286 $evalList = GeneralUtility::trimExplode(',', $config['eval'], TRUE);
1287 $classAndStyleAttributes = $this->formWidthAsArray($size);
1288 $fieldAppendix = '';
1289 $item = '';
1290 $cssClasses = array($classAndStyleAttributes['class']);
1291 $cssStyle = $classAndStyleAttributes['style'];
1292 if (!isset($config['checkbox'])) {
1293 $config['checkbox'] = '0';
1294 $checkboxIsset = FALSE;
1295 } else {
1296 $checkboxIsset = TRUE;
1297 }
1298 if (in_array('date', $evalList) || in_array('datetime', $evalList)) {
1299 if (in_array('datetime', $evalList)) {
1300 $class = 'datetime';
1301 } else {
1302 $class = 'date';
1303 }
1304 $dateRange = '';
1305 if (isset($config['range']['lower'])) {
1306 $dateRange .= ' lower-' . (int)$config['range']['lower'];
1307 }
1308 if (isset($config['range']['upper'])) {
1309 $dateRange .= ' upper-' . (int)$config['range']['upper'];
1310 }
1311 $inputId = uniqid('tceforms-' . $class . 'field-');
1312 $cssClasses[] = 'tceforms-textfield tceforms-' . $class . 'field' . $dateRange;
1313 $fieldAppendix = IconUtility::getSpriteIcon('actions-edit-pick-date', array(
1314 'style' => 'cursor:pointer;',
1315 'id' => 'picker-' . $inputId
1316 ));
1317 } elseif (in_array('timesec', $evalList)) {
1318 $inputId = uniqid('tceforms-timesecfield-');
1319 $cssClasses[] = 'tceforms-textfield tceforms-timesecfield';
1320 } elseif (in_array('year', $evalList)) {
1321 $inputId = uniqid('tceforms-yearfield-');
1322 $cssClasses[] = 'tceforms-textfield tceforms-yearfield';
1323 } elseif (in_array('time', $evalList)) {
1324 $inputId = uniqid('tceforms-timefield-');
1325 $cssClasses[] = 'tceforms-textfield tceforms-timefield';
1326 } elseif (in_array('int', $evalList)) {
1327 $inputId = uniqid('tceforms-intfield-');
1328 $cssClasses[] = 'tceforms-textfield tceforms-intfield';
1329 } elseif (in_array('double2', $evalList)) {
1330 $inputId = uniqid('tceforms-double2field-');
1331 $cssClasses[] = 'tceforms-textfield tceforms-double2field';
1332 } else {
1333 $inputId = uniqid('tceforms-textfield-');
1334 $cssClasses[] = 'tceforms-textfield';
1335 if ($checkboxIsset === FALSE) {
1336 $config['checkbox'] = '';
1337 }
1338 }
1339 if (isset($config['wizards']['link'])) {
1340 $inputId = uniqid('tceforms-linkfield-');
1341 $cssClasses[] = 'tceforms-textfield tceforms-linkfield';
1342 } elseif (isset($config['wizards']['color'])) {
1343 $inputId = uniqid('tceforms-colorfield-');
1344 $cssClasses[] = 'tceforms-textfield tceforms-colorfield';
1345 }
1346 if ($this->renderReadonly || $config['readOnly']) {
1347 $itemFormElValue = $PA['itemFormElValue'];
1348 if (in_array('date', $evalList)) {
1349 $config['format'] = 'date';
1350 } elseif (in_array('datetime', $evalList)) {
1351 $config['format'] = 'datetime';
1352 } elseif (in_array('time', $evalList)) {
1353 $config['format'] = 'time';
1354 }
1355 if (in_array('password', $evalList)) {
1356 $itemFormElValue = $itemFormElValue ? '*********' : '';
1357 }
1358 return $this->getSingleField_typeNone_render($config, $itemFormElValue);
1359 }
1360 foreach ($evalList as $func) {
1361 switch ($func) {
1362 case 'required':
1363 $this->registerRequiredProperty('field', $table . '_' . $row['uid'] . '_' . $field, $PA['itemFormElName']);
1364 // Mark this field for date/time disposal:
1365 if (array_intersect($evalList, array('date', 'datetime', 'time'))) {
1366 $this->requiredAdditional[$PA['itemFormElName']]['isPositiveNumber'] = TRUE;
1367 }
1368 break;
1369 default:
1370 // Pair hook to the one in \TYPO3\CMS\Core\DataHandling\DataHandler::checkValue_input_Eval()
1371 $evalObj = GeneralUtility::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][$func] . ':&' . $func);
1372 if (is_object($evalObj) && method_exists($evalObj, 'deevaluateFieldValue')) {
1373 $_params = array(
1374 'value' => $PA['itemFormElValue']
1375 );
1376 $PA['itemFormElValue'] = $evalObj->deevaluateFieldValue($_params);
1377 }
1378 }
1379 }
1380 $paramsList = '\'' . $PA['itemFormElName'] . '\',\'' . implode(',', $evalList) . '\',\'' . trim($config['is_in']) . '\',' . (isset($config['checkbox']) ? 1 : 0) . ',\'' . $config['checkbox'] . '\'';
1381 if (in_array('date', $evalList) || in_array('datetime', $evalList)) {
1382 $item .= '<span class="t3-tceforms-input-wrapper-datetime" onmouseOver="if (document.getElementById(\'' . $inputId . '\').value) {this.className=\'t3-tceforms-input-wrapper-datetime-hover\';} else {this.className=\'t3-tceforms-input-wrapper-datetime\';};" onmouseOut="this.className=\'t3-tceforms-input-wrapper-datetime\';">';
1383 // Add server timezone offset to UTC to our stored date
1384 if ($PA['itemFormElValue'] > 0) {
1385 $PA['itemFormElValue'] += date('Z', $PA['itemFormElValue']);
1386 }
1387 } else {
1388 $item .= '<span class="t3-tceforms-input-wrapper" onmouseOver="if (document.getElementById(\'' . $inputId . '\').value) {this.className=\'t3-tceforms-input-wrapper-hover\';} else {this.className=\'t3-tceforms-input-wrapper\';};" onmouseOut="this.className=\'t3-tceforms-input-wrapper\';">';
1389 }
1390 $PA['fieldChangeFunc'] = array_merge(array('typo3form.fieldGet' => 'typo3form.fieldGet(' . $paramsList . ');'), $PA['fieldChangeFunc']);
1391 // Old function "checkbox" now the option to set the date / remove the date
1392 if (isset($config['checkbox'])) {
1393 $item .= IconUtility::getSpriteIcon('actions-input-clear', array('tag' => 'a', 'class' => 't3-tceforms-input-clearer', 'onclick' => 'document.getElementById(\'' . $inputId . '\').value=\'\';document.getElementById(\'' . $inputId . '\').focus();' . implode('', $PA['fieldChangeFunc'])));
1394 }
1395 $mLgd = $config['max'] ?: 256;
1396 $iOnChange = implode('', $PA['fieldChangeFunc']);
1397 $cssClasses[] = 'hasDefaultValue';
1398 $item .= '<input type="text" ' . $this->getPlaceholderAttribute($table, $field, $config, $row) . 'id="' . $inputId . '" ' . 'class="' . implode(' ', $cssClasses) . '" ' . 'name="' . $PA['itemFormElName'] . '_hr" ' . 'value=""' . 'style="' . $cssStyle . '" ' . 'maxlength="' . $mLgd . '" ' . 'onchange="' . htmlspecialchars($iOnChange) . '"' . $PA['onFocus'] . ' />';
1399 // This is the EDITABLE form field.
1400 $item .= '<input type="hidden" name="' . $PA['itemFormElName'] . '" value="' . htmlspecialchars($PA['itemFormElValue']) . '" />';
1401 // This is the ACTUAL form field - values from the EDITABLE field must be transferred to this field which is the one that is written to the database.
1402 $item .= $fieldAppendix . '</span><div style="clear:both;"></div>';
1403 $this->extJSCODE .= 'typo3form.fieldSet(' . $paramsList . ');';
1404 // Going through all custom evaluations configured for this field
1405 foreach ($evalList as $evalData) {
1406 $evalObj = GeneralUtility::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][$evalData] . ':&' . $evalData);
1407 if (is_object($evalObj) && method_exists($evalObj, 'returnFieldJS')) {
1408 $this->extJSCODE .= '
1409 TBE_EDITOR.customEvalFunctions[\'' . $evalData . '\'] = function(value) {
1410 ' . $evalObj->returnFieldJS() . '
1411 }
1412 ';
1413 }
1414 }
1415 // Creating an alternative item without the JavaScript handlers.
1416 $altItem = '<input type="hidden" name="' . $PA['itemFormElName'] . '_hr" value="" />';
1417 $altItem .= '<input type="hidden" name="' . $PA['itemFormElName'] . '" value="' . htmlspecialchars($PA['itemFormElValue']) . '" />';
1418 // Wrap a wizard around the item?
1419 $item = $this->renderWizards(array($item, $altItem), $config['wizards'], $table, $row, $field, $PA, $PA['itemFormElName'] . '_hr', $specConf);
1420
1421 return $item;
1422 }
1423
1424 /**
1425 * Renders a view widget to handle and activate NULL values.
1426 * The widget is enabled by using 'null' in the 'eval' TCA definition.
1427 *
1428 * @param string $table Name of the table
1429 * @param string $field Name of the field
1430 * @param array $row Accordant data of the record row
1431 * @param array $PA Parameters array with rendering instructions
1432 * @return string Widget (if any).
1433 */
1434 protected function renderNullValueWidget($table, $field, array $row, array $PA) {
1435 $widget = '';
1436
1437 $config = $PA['fieldConf']['config'];
1438 if (!empty($config['eval']) && GeneralUtility::inList($config['eval'], 'null') && (empty($config['mode']) || $config['mode'] !== 'useOrOverridePlaceholder')) {
1439 $isNull = ($PA['itemFormElValue'] === NULL);
1440
1441 $checked = ($isNull ? '' : ' checked="checked"');
1442 $onChange = htmlspecialchars(
1443 'typo3form.fieldSetNull(\'' . $PA['itemFormElName'] . '\', !this.checked)'
1444 );
1445
1446 $widget = '<span class="t3-tceforms-widget-null-wrapper">' .
1447 '<input type="hidden" name="' . $PA['itemFormElNameActive'] . '" value="0" />' .
1448 '<input type="checkbox" name="' . $PA['itemFormElNameActive'] . '" value="1" onchange="' . $onChange . '"' . $checked . ' />' .
1449 '</span>';
1450 }
1451
1452 return $widget;
1453 }
1454
1455 /**
1456 * Determines whether the current field value is considered as NULL value.
1457 * Using NULL values is enabled by using 'null' in the 'eval' TCA definition.
1458 *
1459 * @param string $table Name of the table
1460 * @param string $field Name of the field
1461 * @param array $row Accordant data
1462 * @param array $PA Parameters array with rendering instructions
1463 * @return boolean
1464 */
1465 protected function isDisabledNullValueField($table, $field, array $row, array $PA) {
1466 $result = FALSE;
1467
1468 $config = $PA['fieldConf']['config'];
1469 if ($PA['itemFormElValue'] === NULL && !empty($config['eval'])
1470 && GeneralUtility::inList($config['eval'], 'null')
1471 && (empty($config['mode']) || $config['mode'] !== 'useOrOverridePlaceholder')) {
1472
1473 $result = TRUE;
1474 }
1475
1476 return $result;
1477 }
1478
1479 /**
1480 * Generation of TCEform elements of the type "text"
1481 * This will render a <textarea> OR RTE area form field, possibly with various control/validation features
1482 *
1483 * @param string $table The table name of the record
1484 * @param string $field The field name which this element is supposed to edit
1485 * @param array $row The record data array where the value(s) for the field can be found
1486 * @param array $PA An array with additional configuration options.
1487 * @return string The HTML code for the TCEform field
1488 * @todo Define visibility
1489 */
1490 public function getSingleField_typeText($table, $field, $row, &$PA) {
1491 // Init config:
1492 $config = $PA['fieldConf']['config'];
1493 $evalList = GeneralUtility::trimExplode(',', $config['eval'], TRUE);
1494 if ($this->renderReadonly || $config['readOnly']) {
1495 return $this->getSingleField_typeNone_render($config, $PA['itemFormElValue']);
1496 }
1497 // Setting columns number:
1498 $cols = MathUtility::forceIntegerInRange($config['cols'] ? $config['cols'] : 30, 5, $this->maxTextareaWidth);
1499 // Setting number of rows:
1500 $origRows = ($rows = MathUtility::forceIntegerInRange($config['rows'] ? $config['rows'] : 5, 1, 20));
1501 if (strlen($PA['itemFormElValue']) > $this->charsPerRow * 2) {
1502 $cols = $this->maxTextareaWidth;
1503 $rows = MathUtility::forceIntegerInRange(round(strlen($PA['itemFormElValue']) / $this->charsPerRow), count(explode(LF, $PA['itemFormElValue'])), 20);
1504 if ($rows < $origRows) {
1505 $rows = $origRows;
1506 }
1507 }
1508 if (in_array('required', $evalList)) {
1509 $this->requiredFields[$table . '_' . $row['uid'] . '_' . $field] = $PA['itemFormElName'];
1510 }
1511 // Init RTE vars:
1512 // Set TRUE, if the RTE is loaded; If not a normal textarea is shown.
1513 $RTEwasLoaded = 0;
1514 // Set TRUE, if the RTE would have been loaded if it wasn't for the disable-RTE flag in the bottom of the page...
1515 $RTEwouldHaveBeenLoaded = 0;
1516 // "Extra" configuration; Returns configuration for the field based on settings found in the "types" fieldlist. Traditionally, this is where RTE configuration has been found.
1517 $specConf = $this->getSpecConfFromString($PA['extra'], $PA['fieldConf']['defaultExtras']);
1518 // Setting up the altItem form field, which is a hidden field containing the value
1519 $altItem = '<input type="hidden" name="' . htmlspecialchars($PA['itemFormElName']) . '" value="' . htmlspecialchars($PA['itemFormElValue']) . '" />';
1520 // If RTE is generally enabled (TYPO3_CONF_VARS and user settings)
1521 if ($this->RTEenabled) {
1522 $p = BackendUtility::getSpecConfParametersFromArray($specConf['rte_transform']['parameters']);
1523 // If the field is configured for RTE and if any flag-field is not set to disable it.
1524 if (isset($specConf['richtext']) && (!$p['flag'] || !$row[$p['flag']])) {
1525 BackendUtility::fixVersioningPid($table, $row);
1526 list($tscPID, $thePidValue) = $this->getTSCpid($table, $row['uid'], $row['pid']);
1527 // If the pid-value is not negative (that is, a pid could NOT be fetched)
1528 if ($thePidValue >= 0) {
1529 $RTEsetup = $GLOBALS['BE_USER']->getTSConfig('RTE', BackendUtility::getPagesTSconfig($tscPID));
1530 $RTEtypeVal = BackendUtility::getTCAtypeValue($table, $row);
1531 $thisConfig = BackendUtility::RTEsetup($RTEsetup['properties'], $table, $field, $RTEtypeVal);
1532 if (!$thisConfig['disabled']) {
1533 if (!$this->disableRTE) {
1534 $this->RTEcounter++;
1535 // Find alternative relative path for RTE images/links:
1536 $eFile = RteHtmlParser::evalWriteFile($specConf['static_write'], $row);
1537 $RTErelPath = is_array($eFile) ? dirname($eFile['relEditFile']) : '';
1538 // Get RTE object, draw form and set flag:
1539 $RTEobj = BackendUtility::RTEgetObj();
1540 $item = $RTEobj->drawRTE($this, $table, $field, $row, $PA, $specConf, $thisConfig, $RTEtypeVal, $RTErelPath, $thePidValue);
1541 // Wizard:
1542 $item = $this->renderWizards(array($item, $altItem), $config['wizards'], $table, $row, $field, $PA, $PA['itemFormElName'], $specConf, 1);
1543 $RTEwasLoaded = 1;
1544 } else {
1545 $RTEwouldHaveBeenLoaded = 1;
1546 $this->commentMessages[] = $PA['itemFormElName'] . ': RTE is disabled by the on-page RTE-flag (probably you can enable it by the check-box in the bottom of this page!)';
1547 }
1548 } else {
1549 $this->commentMessages[] = $PA['itemFormElName'] . ': RTE is disabled by the Page TSconfig, "RTE"-key (eg. by RTE.default.disabled=0 or such)';
1550 }
1551 } else {
1552 $this->commentMessages[] = $PA['itemFormElName'] . ': PID value could NOT be fetched. Rare error, normally with new records.';
1553 }
1554 } else {
1555 if (!isset($specConf['richtext'])) {
1556 $this->commentMessages[] = $PA['itemFormElName'] . ': RTE was not configured for this field in TCA-types';
1557 }
1558 if (!(!$p['flag'] || !$row[$p['flag']])) {
1559 $this->commentMessages[] = $PA['itemFormElName'] . ': Field-flag (' . $PA['flag'] . ') has been set to disable RTE!';
1560 }
1561 }
1562 }
1563 // Display ordinary field if RTE was not loaded.
1564 if (!$RTEwasLoaded) {
1565 // Show message, if no RTE (field can only be edited with RTE!)
1566 if ($specConf['rte_only']) {
1567 $item = '<p><em>' . htmlspecialchars($this->getLL('l_noRTEfound')) . '</em></p>';
1568 } else {
1569 if ($specConf['nowrap']) {
1570 $wrap = 'off';
1571 } else {
1572 $wrap = $config['wrap'] ?: 'virtual';
1573 }
1574 $classes = array();
1575 if ($specConf['fixed-font']) {
1576 $classes[] = 'fixed-font';
1577 }
1578 if ($specConf['enable-tab']) {
1579 $classes[] = 'enable-tab';
1580 }
1581 $formWidthText = $this->formWidthText($cols, $wrap);
1582 // Extract class attributes from $formWidthText (otherwise it would be added twice to the output)
1583 $res = array();
1584 if (preg_match('/ class="(.+?)"/', $formWidthText, $res)) {
1585 $formWidthText = str_replace(' class="' . $res[1] . '"', '', $formWidthText);
1586 $classes = array_merge($classes, explode(' ', $res[1]));
1587 }
1588 if (count($classes)) {
1589 $class = ' class="tceforms-textarea ' . implode(' ', $classes) . '"';
1590 } else {
1591 $class = 'tceforms-textarea';
1592 }
1593 $evalList = GeneralUtility::trimExplode(',', $config['eval'], TRUE);
1594 foreach ($evalList as $func) {
1595 switch ($func) {
1596 case 'required':
1597 $this->registerRequiredProperty('field', $table . '_' . $row['uid'] . '_' . $field, $PA['itemFormElName']);
1598 break;
1599 default:
1600 // Pair hook to the one in \TYPO3\CMS\Core\DataHandling\DataHandler::checkValue_input_Eval() and \TYPO3\CMS\Core\DataHandling\DataHandler::checkValue_text_Eval()
1601 $evalObj = GeneralUtility::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][$func] . ':&' . $func);
1602 if (is_object($evalObj) && method_exists($evalObj, 'deevaluateFieldValue')) {
1603 $_params = array(
1604 'value' => $PA['itemFormElValue']
1605 );
1606 $PA['itemFormElValue'] = $evalObj->deevaluateFieldValue($_params);
1607 }
1608 }
1609 }
1610 $iOnChange = implode('', $PA['fieldChangeFunc']);
1611 $item .= '
1612 <textarea ' . 'id="' . uniqid('tceforms-textarea-') . '" ' . 'name="' . $PA['itemFormElName'] . '"' . $formWidthText . $class . ' ' . 'rows="' . $rows . '" ' . 'wrap="' . $wrap . '" ' . 'onchange="' . htmlspecialchars($iOnChange) . '"' . $this->getPlaceholderAttribute($table, $field, $config, $row) . $PA['onFocus'] . '>' . GeneralUtility::formatForTextarea($PA['itemFormElValue']) . '</textarea>';
1613 $item = $this->renderWizards(array($item, $altItem), $config['wizards'], $table, $row, $field, $PA, $PA['itemFormElName'], $specConf, $RTEwouldHaveBeenLoaded);
1614 }
1615 }
1616 // Return field HTML:
1617 return $item;
1618 }
1619
1620 /**
1621 * Generation of TCEform elements of the type "check"
1622 * This will render a check-box OR an array of checkboxes
1623 *
1624 * @param string $table The table name of the record
1625 * @param string $field The field name which this element is supposed to edit
1626 * @param array $row The record data array where the value(s) for the field can be found
1627 * @param array $PA An array with additional configuration options.
1628 * @return string The HTML code for the TCEform field
1629 * @todo Define visibility
1630 */
1631 public function getSingleField_typeCheck($table, $field, $row, &$PA) {
1632 $config = $PA['fieldConf']['config'];
1633 $item = '';
1634 $disabled = '';
1635 if ($this->renderReadonly || $config['readOnly']) {
1636 $disabled = ' disabled="disabled"';
1637 }
1638 // Traversing the array of items:
1639 $selItems = $this->initItemArray($PA['fieldConf']);
1640 if ($config['itemsProcFunc']) {
1641 $selItems = $this->procItems($selItems, $PA['fieldTSConfig']['itemsProcFunc.'], $config, $table, $row, $field);
1642 }
1643 if (!count($selItems)) {
1644 $selItems[] = array('', '');
1645 }
1646 $thisValue = (int)$PA['itemFormElValue'];
1647 $cols = (int)$config['cols'];
1648 $selItemsCount = count($selItems);
1649 if ($cols > 1) {
1650 $item .= '<table border="0" cellspacing="0" cellpadding="0" class="typo3-TCEforms-checkboxArray">';
1651 for ($c = 0; $c < $selItemsCount; $c++) {
1652 $p = $selItems[$c];
1653 if (!($c % $cols)) {
1654 $item .= '<tr>';
1655 }
1656 $cBP = $this->checkBoxParams($PA['itemFormElName'], $thisValue, $c, count($selItems), implode('', $PA['fieldChangeFunc']));
1657 $cBName = $PA['itemFormElName'] . '_' . $c;
1658 $cBID = $PA['itemFormElID'] . '_' . $c;
1659 $item .= '<td nowrap="nowrap">' . '<input type="checkbox"' . $this->insertDefStyle('check') . ' value="1" name="' . $cBName . '"' . $cBP . $disabled . ' id="' . $cBID . '" />' . $this->wrapLabels(('<label for="' . $cBID . '">' . htmlspecialchars($p[0]) . '</label>&nbsp;')) . '</td>';
1660 if ($c % $cols + 1 == $cols) {
1661 $item .= '</tr>';
1662 }
1663 }
1664 if ($c % $cols) {
1665 $rest = $cols - $c % $cols;
1666 for ($c = 0; $c < $rest; $c++) {
1667 $item .= '<td></td>';
1668 }
1669 if ($c > 0) {
1670 $item .= '</tr>';
1671 }
1672 }
1673 $item .= '</table>';
1674 } else {
1675 for ($c = 0; $c < $selItemsCount; $c++) {
1676 $p = $selItems[$c];
1677 $cBP = $this->checkBoxParams($PA['itemFormElName'], $thisValue, $c, count($selItems), implode('', $PA['fieldChangeFunc']));
1678 $cBName = $PA['itemFormElName'] . '_' . $c;
1679 $cBID = $PA['itemFormElID'] . '_' . $c;
1680 $item .= ($c > 0 ? '<br />' : '') . '<input type="checkbox"' . $this->insertDefStyle('check') . ' value="1" name="' . $cBName . '"' . $cBP . $PA['onFocus'] . $disabled . ' id="' . $cBID . '" />' . $this->wrapLabels(('<label for="' . $cBID . '">' . htmlspecialchars($p[0]) . '</label>'));
1681 }
1682 }
1683 if (!$disabled) {
1684 $item .= '<input type="hidden" name="' . $PA['itemFormElName'] . '" value="' . htmlspecialchars($thisValue) . '" />';
1685 }
1686 return $item;
1687 }
1688
1689 /**
1690 * Generation of TCEform elements of the type "radio"
1691 * This will render a series of radio buttons.
1692 *
1693 * @param string $table The table name of the record
1694 * @param string $field The field name which this element is supposed to edit
1695 * @param array $row The record data array where the value(s) for the field can be found
1696 * @param array $PA An array with additional configuration options.
1697 * @return string The HTML code for the TCEform field
1698 * @todo Define visibility
1699 */
1700 public function getSingleField_typeRadio($table, $field, $row, &$PA) {
1701 $config = $PA['fieldConf']['config'];
1702 $item = '';
1703 $disabled = '';
1704 if ($this->renderReadonly || $config['readOnly']) {
1705 $disabled = ' disabled="disabled"';
1706 }
1707 // Get items for the array:
1708 $selItems = $this->initItemArray($PA['fieldConf']);
1709 if ($config['itemsProcFunc']) {
1710 $selItems = $this->procItems($selItems, $PA['fieldTSConfig']['itemsProcFunc.'], $config, $table, $row, $field);
1711 }
1712 // Traverse the items, making the form elements:
1713 $selItemsCount = count($selItems);
1714 for ($c = 0; $c < $selItemsCount; $c++) {
1715 $p = $selItems[$c];
1716 $rID = $PA['itemFormElID'] . '_' . $c;
1717 $rOnClick = implode('', $PA['fieldChangeFunc']);
1718 $rChecked = (string)$p[1] === (string)$PA['itemFormElValue'] ? ' checked="checked"' : '';
1719 $item .= '<input type="radio"' . $this->insertDefStyle('radio') . ' name="' . $PA['itemFormElName'] . '" value="' . htmlspecialchars($p[1]) . '" onclick="' . htmlspecialchars($rOnClick) . '"' . $rChecked . $PA['onFocus'] . $disabled . ' id="' . $rID . '" />
1720 <label for="' . $rID . '">' . htmlspecialchars($p[0]) . '</label>
1721 <br />';
1722 }
1723 return $item;
1724 }
1725
1726 /**
1727 * Generation of TCEform elements of the type "select"
1728 * This will render a selector box element, or possibly a special construction with two selector boxes. That depends on configuration.
1729 *
1730 * @param string $table The table name of the record
1731 * @param string $field The field name which this element is supposed to edit
1732 * @param array $row The record data array where the value(s) for the field can be found
1733 * @param array $PA An array with additional configuration options.
1734 * @return string The HTML code for the TCEform field
1735 * @todo Define visibility
1736 */
1737 public function getSingleField_typeSelect($table, $field, $row, &$PA) {
1738 // Field configuration from TCA:
1739 $config = $PA['fieldConf']['config'];
1740 $disabled = '';
1741 if ($this->renderReadonly || $config['readOnly']) {
1742 $disabled = ' disabled="disabled"';
1743 }
1744 // "Extra" configuration; Returns configuration for the field based on settings found in the "types" fieldlist. See http://typo3.org/documentation/document-library/doc_core_api/Wizards_Configuratio/.
1745 $specConf = $this->getSpecConfFromString($PA['extra'], $PA['fieldConf']['defaultExtras']);
1746 $selItems = $this->getSelectItems($table, $field, $row, $PA);
1747
1748 // Creating the label for the "No Matching Value" entry.
1749 $nMV_label = isset($PA['fieldTSConfig']['noMatchingValue_label']) ? $this->sL($PA['fieldTSConfig']['noMatchingValue_label']) : '[ ' . $this->getLL('l_noMatchingValue') . ' ]';
1750 // Prepare some values:
1751 $maxitems = (int)$config['maxitems'];
1752 // If a SINGLE selector box...
1753 if ($maxitems <= 1 && $config['renderMode'] !== 'tree') {
1754 $item = $this->getSingleField_typeSelect_single($table, $field, $row, $PA, $config, $selItems, $nMV_label);
1755 } elseif ($config['renderMode'] === 'checkbox') {
1756 // Checkbox renderMode
1757 $item = $this->getSingleField_typeSelect_checkbox($table, $field, $row, $PA, $config, $selItems, $nMV_label);
1758 } elseif ($config['renderMode'] === 'singlebox') {
1759 // Single selector box renderMode
1760 $item = $this->getSingleField_typeSelect_singlebox($table, $field, $row, $PA, $config, $selItems, $nMV_label);
1761 } elseif ($config['renderMode'] === 'tree') {
1762 // Tree renderMode
1763 $treeClass = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Form\\Element\\TreeElement', $this);
1764 $item = $treeClass->renderField($table, $field, $row, $PA, $config, $selItems, $nMV_label);
1765 // Register the required number of elements
1766 $minitems = MathUtility::forceIntegerInRange($config['minitems'], 0);
1767 $this->registerRequiredProperty('range', $PA['itemFormElName'], array($minitems, $maxitems, 'imgName' => $table . '_' . $row['uid'] . '_' . $field));
1768 } else {
1769 // Traditional multiple selector box:
1770 $item = $this->getSingleField_typeSelect_multiple($table, $field, $row, $PA, $config, $selItems, $nMV_label);
1771 }
1772 // Wizards:
1773 if (!$disabled) {
1774 $altItem = '<input type="hidden" name="' . $PA['itemFormElName'] . '" value="' . htmlspecialchars($PA['itemFormElValue']) . '" />';
1775 $item = $this->renderWizards(array($item, $altItem), $config['wizards'], $table, $row, $field, $PA, $PA['itemFormElName'], $specConf);
1776 }
1777 return $item;
1778 }
1779
1780 /**
1781 * Collects the items for a select field by reading the configured
1782 * select items from the configuration and / or by collecting them
1783 * from a foreign table.
1784 *
1785 * @param string $table The table name of the record
1786 * @param string $fieldName The select field name
1787 * @param array $row The record data array where the value(s) for the field can be found
1788 * @param array $PA An array with additional configuration options.
1789 * @return array
1790 */
1791 public function getSelectItems($table, $fieldName, array $row, array $PA) {
1792 $config = $PA['fieldConf']['config'];
1793
1794 // Getting the selector box items from the system
1795 $selectItems = $this->addSelectOptionsToItemArray(
1796 $this->initItemArray($PA['fieldConf']),
1797 $PA['fieldConf'],
1798 $this->setTSconfig($table, $row),
1799 $fieldName
1800 );
1801
1802 // Possibly filter some items:
1803 $selectItems = GeneralUtility::keepItemsInArray(
1804 $selectItems,
1805 $PA['fieldTSConfig']['keepItems'],
1806 function ($value) {
1807 return $value[1];
1808 }
1809 );
1810
1811 // Possibly add some items:
1812 $selectItems = $this->addItems($selectItems, $PA['fieldTSConfig']['addItems.']);
1813
1814 // Process items by a user function:
1815 if (isset($config['itemsProcFunc']) && $config['itemsProcFunc']) {
1816 $selectItems = $this->procItems($selectItems, $PA['fieldTSConfig']['itemsProcFunc.'], $config, $table, $row, $fieldName);
1817 }
1818
1819 // Possibly remove some items:
1820 $removeItems = GeneralUtility::trimExplode(',', $PA['fieldTSConfig']['removeItems'], TRUE);
1821 foreach ($selectItems as $selectItemIndex => $selectItem) {
1822
1823 // Checking languages and authMode:
1824 $languageDeny = FALSE;
1825 if (
1826 !empty($GLOBALS['TCA'][$table]['ctrl']['languageField'])
1827 && $GLOBALS['TCA'][$table]['ctrl']['languageField'] === $fieldName
1828 && !$GLOBALS['BE_USER']->checkLanguageAccess($selectItem[1])
1829 ) {
1830 $languageDeny = TRUE;
1831 }
1832
1833 $authModeDeny = FALSE;
1834 if (
1835 ($config['form_type'] === 'select')
1836 && $config['authMode']
1837 && !$GLOBALS['BE_USER']->checkAuthMode($table, $fieldName, $selectItem[1], $config['authMode'])
1838 ) {
1839 $authModeDeny = TRUE;
1840 }
1841
1842 if (in_array($selectItem[1], $removeItems) || $languageDeny || $authModeDeny) {
1843 unset($selectItems[$selectItemIndex]);
1844 } elseif (isset($PA['fieldTSConfig']['altLabels.'][$selectItem[1]])) {
1845 $selectItems[$selectItemIndex][0] = htmlspecialchars($this->sL($PA['fieldTSConfig']['altLabels.'][$selectItem[1]]));
1846 }
1847
1848 // Removing doktypes with no access:
1849 if (($table === 'pages' || $table === 'pages_language_overlay') && $fieldName === 'doktype') {
1850 if (!($GLOBALS['BE_USER']->isAdmin() || GeneralUtility::inList($GLOBALS['BE_USER']->groupData['pagetypes_select'], $selectItem[1]))) {
1851 unset($selectItems[$selectItemIndex]);
1852 }
1853 }
1854 }
1855
1856 return $selectItems;
1857 }
1858
1859 /**
1860 * Creates a single-selector box
1861 * (Render function for getSingleField_typeSelect())
1862 *
1863 * @param string $table See getSingleField_typeSelect()
1864 * @param string $field See getSingleField_typeSelect()
1865 * @param array $row See getSingleField_typeSelect()
1866 * @param array $PA See getSingleField_typeSelect()
1867 * @param array $config (Redundant) content of $PA['fieldConf']['config'] (for convenience)
1868 * @param array $selItems Items available for selection
1869 * @param string $nMV_label Label for no-matching-value
1870 * @return string The HTML code for the item
1871 * @see getSingleField_typeSelect()
1872 * @todo Define visibility
1873 */
1874 public function getSingleField_typeSelect_single($table, $field, $row, &$PA, $config, $selItems, $nMV_label) {
1875 // check against inline uniqueness
1876 $inlineParent = $this->inline->getStructureLevel(-1);
1877 if (is_array($inlineParent) && $inlineParent['uid']) {
1878 if ($inlineParent['config']['foreign_table'] == $table && $inlineParent['config']['foreign_unique'] == $field) {
1879 $uniqueIds = $this->inline->inlineData['unique'][$this->inline->inlineNames['object'] . InlineElement::Structure_Separator . $table]['used'];
1880 $PA['fieldChangeFunc']['inlineUnique'] = 'inline.updateUnique(this,\'' . $this->inline->inlineNames['object'] . InlineElement::Structure_Separator . $table . '\',\'' . $this->inline->inlineNames['form'] . '\',\'' . $row['uid'] . '\');';
1881 }
1882 // hide uid of parent record for symmetric relations
1883 if ($inlineParent['config']['foreign_table'] == $table && ($inlineParent['config']['foreign_field'] == $field || $inlineParent['config']['symmetric_field'] == $field)) {
1884 $uniqueIds[] = $inlineParent['uid'];
1885 }
1886 }
1887 // Initialization:
1888 $c = 0;
1889 $sI = 0;
1890 $noMatchingValue = 1;
1891 $opt = array();
1892 $selicons = array();
1893 $onlySelectedIconShown = 0;
1894 $size = (int)$config['size'];
1895 // Style set on <select/>
1896 $selectedStyle = '';
1897 $item = '';
1898 $disabled = '';
1899 // TODO: icon is used but never assigned
1900 $onChangeIcon = '';
1901 if ($this->renderReadonly || $config['readOnly']) {
1902 $disabled = ' disabled="disabled"';
1903 $onlySelectedIconShown = 1;
1904 }
1905 // Register as required if minitems is greater than zero
1906 if (($minItems = MathUtility::forceIntegerInRange($config['minitems'], 0)) > 0) {
1907 $this->registerRequiredProperty('field', $table . '_' . $row['uid'] . '_' . $field, $PA['itemFormElName']);
1908 }
1909
1910 // Icon configuration:
1911 if ($config['suppress_icons'] == 'IF_VALUE_FALSE') {
1912 $suppressIcons = !$PA['itemFormElValue'] ? 1 : 0;
1913 } elseif ($config['suppress_icons'] == 'ONLY_SELECTED') {
1914 $suppressIcons = 0;
1915 $onlySelectedIconShown = 1;
1916 } elseif ($config['suppress_icons']) {
1917 $suppressIcons = 1;
1918 } else {
1919 $suppressIcons = 0;
1920 }
1921 // Traverse the Array of selector box items:
1922 $optGroupStart = array();
1923 $optGroupOpen = FALSE;
1924 $classesForSelectTag = array();
1925 foreach ($selItems as $p) {
1926 $sM = (string)$PA['itemFormElValue'] === (string)$p[1] ? ' selected="selected"' : '';
1927 if ($sM) {
1928 $sI = $c;
1929 $noMatchingValue = 0;
1930 }
1931 // Getting style attribute value (for icons):
1932 if ($config['iconsInOptionTags']) {
1933 $styleAttrValue = $this->optionTagStyle($p[2]);
1934 if ($sM) {
1935 list($selectIconFile, $selectIconInfo) = $this->getIcon($p[2]);
1936 if (!empty($selectIconInfo)) {
1937 $selectedStyle = ' style="background-image:url(' . $selectIconFile . ');"';
1938 $classesForSelectTag[] = 'typo3-TCEforms-select-selectedItemWithBackgroundImage';
1939 }
1940 }
1941 }
1942 // Compiling the <option> tag:
1943 if (!($p[1] != $PA['itemFormElValue'] && is_array($uniqueIds) && in_array($p[1], $uniqueIds))) {
1944 if ($p[1] === '--div--') {
1945 $optGroupStart[0] = $p[0];
1946 if ($config['iconsInOptionTags']) {
1947 $optGroupStart[1] = $this->optgroupTagStyle($p[2]);
1948 } else {
1949 $optGroupStart[1] = $styleAttrValue;
1950 }
1951 } else {
1952 if (count($optGroupStart)) {
1953 // Closing last optgroup before next one starts
1954 if ($optGroupOpen) {
1955 $opt[] = '</optgroup>' . LF;
1956 }
1957 $opt[] = '<optgroup label="' . htmlspecialchars($optGroupStart[0], ENT_COMPAT, 'UTF-8', FALSE) . '"' . ($optGroupStart[1] ? ' style="' . htmlspecialchars($optGroupStart[1]) . '"' : '') . ' class="c-divider">' . LF;
1958 $optGroupOpen = TRUE;
1959 $c--;
1960 $optGroupStart = array();
1961 }
1962 $opt[] = '<option value="' . htmlspecialchars($p[1]) . '"' . $sM . ($styleAttrValue ? ' style="' . htmlspecialchars($styleAttrValue) . '"' : '') . '>' . htmlspecialchars($p[0], ENT_COMPAT, 'UTF-8', FALSE) . '</option>' . LF;
1963 }
1964 }
1965 // If there is an icon for the selector box (rendered in selicon-table below)...:
1966 // if there is an icon ($p[2]), icons should be shown, and, if only selected are visible, is it selected
1967 if ($p[2] && !$suppressIcons && (!$onlySelectedIconShown || $sM)) {
1968 list($selIconFile, $selIconInfo) = $this->getIcon($p[2]);
1969 if (!empty($selIconInfo)) {
1970 $iOnClick = $this->elName($PA['itemFormElName']) . '.selectedIndex=' . $c . '; ' . $this->elName($PA['itemFormElName']) . '.style.backgroundImage=' . $this->elName($PA['itemFormElName']) . '.options[' . $c . '].style.backgroundImage; ' . implode('', $PA['fieldChangeFunc']) . 'this.blur(); return false;';
1971 } else {
1972 $iOnClick = $this->elName($PA['itemFormElName']) . '.selectedIndex=' . $c . '; ' . $this->elName($PA['itemFormElName']) . '.className=' . $this->elName($PA['itemFormElName']) . '.options[' . $c . '].className; ' . implode('', $PA['fieldChangeFunc']) . 'this.blur(); return false;';
1973 }
1974 $selicons[] = array(
1975 (!$onlySelectedIconShown ? '<a href="#" onclick="' . htmlspecialchars($iOnClick) . '">' : '') . $this->getIconHtml($p[2], htmlspecialchars($p[0]), htmlspecialchars($p[0])) . (!$onlySelectedIconShown ? '</a>' : ''),
1976 $c,
1977 $sM
1978 );
1979 }
1980 $c++;
1981 }
1982 // Closing optgroup if open
1983 if ($optGroupOpen) {
1984 $opt[] = '</optgroup>';
1985 }
1986 // No-matching-value:
1987 if ($PA['itemFormElValue'] && $noMatchingValue && !$PA['fieldTSConfig']['disableNoMatchingValueElement'] && !$config['disableNoMatchingValueElement']) {
1988 $nMV_label = @sprintf($nMV_label, $PA['itemFormElValue']);
1989 $opt[] = '<option value="' . htmlspecialchars($PA['itemFormElValue']) . '" selected="selected">' . htmlspecialchars($nMV_label) . '</option>';
1990 }
1991 // Create item form fields:
1992 $sOnChange = 'if (this.options[this.selectedIndex].value==\'--div--\') {this.selectedIndex=' . $sI . ';} ' . implode('', $PA['fieldChangeFunc']);
1993 if (!$disabled) {
1994 // MUST be inserted before the selector - else is the value of the hiddenfield here mysteriously submitted...
1995 $item .= '<input type="hidden" name="' . $PA['itemFormElName'] . '_selIconVal" value="' . htmlspecialchars($sI) . '" />';
1996 }
1997 if ($config['iconsInOptionTags']) {
1998 $classesForSelectTag[] = 'icon-select';
1999 }
2000 $item .= '<select' . $selectedStyle . ' id="' . uniqid('tceforms-select-') . '" name="' . $PA['itemFormElName'] . '"' . $this->insertDefStyle('select', implode(' ', $classesForSelectTag)) . ($size ? ' size="' . $size . '"' : '') . ' onchange="' . htmlspecialchars(($onChangeIcon . $sOnChange)) . '"' . $PA['onFocus'] . $disabled . '>';
2001 $item .= implode('', $opt);
2002 $item .= '</select>';
2003 // Create icon table:
2004 if (count($selicons) && !$config['noIconsBelowSelect']) {
2005 $item .= '<table border="0" cellpadding="0" cellspacing="0" class="typo3-TCEforms-selectIcons">';
2006 $selicon_cols = (int)$config['selicon_cols'];
2007 if (!$selicon_cols) {
2008 $selicon_cols = count($selicons);
2009 }
2010 $sR = ceil(count($selicons) / $selicon_cols);
2011 $selicons = array_pad($selicons, $sR * $selicon_cols, '');
2012 for ($sa = 0; $sa < $sR; $sa++) {
2013 $item .= '<tr>';
2014 for ($sb = 0; $sb < $selicon_cols; $sb++) {
2015 $sk = $sa * $selicon_cols + $sb;
2016 $imgN = 'selIcon_' . $table . '_' . $row['uid'] . '_' . $field . '_' . $selicons[$sk][1];
2017 $imgS = $selicons[$sk][2] ? $this->backPath . 'gfx/content_selected.gif' : 'clear.gif';
2018 $item .= '<td><img name="' . htmlspecialchars($imgN) . '" src="' . $imgS . '" width="7" height="10" alt="" /></td>';
2019 $item .= '<td>' . $selicons[$sk][0] . '</td>';
2020 }
2021 $item .= '</tr>';
2022 }
2023 $item .= '</table>';
2024 }
2025 return $item;
2026 }
2027
2028 /**
2029 * Creates a checkbox list (renderMode = "checkbox")
2030 * (Render function for getSingleField_typeSelect())
2031 *
2032 * @param string $table See getSingleField_typeSelect()
2033 * @param string $field See getSingleField_typeSelect()
2034 * @param array $row See getSingleField_typeSelect()
2035 * @param array $PA See getSingleField_typeSelect()
2036 * @param array $config (Redundant) content of $PA['fieldConf']['config'] (for convenience)
2037 * @param array $selItems Items available for selection
2038 * @param string $nMV_label Label for no-matching-value
2039 * @return string The HTML code for the item
2040 * @see getSingleField_typeSelect()
2041 * @todo Define visibility
2042 */
2043 public function getSingleField_typeSelect_checkbox($table, $field, $row, &$PA, $config, $selItems, $nMV_label) {
2044 if (empty($selItems)) {
2045 return '';
2046 }
2047 // Get values in an array (and make unique, which is fine because there can be no duplicates anyway):
2048 $itemArray = array_flip($this->extractValuesOnlyFromValueLabelList($PA['itemFormElValue']));
2049 $item = '';
2050 $disabled = '';
2051 if ($this->renderReadonly || $config['readOnly']) {
2052 $disabled = ' disabled="disabled"';
2053 }
2054 // Traverse the Array of selector box items:
2055 $tRows = array();
2056 $c = 0;
2057 if (!$disabled) {
2058 $sOnChange = implode('', $PA['fieldChangeFunc']);
2059 // Used to accumulate the JS needed to restore the original selection.
2060 $setAll = array();
2061 $unSetAll = array();
2062 foreach ($selItems as $p) {
2063 // Non-selectable element:
2064 if ($p[1] === '--div--') {
2065 $selIcon = '';
2066 if (isset($p[2]) && $p[2] != 'empty-emtpy') {
2067 $selIcon = $this->getIconHtml($p[2]);
2068 }
2069 $tRows[] = '
2070 <tr class="c-header">
2071 <td colspan="3">' . $selIcon . htmlspecialchars($p[0]) . '</td>
2072 </tr>';
2073 } else {
2074 // Selected or not by default:
2075 $sM = '';
2076 if (isset($itemArray[$p[1]])) {
2077 $sM = ' checked="checked"';
2078 unset($itemArray[$p[1]]);
2079 }
2080 // Icon:
2081 if ($p[2]) {
2082 $selIcon = $p[2];
2083 } else {
2084 $selIcon = IconUtility::getSpriteIcon('empty-empty');
2085 }
2086 // Compile row:
2087 $rowId = uniqid('select_checkbox_row_');
2088 $onClickCell = $this->elName(($PA['itemFormElName'] . '[' . $c . ']')) . '.checked=!' . $this->elName(($PA['itemFormElName'] . '[' . $c . ']')) . '.checked;';
2089 $onClick = 'this.attributes.getNamedItem("class").nodeValue = ' . $this->elName(($PA['itemFormElName'] . '[' . $c . ']')) . '.checked ? "c-selectedItem" : "c-unselectedItem";';
2090 $setAll[] = $this->elName(($PA['itemFormElName'] . '[' . $c . ']')) . '.checked=1;';
2091 $setAll[] .= '$(\'' . $rowId . '\').removeClassName(\'c-unselectedItem\');$(\'' . $rowId . '\').addClassName(\'c-selectedItem\');';
2092 $unSetAll[] = $this->elName(($PA['itemFormElName'] . '[' . $c . ']')) . '.checked=0;';
2093 $unSetAll[] .= '$(\'' . $rowId . '\').removeClassName(\'c-selectedItem\');$(\'' . $rowId . '\').addClassName(\'c-unselectedItem\');';
2094 $restoreCmd[] = $this->elName(($PA['itemFormElName'] . '[' . $c . ']')) . '.checked=' . ($sM ? 1 : 0) . ';' . '$(\'' . $rowId . '\').removeClassName(\'c-selectedItem\');$(\'' . $rowId . '\').removeClassName(\'c-unselectedItem\');' . '$(\'' . $rowId . '\').addClassName(\'c-' . ($sM ? '' : 'un') . 'selectedItem\');';
2095 // Check if some help text is available
2096 // Since TYPO3 4.5 help text is expected to be an associative array
2097 // with two key, "title" and "description"
2098 // For the sake of backwards compatibility, we test if the help text
2099 // is a string and use it as a description (this could happen if items
2100 // are modified with an itemProcFunc)
2101 $hasHelp = FALSE;
2102 $help = '';
2103 $helpArray = array();
2104 if (is_array($p[3]) && count($p[3]) > 0 || !empty($p[3])) {
2105 $hasHelp = TRUE;
2106 if (is_array($p[3])) {
2107 $helpArray = $p[3];
2108 } else {
2109 $helpArray['description'] = $p[3];
2110 }
2111 }
2112 $label = htmlspecialchars($p[0], ENT_COMPAT, 'UTF-8', FALSE);
2113 if ($hasHelp) {
2114 $help = BackendUtility::wrapInHelp('', '', '', $helpArray);
2115 }
2116 $tRows[] = '
2117 <tr id="' . $rowId . '" class="' . ($sM ? 'c-selectedItem' : 'c-unselectedItem') . '" onclick="' . htmlspecialchars($onClick) . '" style="cursor: pointer;">
2118 <td class="c-checkbox"><input type="checkbox"' . $this->insertDefStyle('check') . ' name="' . htmlspecialchars(($PA['itemFormElName'] . '[' . $c . ']')) . '" value="' . htmlspecialchars($p[1]) . '"' . $sM . ' onclick="' . htmlspecialchars($sOnChange) . '"' . $PA['onFocus'] . ' /></td>
2119 <td class="c-labelCell" onclick="' . htmlspecialchars($onClickCell) . '">' . $this->getIconHtml($selIcon) . $label . '</td>
2120 <td class="c-descr" onclick="' . htmlspecialchars($onClickCell) . '">' . (empty($help) ? '' : $help) . '</td>
2121 </tr>';
2122 $c++;
2123 }
2124 }
2125 }
2126 // Remaining values (invalid):
2127 if (count($itemArray) && !$PA['fieldTSConfig']['disableNoMatchingValueElement'] && !$config['disableNoMatchingValueElement']) {
2128 foreach ($itemArray as $theNoMatchValue => $temp) {
2129 // Compile <checkboxes> tag:
2130 array_unshift($tRows, '
2131 <tr class="c-invalidItem">
2132 <td class="c-checkbox"><input type="checkbox"' . $this->insertDefStyle('check') . ' name="' . htmlspecialchars(($PA['itemFormElName'] . '[' . $c . ']')) . '" value="' . htmlspecialchars($theNoMatchValue) . '" checked="checked" onclick="' . htmlspecialchars($sOnChange) . '"' . $PA['onFocus'] . $disabled . ' /></td>
2133 <td class="c-labelCell">' . htmlspecialchars(@sprintf($nMV_label, $theNoMatchValue), ENT_COMPAT, 'UTF-8', FALSE) . '</td><td>&nbsp;</td>
2134 </tr>');
2135 $c++;
2136 }
2137 }
2138 // Add an empty hidden field which will send a blank value if all items are unselected.
2139 $item .= '<input type="hidden" class="select-checkbox" name="' . htmlspecialchars($PA['itemFormElName']) . '" value="" />';
2140 // Remaining checkboxes will get their set-all link:
2141 if (count($setAll)) {
2142 $tableHead = '<thead>
2143 <tr class="c-header-checkbox-controls t3-row-header">
2144 <td class="c-checkbox">
2145 <input type="checkbox" class="checkbox" onclick="if (checked) {' . htmlspecialchars((implode('', $setAll) . '} else {' . implode('', $unSetAll) . '}')) . '">
2146 </td>
2147 <td colspan="2">
2148 </td>
2149 </tr></thead>';
2150 }
2151 // Implode rows in table:
2152 $item .= '
2153 <table border="0" cellpadding="0" cellspacing="0" class="typo3-TCEforms-select-checkbox">' . $tableHead . '<tbody>' . implode('', $tRows) . '</tbody>
2154 </table>
2155 ';
2156 // Add revert icon
2157 if (is_array($restoreCmd)) {
2158 $item .= '<a href="#" onclick="' . implode('', $restoreCmd) . ' return false;' . '">' . IconUtility::getSpriteIcon('actions-edit-undo', array('title' => htmlspecialchars($this->getLL('l_revertSelection')))) . '</a>';
2159 }
2160 return $item;
2161 }
2162
2163 /**
2164 * Creates a selectorbox list (renderMode = "singlebox")
2165 * (Render function for getSingleField_typeSelect())
2166 *
2167 * @param string $table See getSingleField_typeSelect()
2168 * @param string $field See getSingleField_typeSelect()
2169 * @param array $row See getSingleField_typeSelect()
2170 * @param array $PA See getSingleField_typeSelect()
2171 * @param array $config (Redundant) content of $PA['fieldConf']['config'] (for convenience)
2172 * @param array $selItems Items available for selection
2173 * @param string $nMV_label Label for no-matching-value
2174 * @return string The HTML code for the item
2175 * @see getSingleField_typeSelect()
2176 * @todo Define visibility
2177 */
2178 public function getSingleField_typeSelect_singlebox($table, $field, $row, &$PA, $config, $selItems, $nMV_label) {
2179 // Get values in an array (and make unique, which is fine because there can be no duplicates anyway):
2180 $itemArray = array_flip($this->extractValuesOnlyFromValueLabelList($PA['itemFormElValue']));
2181 $item = '';
2182 $disabled = '';
2183 if ($this->renderReadonly || $config['readOnly']) {
2184 $disabled = ' disabled="disabled"';
2185 }
2186 // Traverse the Array of selector box items:
2187 $opt = array();
2188 // Used to accumulate the JS needed to restore the original selection.
2189 $restoreCmd = array();
2190 $c = 0;
2191 foreach ($selItems as $p) {
2192 // Selected or not by default:
2193 $sM = '';
2194 if (isset($itemArray[$p[1]])) {
2195 $sM = ' selected="selected"';
2196 $restoreCmd[] = $this->elName(($PA['itemFormElName'] . '[]')) . '.options[' . $c . '].selected=1;';
2197 unset($itemArray[$p[1]]);
2198 }
2199 // Non-selectable element:
2200 $nonSel = '';
2201 if ((string) $p[1] === '--div--') {
2202 $nonSel = ' onclick="this.selected=0;" class="c-divider"';
2203 }
2204 // Icon style for option tag:
2205 if ($config['iconsInOptionTags']) {
2206 $styleAttrValue = $this->optionTagStyle($p[2]);
2207 }
2208 // Compile <option> tag:
2209 $opt[] = '<option value="' . htmlspecialchars($p[1]) . '"' . $sM . $nonSel . ($styleAttrValue ? ' style="' . htmlspecialchars($styleAttrValue) . '"' : '') . '>' . htmlspecialchars($p[0], ENT_COMPAT, 'UTF-8', FALSE) . '</option>';
2210 $c++;
2211 }
2212 // Remaining values:
2213 if (count($itemArray) && !$PA['fieldTSConfig']['disableNoMatchingValueElement'] && !$config['disableNoMatchingValueElement']) {
2214 foreach ($itemArray as $theNoMatchValue => $temp) {
2215 // Compile <option> tag:
2216 array_unshift($opt, '<option value="' . htmlspecialchars($theNoMatchValue) . '" selected="selected">' . htmlspecialchars(@sprintf($nMV_label, $theNoMatchValue), ENT_COMPAT, 'UTF-8', FALSE) . '</option>');
2217 }
2218 }
2219 // Compile selector box:
2220 $sOnChange = implode('', $PA['fieldChangeFunc']);
2221 $selector_itemListStyle = isset($config['itemListStyle']) ? ' style="' . htmlspecialchars($config['itemListStyle']) . '"' : ' style="' . $this->defaultMultipleSelectorStyle . '"';
2222 $size = (int)$config['size'];
2223 $cssPrefix = $size === 1 ? 'tceforms-select' : 'tceforms-multiselect';
2224 $size = $config['autoSizeMax'] ? MathUtility::forceIntegerInRange(count($selItems) + 1, MathUtility::forceIntegerInRange($size, 1), $config['autoSizeMax']) : $size;
2225 $selectBox = '<select id="' . uniqid($cssPrefix) . '" name="' . $PA['itemFormElName'] . '[]"' . $this->insertDefStyle('select', $cssPrefix) . ($size ? ' size="' . $size . '"' : '') . ' multiple="multiple" onchange="' . htmlspecialchars($sOnChange) . '"' . $PA['onFocus'] . $selector_itemListStyle . $disabled . '>
2226 ' . implode('
2227 ', $opt) . '
2228 </select>';
2229 // Add an empty hidden field which will send a blank value if all items are unselected.
2230 if (!$disabled) {
2231 $item .= '<input type="hidden" name="' . htmlspecialchars($PA['itemFormElName']) . '" value="" />';
2232 }
2233 // Put it all into a table:
2234 $item .= '
2235 <table border="0" cellspacing="0" cellpadding="0" width="1" class="typo3-TCEforms-select-singlebox">
2236 <tr>
2237 <td>
2238 ' . $selectBox . '
2239 <br/>
2240 <em>' . htmlspecialchars($this->getLL('l_holdDownCTRL')) . '</em>
2241 </td>
2242 <td valign="top">
2243 <a href="#" onclick="' . htmlspecialchars(($this->elName(($PA['itemFormElName'] . '[]')) . '.selectedIndex=-1;' . implode('', $restoreCmd) . ' return false;')) . '" title="' . htmlspecialchars($this->getLL('l_revertSelection')) . '">' . IconUtility::getSpriteIcon('actions-edit-undo') . '</a>
2244 </td>
2245 </tr>
2246 </table>
2247 ';
2248 return $item;
2249 }
2250
2251 /**
2252 * Creates a multiple-selector box (two boxes, side-by-side)
2253 * (Render function for getSingleField_typeSelect())
2254 *
2255 * @param string $table See getSingleField_typeSelect()
2256 * @param string $field See getSingleField_typeSelect()
2257 * @param array $row See getSingleField_typeSelect()
2258 * @param array $PA See getSingleField_typeSelect()
2259 * @param array $config (Redundant) content of $PA['fieldConf']['config'] (for convenience)
2260 * @param array $selItems Items available for selection
2261 * @param string $nMV_label Label for no-matching-value
2262 * @return string The HTML code for the item
2263 * @see getSingleField_typeSelect()
2264 * @todo Define visibility
2265 */
2266 public function getSingleField_typeSelect_multiple($table, $field, $row, &$PA, $config, $selItems, $nMV_label) {
2267 $item = '';
2268 $disabled = '';
2269 if ($this->renderReadonly || $config['readOnly']) {
2270 $disabled = ' disabled="disabled"';
2271 }
2272 // Setting this hidden field (as a flag that JavaScript can read out)
2273 if (!$disabled) {
2274 $item .= '<input type="hidden" name="' . $PA['itemFormElName'] . '_mul" value="' . ($config['multiple'] ? 1 : 0) . '" />';
2275 }
2276 // Set max and min items:
2277 $maxitems = MathUtility::forceIntegerInRange($config['maxitems'], 0);
2278 if (!$maxitems) {
2279 $maxitems = 100000;
2280 }
2281 $minitems = MathUtility::forceIntegerInRange($config['minitems'], 0);
2282 // Register the required number of elements:
2283 $this->registerRequiredProperty('range', $PA['itemFormElName'], array($minitems, $maxitems, 'imgName' => $table . '_' . $row['uid'] . '_' . $field));
2284 // Get "removeItems":
2285 $removeItems = GeneralUtility::trimExplode(',', $PA['fieldTSConfig']['removeItems'], TRUE);
2286 // Get the array with selected items:
2287 $itemArray = GeneralUtility::trimExplode(',', $PA['itemFormElValue'], TRUE);
2288
2289 // Possibly filter some items:
2290 $itemArray = GeneralUtility::keepItemsInArray(
2291 $itemArray,
2292 $PA['fieldTSConfig']['keepItems'],
2293 function ($value) {
2294 $parts = explode('|', $value, 2);
2295 return rawurldecode($parts[0]);
2296 }
2297 );
2298
2299 // Perform modification of the selected items array:
2300 foreach ($itemArray as $tk => $tv) {
2301 $tvP = explode('|', $tv, 2);
2302 $evalValue = $tvP[0];
2303 $isRemoved = in_array($evalValue, $removeItems) || $config['form_type'] == 'select' && $config['authMode'] && !$GLOBALS['BE_USER']->checkAuthMode($table, $field, $evalValue, $config['authMode']);
2304 if ($isRemoved && !$PA['fieldTSConfig']['disableNoMatchingValueElement'] && !$config['disableNoMatchingValueElement']) {
2305 $tvP[1] = rawurlencode(@sprintf($nMV_label, $evalValue));
2306 } elseif (isset($PA['fieldTSConfig']['altLabels.'][$evalValue])) {
2307 $tvP[1] = rawurlencode($this->sL($PA['fieldTSConfig']['altLabels.'][$evalValue]));
2308 }
2309 if ($tvP[1] == '') {
2310 // Case: flexform, default values supplied, no label provided (bug #9795)
2311 foreach ($selItems as $selItem) {
2312 if ($selItem[1] == $tvP[0]) {
2313 $tvP[1] = html_entity_decode($selItem[0]);
2314 break;
2315 }
2316 }
2317 }
2318 $itemArray[$tk] = implode('|', $tvP);
2319 }
2320 $itemsToSelect = '';
2321 $filterTextfield = '';
2322 $filterSelectbox = '';
2323 if (!$disabled) {
2324 // Create option tags:
2325 $opt = array();
2326 $styleAttrValue = '';
2327 foreach ($selItems as $p) {
2328 if ($config['iconsInOptionTags']) {
2329 $styleAttrValue = $this->optionTagStyle($p[2]);
2330 }
2331 $opt[] = '<option value="' . htmlspecialchars($p[1]) . '"' . ($styleAttrValue ? ' style="' . htmlspecialchars($styleAttrValue) . '"' : '') . ' title="' . $p[0] . '">' . $p[0] . '</option>';
2332 }
2333 // Put together the selector box:
2334 $selector_itemListStyle = isset($config['itemListStyle']) ? ' style="' . htmlspecialchars($config['itemListStyle']) . '"' : ' style="' . $this->defaultMultipleSelectorStyle . '"';
2335 $size = (int)$config['size'];
2336 $size = $config['autoSizeMax'] ? MathUtility::forceIntegerInRange(count($itemArray) + 1, MathUtility::forceIntegerInRange($size, 1), $config['autoSizeMax']) : $size;
2337 $sOnChange = implode('', $PA['fieldChangeFunc']);
2338
2339 $multiSelectId = uniqid('tceforms-multiselect-');
2340 $itemsToSelect = '
2341 <select data-relatedfieldname="' . htmlspecialchars($PA['itemFormElName']) . '" data-exclusivevalues="' . htmlspecialchars($config['exclusiveKeys']) . '" id="' . $multiSelectId . '" name="' . $PA['itemFormElName'] . '_sel"' . $this->insertDefStyle('select', 'tceforms-multiselect tceforms-itemstoselect t3-form-select-itemstoselect') . ($size ? ' size="' . $size . '"' : '') . ' onchange="' . htmlspecialchars($sOnChange) . '"' . $PA['onFocus'] . $selector_itemListStyle . '>
2342 ' . implode('
2343 ', $opt) . '
2344 </select>';
2345
2346 if ($config['enableMultiSelectFilterTextfield'] || $config['multiSelectFilterItems']) {
2347 $this->multiSelectFilterCount++;
2348 $jsSelectBoxFilterName = str_replace(' ', '', ucwords(str_replace('-', ' ', GeneralUtility::strtolower($multiSelectId))));
2349 $this->additionalJS_post[] = '
2350 var '. $jsSelectBoxFilterName . ' = new TCEForms.SelectBoxFilter("' . $multiSelectId . '");
2351 ';
2352 }
2353
2354 if ($config['enableMultiSelectFilterTextfield']) {
2355 // add input field for filter
2356 $filterTextfield = '<input class="typo3-TCEforms-suggest-search typo3-TCEforms-multiselect-filter" id="' . $multiSelectId . '_filtertextfield" value="" style="width: 104px;" />';
2357 }
2358
2359 if (isset($config['multiSelectFilterItems']) && is_array($config['multiSelectFilterItems']) && count($config['multiSelectFilterItems']) > 1) {
2360 $filterDropDownOptions = array();
2361 foreach ($config['multiSelectFilterItems'] as $optionElement) {
2362 $filterDropDownOptions[] = '<option value="' . htmlspecialchars($this->sL($optionElement[0])) . '">' . htmlspecialchars((isset($optionElement[1]) && $optionElement[1] != '') ? $this->sL($optionElement[1]) : $this->sL($optionElement[0])) . '</option>';
2363 }
2364 $filterSelectbox = '
2365 <select id="' . $multiSelectId . '_filterdropdown">
2366 ' . implode('
2367 ', $filterDropDownOptions) . '
2368 </select>';
2369 }
2370 }
2371 // Pass to "dbFileIcons" function:
2372 $params = array(
2373 'size' => $size,
2374 'autoSizeMax' => MathUtility::forceIntegerInRange($config['autoSizeMax'], 0),
2375 'style' => isset($config['selectedListStyle']) ? ' style="' . htmlspecialchars($config['selectedListStyle']) . '"' : ' style="' . $this->defaultMultipleSelectorStyle . '"',
2376 'dontShowMoveIcons' => $maxitems <= 1,
2377 'maxitems' => $maxitems,
2378 'info' => '',
2379 'headers' => array(
2380 'selector' => $this->getLL('l_selected') . ':<br />',
2381 'items' => $this->getLL('l_items') . ': ' . $filterSelectbox . $filterTextfield . '<br />'
2382 ),
2383 'noBrowser' => 1,
2384 'thumbnails' => $itemsToSelect,
2385 'readOnly' => $disabled
2386 );
2387 $item .= $this->dbFileIcons($PA['itemFormElName'], '', '', $itemArray, '', $params, $PA['onFocus']);
2388 return $item;
2389 }
2390
2391 /**
2392 * Generation of TCEform elements of the type "group"
2393 * This will render a selectorbox into which elements from either the file system or database can be inserted. Relations.
2394 *
2395 * @param string $table The table name of the record
2396 * @param string $field The field name which this element is supposed to edit
2397 * @param array $row The record data array where the value(s) for the field can be found
2398 * @param array $PA An array with additional configuration options.
2399 * @return string The HTML code for the TCEform field
2400 * @todo Define visibility
2401 */
2402 public function getSingleField_typeGroup($table, $field, $row, &$PA) {
2403 // Init:
2404 $config = $PA['fieldConf']['config'];
2405 $show_thumbs = $config['show_thumbs'];
2406 $size = isset($config['size']) ? (int)$config['size'] : 5;
2407 $maxitems = MathUtility::forceIntegerInRange($config['maxitems'], 1);
2408 $minitems = MathUtility::forceIntegerInRange($config['minitems'], 0);
2409 $allowed = trim($config['allowed']);
2410 $disallowed = trim($config['disallowed']);
2411 $item = '';
2412 $disabled = '';
2413 if ($this->renderReadonly || $config['readOnly']) {
2414 $disabled = ' disabled="disabled"';
2415 }
2416 $item .= '<input type="hidden" name="' . $PA['itemFormElName'] . '_mul" value="' . ($config['multiple'] ? 1 : 0) . '"' . $disabled . ' />';
2417 $this->registerRequiredProperty('range', $PA['itemFormElName'], array($minitems, $maxitems, 'imgName' => $table . '_' . $row['uid'] . '_' . $field));
2418 $info = '';
2419 // "Extra" configuration; Returns configuration for the field based on settings found in the "types" fieldlist. See http://typo3.org/documentation/document-library/doc_core_api/Wizards_Configuratio/.
2420 $specConf = $this->getSpecConfFromString($PA['extra'], $PA['fieldConf']['defaultExtras']);
2421 $PA['itemFormElID_file'] = $PA['itemFormElID'] . '_files';
2422 // whether the list and delete controls should be disabled
2423 $noList = isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'list');
2424 $noDelete = isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'delete');
2425 // if maxitems==1 then automatically replace the current item (in list and file selector)
2426 if ($maxitems === 1) {
2427 $this->additionalJS_post[] = 'TBE_EDITOR.clearBeforeSettingFormValueFromBrowseWin[\'' . $PA['itemFormElName'] . '\'] = {
2428 itemFormElID_file: \'' . $PA['itemFormElID_file'] . '\'
2429 }';
2430 $PA['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] = 'setFormValueManipulate(\'' . $PA['itemFormElName'] . '\', \'Remove\'); ' . $PA['fieldChangeFunc']['TBE_EDITOR_fieldChanged'];
2431 } elseif ($noList) {
2432 // If the list controls have been removed and the maximum number is reached, remove the first entry to avoid "write once" field
2433 $PA['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] = 'setFormValueManipulate(\'' . $PA['itemFormElName'] . '\', \'RemoveFirstIfFull\', \'' . $maxitems . '\'); ' . $PA['fieldChangeFunc']['TBE_EDITOR_fieldChanged'];
2434 }
2435 // Acting according to either "file" or "db" type:
2436 switch ((string) $config['internal_type']) {
2437 case 'file_reference':
2438 $config['uploadfolder'] = '';
2439 // Fall through
2440 case 'file':
2441 // Creating string showing allowed types:
2442 $tempFT = GeneralUtility::trimExplode(',', $allowed, TRUE);
2443 if (!count($tempFT)) {
2444 $info .= '*';
2445 }
2446 foreach ($tempFT as $ext) {
2447 if ($ext) {
2448 $info .= strtoupper($ext) . ' ';
2449 }
2450 }
2451 // Creating string, showing disallowed types:
2452 $tempFT_dis = GeneralUtility::trimExplode(',', $disallowed, TRUE);
2453 if (count($tempFT_dis)) {
2454 $info .= '<br />';
2455 }
2456 foreach ($tempFT_dis as $ext) {
2457 if ($ext) {
2458 $info .= '-' . strtoupper($ext) . ' ';
2459 }
2460 }
2461 // Making the array of file items:
2462 $itemArray = GeneralUtility::trimExplode(',', $PA['itemFormElValue'], TRUE);
2463 $fileFactory = ResourceFactory::getInstance();
2464 // Correct the filename for the FAL items
2465 foreach ($itemArray as &$fileItem) {
2466 list($fileUid, $fileLabel) = explode('|', $fileItem);
2467 if (MathUtility::canBeInterpretedAsInteger($fileUid)) {
2468 $fileObject = $fileFactory->getFileObject($fileUid);
2469 $fileLabel = $fileObject->getName();
2470 }
2471 $fileItem = $fileUid . '|' . $fileLabel;
2472 }
2473 // Showing thumbnails:
2474 $thumbsnail = '';
2475 if ($show_thumbs) {
2476 $imgs = array();
2477 foreach ($itemArray as $imgRead) {
2478 $imgP = explode('|', $imgRead);
2479 $imgPath = rawurldecode($imgP[0]);
2480 // FAL icon production
2481 if (MathUtility::canBeInterpretedAsInteger($imgP[0])) {
2482 $fileObject = $fileFactory->getFileObject($imgP[0]);
2483
2484 if ($fileObject->isMissing()) {
2485 $flashMessage = \TYPO3\CMS\Core\Resource\Utility\BackendUtility::getFlashMessageForMissingFile($fileObject);
2486 $imgs[] = $flashMessage->render();
2487 } elseif (GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileObject->getExtension())) {
2488 $imageUrl = $fileObject->process(ProcessedFile::CONTEXT_IMAGEPREVIEW, array())->getPublicUrl(TRUE);
2489 $imgTag = '<img src="' . $imageUrl . '" alt="' . htmlspecialchars($fileObject->getName()) . '" />';
2490 $imgs[] = '<span class="nobr">' . $imgTag . htmlspecialchars($fileObject->getName()) . '</span>';
2491 } else {
2492 // Icon
2493 $imgTag = IconUtility::getSpriteIconForResource($fileObject, array('title' => $fileObject->getName()));
2494 $imgs[] = '<span class="nobr">' . $imgTag . htmlspecialchars($fileObject->getName()) . '</span>';
2495 }
2496 } else {
2497 $rowCopy = array();
2498 $rowCopy[$field] = $imgPath;
2499 $thumbnailCode = '';
2500 try {
2501 $thumbnailCode = BackendUtility::thumbCode(
2502 $rowCopy, $table, $field, $this->backPath, 'thumbs.php',
2503 $config['uploadfolder'], 0, ' align="middle"'
2504 );
2505 $thumbnailCode = '<span class="nobr">' . $thumbnailCode . $imgPath . '</span>';
2506
2507 } catch (\Exception $exception) {
2508 /** @var $flashMessage FlashMessage */
2509 $message = $exception->getMessage();
2510 $flashMessage = GeneralUtility::makeInstance(
2511 'TYPO3\\CMS\\Core\\Messaging\\FlashMessage',
2512 htmlspecialchars($message), '', FlashMessage::ERROR, TRUE
2513 );
2514 $class = 'TYPO3\\CMS\\Core\\Messaging\\FlashMessageService';
2515 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
2516 $flashMessageService = GeneralUtility::makeInstance($class);
2517 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
2518 $defaultFlashMessageQueue->enqueue($flashMessage);
2519
2520 $logMessage = $message . ' (' . $table . ':' . $row['uid'] . ')';
2521 GeneralUtility::sysLog($logMessage, 'core', GeneralUtility::SYSLOG_SEVERITY_WARNING);
2522 }
2523
2524 $imgs[] = $thumbnailCode;
2525 }
2526 }
2527 $thumbsnail = implode('<br />', $imgs);
2528 }
2529 // Creating the element:
2530 $params = array(
2531 'size' => $size,
2532 'dontShowMoveIcons' => $maxitems <= 1,
2533 'autoSizeMax' => MathUtility::forceIntegerInRange($config['autoSizeMax'], 0),
2534 'maxitems' => $maxitems,
2535 'style' => isset($config['selectedListStyle']) ? ' style="' . htmlspecialchars($config['selectedListStyle']) . '"' : ' style="' . $this->defaultMultipleSelectorStyle . '"',
2536 'info' => $info,
2537 'thumbnails' => $thumbsnail,
2538 'readOnly' => $disabled,
2539 'noBrowser' => $noList || isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'browser'),
2540 'noList' => $noList,
2541 'noDelete' => $noDelete
2542 );
2543 $item .= $this->dbFileIcons($PA['itemFormElName'], 'file', implode(',', $tempFT), $itemArray, '', $params, $PA['onFocus'], '', '', '', $config);
2544 if (!$disabled && !(isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'upload'))) {
2545 // Adding the upload field:
2546 if ($this->edit_docModuleUpload && $config['uploadfolder']) {
2547 // Insert the multiple attribute to enable HTML5 multiple file upload
2548 $multipleAttribute = '';
2549 $multipleFilenameSuffix = '';
2550 if (isset($config['maxitems']) && $config['maxitems'] > 1) {
2551 $multipleAttribute = ' multiple="multiple"';
2552 $multipleFilenameSuffix = '[]';
2553 }
2554 $item .= '<div id="' . $PA['itemFormElID_file'] . '"><input type="file"' . $multipleAttribute . ' name="' . $PA['itemFormElName_file'] . $multipleFilenameSuffix . '" size="35" onchange="' . implode('', $PA['fieldChangeFunc']) . '" /></div>';
2555 }
2556 }
2557 break;
2558 case 'folder':
2559 // If the element is of the internal type "folder":
2560 // Array of folder items:
2561 $itemArray = GeneralUtility::trimExplode(',', $PA['itemFormElValue'], TRUE);
2562 // Creating the element:
2563 $params = array(
2564 'size' => $size,
2565 'dontShowMoveIcons' => $maxitems <= 1,
2566 'autoSizeMax' => MathUtility::forceIntegerInRange($config['autoSizeMax'], 0),
2567 'maxitems' => $maxitems,
2568 'style' => isset($config['selectedListStyle']) ? ' style="' . htmlspecialchars($config['selectedListStyle']) . '"' : ' style="' . $this->defaultMultipleSelectorStyle . '"',
2569 'info' => $info,
2570 'readOnly' => $disabled,
2571 'noBrowser' => $noList || isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'browser'),
2572 'noList' => $noList
2573 );
2574 $item .=</