[FEATURE] Add TCA autocomplete setting for input fields
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / Element / InputTextElement.php
1 <?php
2 namespace TYPO3\CMS\Backend\Form\Element;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Backend\Utility\BackendUtility;
18 use TYPO3\CMS\Core\Imaging\Icon;
19 use TYPO3\CMS\Core\Imaging\IconFactory;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21 use TYPO3\CMS\Core\Utility\MathUtility;
22 use TYPO3\CMS\Core\Utility\StringUtility;
23
24 /**
25 * Generation of TCEform elements of the type "input type=text"
26 */
27 class InputTextElement extends AbstractFormElement {
28
29 /**
30 * This will render a single-line input form field, possibly with various control/validation features
31 *
32 * @return array As defined in initializeResultArray() of AbstractNode
33 */
34 public function render() {
35 /** @var IconFactory $iconFactory */
36 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
37 $languageService = $this->getLanguageService();
38
39 $table = $this->data['tableName'];
40 $fieldName = $this->data['fieldName'];
41 $row = $this->data['databaseRow'];
42 $parameterArray = $this->data['parameterArray'];
43 $resultArray = $this->initializeResultArray();
44 $isDateField = FALSE;
45
46 $config = $parameterArray['fieldConf']['config'];
47 $specConf = BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']);
48 $size = MathUtility::forceIntegerInRange($config['size'] ?: $this->defaultInputWidth, $this->minimumInputWidth, $this->maxInputWidth);
49 $evalList = GeneralUtility::trimExplode(',', $config['eval'], TRUE);
50 $classes = array();
51 $attributes = array();
52
53 if (!isset($config['checkbox'])) {
54 $config['checkbox'] = '0';
55 $checkboxIsset = FALSE;
56 } else {
57 $checkboxIsset = TRUE;
58 }
59
60 // set all date times available
61 $dateFormats = array(
62 'date' => '%d-%m-%Y',
63 'year' => '%Y',
64 'time' => '%H:%M',
65 'timesec' => '%H:%M:%S'
66 );
67 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat']) {
68 $dateFormats['date'] = '%m-%d-%Y';
69 }
70 $dateFormats['datetime'] = $dateFormats['time'] . ' ' . $dateFormats['date'];
71 $dateFormats['datetimesec'] = $dateFormats['timesec'] . ' ' . $dateFormats['date'];
72
73 // readonly
74 if ($config['readOnly']) {
75 $itemFormElValue = $parameterArray['itemFormElValue'];
76 if (in_array('date', $evalList)) {
77 $config['format'] = 'date';
78 } elseif (in_array('datetime', $evalList)) {
79 $config['format'] = 'datetime';
80 } elseif (in_array('time', $evalList)) {
81 $config['format'] = 'time';
82 }
83 if (in_array('password', $evalList)) {
84 $itemFormElValue = $itemFormElValue ? '*********' : '';
85 }
86 $options = $this->data;
87 $options['parameterArray'] = array(
88 'fieldConf' => array(
89 'config' => $config,
90 ),
91 'itemFormElValue' => $itemFormElValue,
92 );
93 $options['renderType'] = 'none';
94 return $this->nodeFactory->create($options)->render();
95 }
96
97 if (in_array('datetime', $evalList, TRUE)
98 || in_array('date', $evalList)) {
99
100 $classes[] = 't3js-datetimepicker';
101 $isDateField = TRUE;
102 if (in_array('datetime', $evalList)) {
103 $attributes['data-date-type'] = 'datetime';
104 $dateFormat = $dateFormats['datetime'];
105 } elseif (in_array('date', $evalList)) {
106 $attributes['data-date-type'] = 'date';
107 $dateFormat = $dateFormats['date'];
108 }
109 if ($parameterArray['itemFormElValue'] > 0) {
110 $parameterArray['itemFormElValue'] += date('Z', $parameterArray['itemFormElValue']);
111 }
112 if (isset($config['range']['lower'])) {
113 $attributes['data-date-minDate'] = (int)$config['range']['lower'];
114 }
115 if (isset($config['range']['upper'])) {
116 $attributes['data-date-maxDate'] = (int)$config['range']['upper'];
117 }
118 } elseif (in_array('time', $evalList)) {
119 $dateFormat = $dateFormats['time'];
120 $isDateField = TRUE;
121 $classes[] = 't3js-datetimepicker';
122 $attributes['data-date-type'] = 'time';
123 } elseif (in_array('timesec', $evalList)) {
124 $dateFormat = $dateFormats['timesec'];
125 $isDateField = TRUE;
126 $classes[] = 't3js-datetimepicker';
127 $attributes['data-date-type'] = 'timesec';
128 } else {
129 if ($checkboxIsset === FALSE) {
130 $config['checkbox'] = '';
131 }
132 }
133
134 // @todo: The whole eval handling is a mess and needs refactoring
135 foreach ($evalList as $func) {
136 switch ($func) {
137 case 'required':
138 $attributes['data-formengine-validation-rules'] = $this->getValidationDataAsJsonString(array('required' => TRUE));
139 break;
140 default:
141 // @todo: This is ugly: The code should find out on it's own whether a eval definition is a
142 // @todo: keyword like "date", or a class reference. The global registration could be dropped then
143 // Pair hook to the one in \TYPO3\CMS\Core\DataHandling\DataHandler::checkValue_input_Eval()
144 if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][$func])) {
145 if (class_exists($func)) {
146 $evalObj = GeneralUtility::makeInstance($func);
147 if (method_exists($evalObj, 'deevaluateFieldValue')) {
148 $_params = array(
149 'value' => $parameterArray['itemFormElValue']
150 );
151 $parameterArray['itemFormElValue'] = $evalObj->deevaluateFieldValue($_params);
152 }
153 }
154 }
155 }
156 }
157 $paramsList = array(
158 'field' => $parameterArray['itemFormElName'],
159 'evalList' => implode(',', $evalList),
160 'is_in' => trim($config['is_in']),
161 'checkbox' => ($config['checkbox'] ? 1 : 0),
162 'checkboxValue' => $config['checkbox'],
163 );
164 // set classes
165 $classes[] = 'form-control';
166 $classes[] = 't3js-clearable';
167 $classes[] = 'hasDefaultValue';
168
169 // calculate attributes
170 $attributes['data-formengine-validation-rules'] = $this->getValidationDataAsJsonString($config);
171 $attributes['data-formengine-input-params'] = json_encode($paramsList);
172 $attributes['data-formengine-input-name'] = htmlspecialchars($parameterArray['itemFormElName']);
173 $attributes['id'] = StringUtility::getUniqueId('formengine-input-');
174 $attributes['value'] = '';
175 if (isset($config['max']) && (int)$config['max'] > 0) {
176 $attributes['maxlength'] = (int)$config['max'];
177 }
178 if (!empty($classes)) {
179 $attributes['class'] = implode(' ', $classes);
180 }
181
182 // This is the EDITABLE form field.
183 if (!empty($config['placeholder'])) {
184 $attributes['placeholder'] = trim($config['placeholder']);
185 }
186
187 if (isset($config['autocomplete'])) {
188 $attributes['autocomplete'] = empty($config['autocomplete']) ? 'off' : 'on';
189 }
190
191 // Build the attribute string
192 $attributeString = '';
193 foreach ($attributes as $attributeName => $attributeValue) {
194 $attributeString .= ' ' . $attributeName . '="' . htmlspecialchars($attributeValue) . '"';
195 }
196
197 $html = '
198 <input type="text"'
199 . $attributeString
200 . $parameterArray['onFocus'] . ' />';
201
202 // 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.
203 $html .= '<input type="hidden" name="' . $parameterArray['itemFormElName'] . '" value="' . htmlspecialchars($parameterArray['itemFormElValue']) . '" />';
204
205 // Going through all custom evaluations configured for this field
206 // @todo: Similar to above code!
207 foreach ($evalList as $evalData) {
208 if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][$evalData])) {
209 if (class_exists($evalData)) {
210 $evalObj = GeneralUtility::makeInstance($evalData);
211 if (method_exists($evalObj, 'returnFieldJS')) {
212 $resultArray['extJSCODE'] .= LF . 'TBE_EDITOR.customEvalFunctions[' . GeneralUtility::quoteJSvalue($evalData) . '] = function(value) {' . $evalObj->returnFieldJS() . '}';
213 }
214 }
215 }
216 }
217
218 // add HTML wrapper
219 if ($isDateField) {
220 $html = '
221 <div class="input-group">
222 ' . $html . '
223 <span class="input-group-btn">
224 <label class="btn btn-default" for="' . $attributes['id'] . '">
225 ' . $iconFactory->getIcon('actions-edit-pick-date', Icon::SIZE_SMALL)->render() . '
226 </label>
227 </span>
228 </div>';
229 }
230
231 // Wrap a wizard around the item?
232 $html = $this->renderWizards(
233 array($html),
234 $config['wizards'],
235 $table,
236 $row,
237 $fieldName,
238 $parameterArray,
239 $parameterArray['itemFormElName'],
240 $specConf
241 );
242
243 // Add a wrapper to remain maximum width
244 $width = (int)$this->formMaxWidth($size);
245 $html = '<div class="form-control-wrap"' . ($width ? ' style="max-width: ' . $width . 'px"' : '') . '>' . $html . '</div>';
246 $resultArray['html'] = $html;
247 return $resultArray;
248 }
249
250 }