[TASK] Remove dependency to tab.js in FormEngine
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / Element / TextElement.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\Utility\GeneralUtility;
19 use TYPO3\CMS\Core\Utility\MathUtility;
20 use TYPO3\CMS\Backend\Form\FormEngine;
21 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
22
23 /**
24 * Generation of TCEform elements of the type "text"
25 */
26 class TextElement extends AbstractFormElement {
27
28 /**
29 * The number of chars expected per row when the height of a text area field is
30 * automatically calculated based on the number of characters found in the field content.
31 *
32 * @var int
33 */
34 protected $charactersPerRow = 40;
35
36 /**
37 * This will render a <textarea> OR RTE area form field,
38 * possibly with various control/validation features
39 *
40 * @return array As defined in initializeResultArray() of AbstractNode
41 */
42 public function render() {
43 $languageService = $this->getLanguageService();
44
45 $table = $this->globalOptions['table'];
46 $fieldName = $this->globalOptions['fieldName'];
47 $row = $this->globalOptions['databaseRow'];
48 $parameterArray = $this->globalOptions['parameterArray'];
49 $resultArray = $this->initializeResultArray();
50 $backendUser = $this->getBackendUserAuthentication();
51
52 $config = $parameterArray['fieldConf']['config'];
53
54 // Setting columns number
55 $cols = MathUtility::forceIntegerInRange($config['cols'] ?: $this->defaultInputWidth, $this->minimumInputWidth, $this->maxInputWidth);
56
57 // Setting number of rows
58 $rows = MathUtility::forceIntegerInRange($config['rows'] ?: 5, 1, 20);
59 $originalRows = $rows;
60
61 $itemFormElementValueLength = strlen($parameterArray['itemFormElValue']);
62 if ($itemFormElementValueLength > $this->charactersPerRow * 2) {
63 $cols = $this->maxInputWidth;
64 $rows = MathUtility::forceIntegerInRange(
65 round($itemFormElementValueLength / $this->charactersPerRow),
66 count(explode(LF, $parameterArray['itemFormElValue'])),
67 20
68 );
69 if ($rows < $originalRows) {
70 $rows = $originalRows;
71 }
72 }
73
74 // must be called after the cols and rows calculation, so the parameters are applied
75 // to read-only fields as well.
76 // @todo: Same as in InputElement ...
77 if ($this->isGlobalReadonly() || $config['readOnly']) {
78 $config['cols'] = $cols;
79 $config['rows'] = $rows;
80 /** @var NoneElement $noneElement */
81 $noneElement = GeneralUtility::makeInstance(NoneElement::class);
82 $noneElementOptions = $this->globalOptions;
83 $noneElementOptions['parameterArray'] = array(
84 'fieldConf' => array(
85 'config' => $config,
86 ),
87 'itemFormElValue' => $parameterArray['itemFormElValue'],
88 );
89 return $noneElement->setGlobalOptions($noneElementOptions)->render();
90 }
91
92 $evalList = GeneralUtility::trimExplode(',', $config['eval'], TRUE);
93 if (in_array('required', $evalList, TRUE)) {
94 $resultArray['requiredFields'][$table . '_' . $row['uid'] . '_' . $fieldName] = $parameterArray['itemFormElName'];
95 $tabAndInlineStack = $this->globalOptions['tabAndInlineStack'];
96 if (!empty($tabAndInlineStack) && preg_match('/^(.+\\])\\[(\\w+)\\]$/', $parameterArray['itemFormElName'], $match)) {
97 array_shift($match);
98 $resultArray['requiredNested'][$parameterArray['itemFormElName']] = array(
99 'parts' => $match,
100 'level' => $tabAndInlineStack,
101 );
102 }
103 }
104 // Init RTE vars
105 // Set TRUE, if the RTE is loaded; If not a normal textarea is shown.
106 $rteWasLoaded = FALSE;
107 // Set TRUE, if the RTE would have been loaded if it wasn't for the disable-RTE flag in the bottom of the page...
108 $rteWouldHaveBeenLoaded = FALSE;
109 // "Extra" configuration; Returns configuration for the field based on settings found in the "types" fieldlist. Traditionally, this is where RTE configuration has been found.
110 $specialConfiguration = BackendUtility::getSpecConfParts($parameterArray['extra'], $parameterArray['fieldConf']['defaultExtras']);
111 // Setting up the altItem form field, which is a hidden field containing the value
112 $altItem = '<input type="hidden" name="' . htmlspecialchars($parameterArray['itemFormElName']) . '" value="' . htmlspecialchars($parameterArray['itemFormElValue']) . '" />';
113 $html = '';
114 // If RTE is generally enabled (TYPO3_CONF_VARS and user settings)
115 if ($backendUser->isRTE()) {
116 $parameters = BackendUtility::getSpecConfParametersFromArray($specialConfiguration['rte_transform']['parameters']);
117 // If the field is configured for RTE and if any flag-field is not set to disable it.
118 if (isset($specialConfiguration['richtext']) && (!$parameters['flag'] || !$row[$parameters['flag']])) {
119 BackendUtility::fixVersioningPid($table, $row);
120 list($recordPid, $tsConfigPid) = BackendUtility::getTSCpidCached($table, $row['uid'], $row['pid']);
121 // If the pid-value is not negative (that is, a pid could NOT be fetched)
122 if ($tsConfigPid >= 0) {
123 $rteSetup = $backendUser->getTSConfig('RTE', BackendUtility::getPagesTSconfig($recordPid));
124 $rteTcaTypeValue = BackendUtility::getTCAtypeValue($table, $row);
125 $rteSetupConfiguration = BackendUtility::RTEsetup($rteSetup['properties'], $table, $fieldName, $rteTcaTypeValue);
126 if (!$rteSetupConfiguration['disabled']) {
127 // Get RTE object, draw form and set flag:
128 $rteObject = BackendUtility::RTEgetObj();
129 $dummyFormEngine = new FormEngine();
130 $rteResult = $rteObject->drawRTE(
131 $dummyFormEngine,
132 $table,
133 $fieldName,
134 $row,
135 $parameterArray,
136 $specialConfiguration,
137 $rteSetupConfiguration,
138 $rteTcaTypeValue,
139 '',
140 $tsConfigPid,
141 $this->globalOptions,
142 $this->initializeResultArray()
143 );
144 // This is a compat layer for "other" RTE's: If the result is not an array, it is the html string,
145 // otherwise it is a structure similar to our casual return array
146 // @todo: This interface needs a full re-definition, RTE should probably be its own type in the
147 // @todo: end, and other RTE implementations could then just override this.
148 if (is_array($rteResult)) {
149 $html = $rteResult['html'];
150 $rteResult['html'] = '';
151 $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $rteResult);
152 } else {
153 $html = $rteResult;
154 }
155
156 // Wizard
157 $html = $this->renderWizards(
158 array($html, $altItem),
159 $config['wizards'],
160 $table,
161 $row,
162 $fieldName,
163 $parameterArray,
164 $parameterArray['itemFormElName'],
165 $specialConfiguration,
166 TRUE
167 );
168 $rteWasLoaded = TRUE;
169 }
170 }
171 }
172 }
173 // Display ordinary field if RTE was not loaded.
174 if (!$rteWasLoaded) {
175 // Show message, if no RTE (field can only be edited with RTE!)
176 if ($specialConfiguration['rte_only']) {
177 $html = '<p><em>' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.noRTEfound')) . '</em></p>';
178 } else {
179 // validation
180 foreach ($evalList as $func) {
181 if ($func === 'required') {
182 $resultArray['requiredFields'][$table . '_' . $row['uid'] . '_' . $fieldName] = $parameterArray['itemFormElName'];
183 } else {
184 // Pair hook to the one in \TYPO3\CMS\Core\DataHandling\DataHandler::checkValue_input_Eval()
185 // and \TYPO3\CMS\Core\DataHandling\DataHandler::checkValue_text_Eval()
186 $evalObj = GeneralUtility::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][$func] . ':&' . $func);
187 if (is_object($evalObj) && method_exists($evalObj, 'deevaluateFieldValue')) {
188 $_params = array(
189 'value' => $parameterArray['itemFormElValue']
190 );
191 $parameterArray['itemFormElValue'] = $evalObj->deevaluateFieldValue($_params);
192 }
193 }
194 }
195
196 // calculate classes
197 $classes = array();
198 $classes[] = 'form-control';
199 $classes[] = 't3js-formengine-textarea';
200 if ($specialConfiguration['fixed-font']) {
201 $classes[] = 'text-monospace';
202 }
203 if ($specialConfiguration['enable-tab']) {
204 $classes[] = 't3js-enable-tab';
205 }
206
207 // calculate styles
208 $styles = array();
209 // add the max-height from the users' preference to it
210 $maximumHeight = (int)$backendUser->uc['resizeTextareas_MaxHeight'];
211 if ($maximumHeight > 0) {
212 $styles[] = 'max-height: ' . $maximumHeight . 'px';
213 }
214
215 // calculate attributes
216 $attributes = array();
217 $attributes['id'] = str_replace('.', '', uniqid('formengine-textarea-', TRUE));
218 $attributes['name'] = $parameterArray['itemFormElName'];
219 if (!empty($styles)) {
220 $attributes['style'] = implode(' ', $styles);
221 }
222 if (!empty($classes)) {
223 $attributes['class'] = implode(' ', $classes);
224 }
225 $attributes['rows'] = $rows;
226 $attributes['wrap'] = $specialConfiguration['nowrap'] ? 'off' : ($config['wrap'] ?: 'virtual');
227 $attributes['onChange'] = implode('', $parameterArray['fieldChangeFunc']);
228 if (isset($config['max']) && (int)$config['max'] > 0) {
229 $attributes['maxlength'] = (int)$config['max'];
230 }
231 $attributeString = '';
232 foreach ($attributes as $attributeName => $attributeValue) {
233 $attributeString .= ' '. $attributeName . '="' . htmlspecialchars($attributeValue) . '"';
234 }
235
236 // Build the textarea
237 $placeholderValue = $this->getPlaceholderValue($table, $config, $row);
238 $placeholderAttribute = '';
239 if (!empty($placeholderValue)) {
240 $placeholderAttribute = ' placeholder="' . htmlspecialchars(trim($languageService->sL($placeholderValue))) . '" ';
241 }
242
243 $html .= '<textarea'
244 . $attributeString
245 . $placeholderAttribute
246 . $parameterArray['onFocus']
247 . '>' . GeneralUtility::formatForTextarea($parameterArray['itemFormElValue']) . '</textarea>';
248
249 // Wrap a wizard around the item?
250 $html = $this->renderWizards(
251 array($html, $altItem),
252 $config['wizards'],
253 $table,
254 $row,
255 $fieldName,
256 $parameterArray,
257 $parameterArray['itemFormElName'],
258 $specialConfiguration,
259 $rteWouldHaveBeenLoaded
260 );
261
262 $maximumWidth = (int)$this->formMaxWidth($cols);
263 $html = '<div class="form-control-wrap"' . ($maximumWidth ? ' style="max-width: ' . $maximumWidth . 'px"' : '') . '>' . $html . '</div>';
264 }
265 }
266 $resultArray['html'] = $html;
267 return $resultArray;
268 }
269
270 /**
271 * @return BackendUserAuthentication
272 */
273 protected function getBackendUserAuthentication() {
274 return $GLOBALS['BE_USER'];
275 }
276
277 }