e11193f76e970d83734c8524c51f23df977b9b12
[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 FormEngine::$RTEcounter = FormEngine::$RTEcounter + 1;
128 // Get RTE object, draw form and set flag:
129 $rteObject = BackendUtility::RTEgetObj();
130 $dummyFormEngine = new FormEngine();
131 $rteResult = $rteObject->drawRTE(
132 $dummyFormEngine,
133 $table,
134 $fieldName,
135 $row,
136 $parameterArray,
137 $specialConfiguration,
138 $rteSetupConfiguration,
139 $rteTcaTypeValue,
140 '',
141 $tsConfigPid,
142 $this->globalOptions,
143 $this->initializeResultArray()
144 );
145 // This is a compat layer for "other" RTE's: If the result is not an array, it is the html string,
146 // otherwise it is a structure similar to our casual return array
147 // @todo: This interface needs a full re-definition, RTE should probably be its own type in the
148 // @todo: end, and other RTE implementations could then just override this.
149 if (is_array($rteResult)) {
150 $html = $rteResult['html'];
151 $rteResult['html'] = '';
152 $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $rteResult);
153 } else {
154 $html = $rteResult;
155 }
156
157 // Wizard
158 $html = $this->renderWizards(
159 array($html, $altItem),
160 $config['wizards'],
161 $table,
162 $row,
163 $fieldName,
164 $parameterArray,
165 $parameterArray['itemFormElName'],
166 $specialConfiguration,
167 TRUE
168 );
169 $rteWasLoaded = TRUE;
170 }
171 }
172 }
173 }
174 // Display ordinary field if RTE was not loaded.
175 if (!$rteWasLoaded) {
176 // Show message, if no RTE (field can only be edited with RTE!)
177 if ($specialConfiguration['rte_only']) {
178 $html = '<p><em>' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.noRTEfound')) . '</em></p>';
179 } else {
180 // validation
181 foreach ($evalList as $func) {
182 if ($func === 'required') {
183 $resultArray['requiredFields'][$table . '_' . $row['uid'] . '_' . $fieldName] = $parameterArray['itemFormElName'];
184 } else {
185 // Pair hook to the one in \TYPO3\CMS\Core\DataHandling\DataHandler::checkValue_input_Eval()
186 // and \TYPO3\CMS\Core\DataHandling\DataHandler::checkValue_text_Eval()
187 $evalObj = GeneralUtility::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][$func] . ':&' . $func);
188 if (is_object($evalObj) && method_exists($evalObj, 'deevaluateFieldValue')) {
189 $_params = array(
190 'value' => $parameterArray['itemFormElValue']
191 );
192 $parameterArray['itemFormElValue'] = $evalObj->deevaluateFieldValue($_params);
193 }
194 }
195 }
196
197 // calculate classes
198 $classes = array();
199 $classes[] = 'form-control';
200 $classes[] = 't3js-formengine-textarea';
201 if ($specialConfiguration['fixed-font']) {
202 $classes[] = 'text-monospace';
203 }
204 if ($specialConfiguration['enable-tab']) {
205 $classes[] = 'enable-tab';
206 }
207
208 // calculate styles
209 $styles = array();
210 // add the max-height from the users' preference to it
211 $maximumHeight = (int)$backendUser->uc['resizeTextareas_MaxHeight'];
212 if ($maximumHeight > 0) {
213 $styles[] = 'max-height: ' . $maximumHeight . 'px';
214 }
215
216 // calculate attributes
217 $attributes = array();
218 $attributes['id'] = str_replace('.', '', uniqid('formengine-textarea-', TRUE));
219 $attributes['name'] = $parameterArray['itemFormElName'];
220 if (!empty($styles)) {
221 $attributes['style'] = implode(' ', $styles);
222 }
223 if (!empty($classes)) {
224 $attributes['class'] = implode(' ', $classes);
225 }
226 $attributes['rows'] = $rows;
227 $attributes['wrap'] = $specialConfiguration['nowrap'] ? 'off' : ($config['wrap'] ?: 'virtual');
228 $attributes['onChange'] = htmlspecialchars(implode('', $parameterArray['fieldChangeFunc']));
229 if (isset($config['max']) && (int)$config['max'] > 0) {
230 $attributes['maxlength'] = (int)$config['max'];
231 }
232 $attributeString = '';
233 foreach ($attributes as $attributeName => $attributeValue) {
234 $attributeString .= ' '. $attributeName . '="' . $attributeValue . '"';
235 }
236
237 // Build the textarea
238 $placeholderValue = $this->getPlaceholderValue($table, $config, $row);
239 $placeholderAttribute = '';
240 if (!empty($placeholderValue)) {
241 $placeholderAttribute = ' placeholder="' . htmlspecialchars(trim($languageService->sL($placeholderValue))) . '" ';
242 }
243
244 $html .= '<textarea'
245 . $attributeString
246 . $placeholderAttribute
247 . $parameterArray['onFocus']
248 . '>' . GeneralUtility::formatForTextarea($parameterArray['itemFormElValue']) . '</textarea>';
249
250 // Wrap a wizard around the item?
251 $html = $this->renderWizards(
252 array($html, $altItem),
253 $config['wizards'],
254 $table,
255 $row,
256 $fieldName,
257 $parameterArray,
258 $parameterArray['itemFormElName'],
259 $specialConfiguration,
260 $rteWouldHaveBeenLoaded
261 );
262
263 $maximumWidth = (int)$this->formMaxWidth($cols);
264 $html = '<div class="form-control-wrap"' . ($maximumWidth ? ' style="max-width: ' . $maximumWidth . 'px"' : '') . '>' . $html . '</div>';
265 }
266 }
267 $resultArray['html'] = $html;
268 return $resultArray;
269 }
270
271 /**
272 * @return BackendUserAuthentication
273 */
274 protected function getBackendUserAuthentication() {
275 return $GLOBALS['BE_USER'];
276 }
277
278 }