[TASK] Call explicit render() on icon objects
[Packages/TYPO3.CMS.git] / typo3 / sysext / compatibility6 / Classes / Controller / Wizard / FormsController.php
1 <?php
2 namespace TYPO3\CMS\Compatibility6\Controller\Wizard;
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 Psr\Http\Message\ServerRequestInterface;
18 use Psr\Http\Message\ResponseInterface;
19 use TYPO3\CMS\Backend\Utility\BackendUtility;
20 use TYPO3\CMS\Core\Imaging\Icon;
21 use TYPO3\CMS\Core\Imaging\IconFactory;
22 use TYPO3\CMS\Core\Utility\GeneralUtility;
23
24 /**
25 * API comments:
26 *
27 * The form wizard can help you to create forms - it allows you to create almost any kind of HTML form elements and in any order and amount.
28 *
29 * The format for the resulting configuration code can be either a line-based configuration. That can look like this:
30 *
31 * Your name: | *name=input | (input your name here!)
32 * Your Email: | *email=input
33 * Your address: | address=textarea,40,10
34 * Your Haircolor: | hair=radio |
35 * upload | attachment=file
36 * | quoted_printable=hidden | 0
37 * | formtype_mail=submit | Send form
38 * | html_enabled=hidden
39 * | subject=hidden | This is the subject
40 *
41 *
42 * Alternatively it can be XML. The same configuration from above looks like this in XML:
43 *
44 * <T3FormWizard>
45 * <n2>
46 * <type>input</type>
47 * <label>Your name:</label>
48 * <required>1</required>
49 * <fieldname>name</fieldname>
50 * <size></size>
51 * <max></max>
52 * <default>(input your name here!)</default>
53 * </n2>
54 * <n4>
55 * <type>input</type>
56 * <label>Your Email:</label>
57 * <required>1</required>
58 * <fieldname>email</fieldname>
59 * <size></size>
60 * <max></max>
61 * <default></default>
62 * </n4>
63 * <n6>
64 * <type>textarea</type>
65 * <label>Your address:</label>
66 * <fieldname>address</fieldname>
67 * <cols>40</cols>
68 * <rows>10</rows>
69 * <default></default>
70 * </n6>
71 * <n8>
72 * <type>radio</type>
73 * <label>Your Haircolor:</label>
74 * <fieldname>hair</fieldname>
75 * <options></options>
76 * </n8>
77 * <n10>
78 * <type>file</type>
79 * <label>upload</label>
80 * <fieldname>attachment</fieldname>
81 * <size></size>
82 * </n10>
83 * <n12>
84 * <type>hidden</type>
85 * <label></label>
86 * <fieldname>quoted_printable</fieldname>
87 * <default>0</default>
88 * </n12>
89 * <n2000>
90 * <fieldname>formtype_mail</fieldname>
91 * <type>submit</type>
92 * <default>Send form</default>
93 * </n2000>
94 * <n2002>
95 * <fieldname>html_enabled</fieldname>
96 * <type>hidden</type>
97 * </n2002>
98 * <n2004>
99 * <fieldname>subject</fieldname>
100 * <type>hidden</type>
101 * <default>This is the subject</default>
102 * </n2004>
103 * <n20>
104 * <content></content>
105 * </n20>
106 * </T3FormWizard>
107 *
108 *
109 * The XML/phpArray structure is the internal format of the wizard.
110 */
111 class FormsController extends \TYPO3\CMS\Backend\Controller\Wizard\AbstractWizardController {
112
113 /**
114 * document template object
115 *
116 * @var \TYPO3\CMS\Backend\Template\DocumentTemplate
117 */
118 public $doc;
119
120 /**
121 * Content accumulation for the module.
122 *
123 * @var string
124 */
125 public $content;
126
127 /**
128 * Used to numerate attachments automatically.
129 *
130 * @var int
131 */
132 public $attachmentCounter = 0;
133
134 /**
135 * If set, the string version of the content is interpreted/written as XML instead of
136 * the original linebased kind. This variable still needs binding to the wizard parameters
137 * - but support is ready!
138 *
139 * @var int
140 */
141 public $xmlStorage = 0;
142
143 /**
144 * Wizard parameters, coming from TCEforms linking to the wizard.
145 *
146 * @var array
147 */
148 public $P;
149
150 /**
151 * The array which is constantly submitted by the multidimensional form of this wizard.
152 *
153 * @var array
154 */
155 public $FORMCFG;
156
157 /**
158 * Indicates if the form is of a dedicated type, like "formtype_mail" (for tt_content element "Form")
159 *
160 * @var string
161 */
162 public $special;
163
164 /**
165 * @var IconFactory
166 */
167 protected $iconFactory;
168
169 /**
170 * Constructor
171 */
172 public function __construct() {
173 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
174 $this->getLanguageService()->includeLLFile('EXT:lang/locallang_wizards.xlf');
175 $GLOBALS['SOBE'] = $this;
176
177 $this->init();
178 }
179
180 /**
181 * Initialization the class
182 *
183 * @return void
184 */
185 protected function init() {
186 // GPvars:
187 $this->P = GeneralUtility::_GP('P');
188 $this->special = GeneralUtility::_GP('special');
189 $this->FORMCFG = GeneralUtility::_GP('FORMCFG');
190 // Setting options:
191 $this->xmlStorage = $this->P['params']['xmlOutput'];
192 // Document template object:
193 $this->doc = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Template\DocumentTemplate::class);
194 $this->doc->setModuleTemplate('EXT:compatibility6/Resources/Private/Templates/Wizard/Forms.html');
195 // Setting form tag:
196 list($rUri) = explode('#', GeneralUtility::getIndpEnv('REQUEST_URI'));
197 $this->doc->form = '<form action="' . htmlspecialchars($rUri) . '" method="post" name="wizardForm">';
198 }
199
200 /**
201 * Injects the request object for the current request or subrequest
202 * As this controller goes only through the main() method, it is rather simple for now
203 *
204 * @param ServerRequestInterface $request the current request
205 * @param ResponseInterface $response
206 * @return ResponseInterface the response with the content
207 */
208 public function mainAction(ServerRequestInterface $request, ResponseInterface $response) {
209 $this->main();
210
211 $response->getBody()->write($this->content);
212 return $response;
213 }
214
215 /**
216 * Main function for rendering the form wizard HTML
217 *
218 * @return void
219 */
220 public function main() {
221 if ($this->P['table'] && $this->P['field'] && $this->P['uid']) {
222 $this->content .= $this->doc->section($this->getLanguageService()->getLL('forms_title'), $this->formsWizard(), 0, 1);
223 } else {
224 $this->content .= $this->doc->section($this->getLanguageService()->getLL('forms_title'), '<span class="text-danger">' . $this->getLanguageService()->getLL('table_noData', 1) . '</span>', 0, 1);
225 }
226 // Setting up the buttons and markers for docheader
227 $docHeaderButtons = $this->getButtons();
228 $markers['CSH'] = $docHeaderButtons['csh'];
229 $markers['CONTENT'] = $this->content;
230 // Build the <body> for the module
231 $this->content = $this->doc->startPage('Form Wizard');
232 $this->content .= $this->doc->moduleBody($this->pageinfo, $docHeaderButtons, $markers);
233 $this->content .= $this->doc->endPage();
234 $this->content = $this->doc->insertStylesAndJS($this->content);
235 }
236
237 /**
238 * Outputting the accumulated content to screen
239 *
240 * @return void
241 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8, use mainAction() instead
242 */
243 public function printContent() {
244 GeneralUtility::logDeprecatedFunction();
245 echo $this->content;
246 }
247
248 /**
249 * Create the panel of buttons for submitting the form or otherwise perform operations.
250 *
251 * @return array All available buttons as an assoc. array
252 */
253 protected function getButtons() {
254 $buttons = array(
255 'csh' => '',
256 'csh_buttons' => '',
257 'close' => '',
258 'save' => '',
259 'save_close' => '',
260 'reload' => ''
261 );
262 if ($this->P['table'] && $this->P['field'] && $this->P['uid']) {
263 // CSH
264 $buttons['csh'] = BackendUtility::cshItem('xMOD_csh_corebe', 'wizard_forms_wiz');
265 // CSH Buttons
266 $buttons['csh_buttons'] = BackendUtility::cshItem('xMOD_csh_corebe', 'wizard_forms_wiz_buttons');
267 // Close
268 $buttons['close'] = '<button class="c-inputButton" name="closedok" value="1" title=' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:rm.closeDoc', TRUE) . '>' . $this->iconFactory->getIcon('actions-document-close', Icon::SIZE_SMALL)->render() . '</button>';
269 // Save
270 $buttons['save'] = '<button class="c-inputButton" name="savedok" value="1" title=' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveDoc', TRUE) . '>' . $this->iconFactory->getIcon('actions-document-save', Icon::SIZE_SMALL)->render() . '</button>';
271 // Save & Close
272 $buttons['save_close'] = '<button class="c-inputButton" name="saveandclosedok" value="1" title=' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveCloseDoc', TRUE) . '>' . $this->iconFactory->getIcon('actions-document-save-close', Icon::SIZE_SMALL)->render() . '</button>';
273 // Reload
274 $buttons['reload'] = '<button class="c-inputButton" name="_refresh" value="1" title="' . $this->getLanguageService()->getLL('forms_refresh', TRUE) . '">' . $this->iconFactory->getIcon('actions-refresh', Icon::SIZE_SMALL)->render() . '</button>';
275 }
276 return $buttons;
277 }
278
279 /**
280 * Draws the form wizard content
281 *
282 * @return string HTML content for the form.
283 */
284 public function formsWizard() {
285 if (!$this->checkEditAccess($this->P['table'], $this->P['uid'])) {
286 throw new \RuntimeException('Wizard Error: No access', 1385807526);
287 }
288 // First, check the references by selecting the record:
289 $row = BackendUtility::getRecord($this->P['table'], $this->P['uid']);
290 if (!is_array($row)) {
291 throw new \RuntimeException('Wizard Error: No reference to record', 1294587124);
292 }
293 // This will get the content of the form configuration code field to us - possibly
294 // cleaned up, saved to database etc. if the form has been submitted in the meantime.
295 $formCfgArray = $this->getConfigCode($row);
296 // Generation of the Form Wizards HTML code:
297 $content = $this->getFormHTML($formCfgArray, $row);
298 // Return content:
299 return $content;
300 }
301
302 /****************************
303 *
304 * Helper functions
305 *
306 ***************************/
307 /**
308 * Will get and return the configuration code string
309 * Will also save (and possibly redirect/exit) the content if a save button has been pressed
310 *
311 * @param array $row Current parent record row (passed by value!)
312 * @return array Configuration Array
313 * @access private
314 */
315 public function getConfigCode(&$row) {
316 // If some data has been submitted, then construct
317 if (isset($this->FORMCFG['c'])) {
318 // Process incoming:
319 $this->changeFunc();
320 // Convert to string (either line based or XML):
321 if ($this->xmlStorage) {
322 // Convert the input array to XML:
323 $bodyText = GeneralUtility::array2xml_cs($this->FORMCFG['c'], 'T3FormWizard');
324 // Setting cfgArr directly from the input:
325 $cfgArr = $this->FORMCFG['c'];
326 } else {
327 // Convert the input array to a string of configuration code:
328 $bodyText = $this->cfgArray2CfgString($this->FORMCFG['c']);
329 // Create cfgArr from the string based configuration - that way it is cleaned
330 // up and any incompatibilities will be removed!
331 $cfgArr = $this->cfgString2CfgArray($bodyText);
332 }
333 // If a save button has been pressed, then save the new field content:
334 if (isset($_POST['savedok']) || isset($_POST['saveandclosedok'])) {
335 // Make TCEmain object:
336 $tce = GeneralUtility::makeInstance(\TYPO3\CMS\Core\DataHandling\DataHandler::class);
337 $tce->stripslashes_values = 0;
338 // Put content into the data array:
339 $data = array();
340 $data[$this->P['table']][$this->P['uid']][$this->P['field']] = $bodyText;
341 if ($this->special == 'formtype_mail') {
342 $data[$this->P['table']][$this->P['uid']]['subheader'] = $this->FORMCFG['recipient'];
343 }
344 // Perform the update:
345 $tce->start($data, array());
346 $tce->process_datamap();
347 // Re-load the record content:
348 $row = BackendUtility::getRecord($this->P['table'], $this->P['uid']);
349 }
350 // If the save/close or close button was pressed, then redirect the screen:
351 if (isset($_POST['saveandclosedok']) || isset($_POST['closedok'])) {
352 \TYPO3\CMS\Core\Utility\HttpUtility::redirect(GeneralUtility::sanitizeLocalUrl($this->P['returnUrl']));
353 }
354 } else {
355 // If nothing has been submitted, load the $bodyText variable from the selected database row:
356 if ($this->xmlStorage) {
357 $cfgArr = GeneralUtility::xml2array($row[$this->P['field']]);
358 } else {
359 // Regular linebased form configuration:
360 $cfgArr = $this->cfgString2CfgArray($row[$this->P['field']]);
361 }
362 $cfgArr = is_array($cfgArr) ? $cfgArr : array();
363 }
364 // Return configuration code:
365 return $cfgArr;
366 }
367
368 /**
369 * Creates the HTML for the Form Wizard:
370 *
371 * @param string $formCfgArray Form config array
372 * @param array $row Current parent record array
373 * @return string HTML for the form wizard
374 * @access private
375 */
376 public function getFormHTML($formCfgArray, $row) {
377 // Initialize variables:
378 $specParts = array();
379 $hiddenFields = array();
380 $tRows = array();
381 // Set header row:
382 $cells = array(
383 $this->getLanguageService()->getLL('forms_preview', TRUE) . ':',
384 $this->getLanguageService()->getLL('forms_element', TRUE) . ':',
385 $this->getLanguageService()->getLL('forms_config', TRUE) . ':'
386 );
387 $tRows[] = '
388 <tr class="bgColor2" id="typo3-formWizardHeader">
389 <td>&nbsp;</td>
390 <td><strong>' . implode('</strong></td>
391 <td><strong>', $cells) . '</strong></td>
392 </tr>';
393 // Traverse the number of form elements:
394 $k = 0;
395 foreach ($formCfgArray as $confData) {
396 // Initialize:
397 $cells = array();
398 // If there is a configuration line which is active, then render it:
399 if (!isset($confData['comment'])) {
400 // Special parts:
401 if ($this->special == 'formtype_mail' && GeneralUtility::inList('formtype_mail,subject,html_enabled', $confData['fieldname'])) {
402 $specParts[$confData['fieldname']] = $confData['default'];
403 } else {
404 // Render title/field preview COLUMN
405 $cells[] = $confData['type'] != 'hidden' ? '<strong>' . htmlspecialchars($confData['label']) . '</strong>' : '';
406 // Render general type/title COLUMN:
407 $temp_cells = array();
408 // Field type selector:
409 $opt = array();
410 $opt[] = '<option value=""></option>';
411 $types = explode(',', 'input,textarea,select,check,radio,password,file,hidden,submit,property,label');
412 foreach ($types as $t) {
413 $opt[] = '
414 <option value="' . $t . '"' . ($confData['type'] == $t ? ' selected="selected"' : '') . '>' . $this->getLanguageService()->getLL(('forms_type_' . $t), TRUE) . '</option>';
415 }
416 $temp_cells[$this->getLanguageService()->getLL('forms_type')] = '
417 <select name="FORMCFG[c][' . ($k + 1) * 2 . '][type]">
418 ' . implode('
419 ', $opt) . '
420 </select>';
421 // Title field:
422 if (!GeneralUtility::inList('hidden,submit', $confData['type'])) {
423 $temp_cells[$this->getLanguageService()->getLL('forms_label')] = '<input type="text"' . $this->doc->formWidth(15) . ' name="FORMCFG[c][' . ($k + 1) * 2 . '][label]" value="' . htmlspecialchars($confData['label']) . '" />';
424 }
425 // Required checkbox:
426 if (!GeneralUtility::inList('check,hidden,submit,label', $confData['type'])) {
427 $temp_cells[$this->getLanguageService()->getLL('forms_required')] = '<input type="checkbox" name="FORMCFG[c][' . ($k + 1) * 2 . '][required]" value="1"' . ($confData['required'] ? ' checked="checked"' : '') . ' title="' . $this->getLanguageService()->getLL('forms_required', TRUE) . '" />';
428 }
429 // Put sub-items together into table cell:
430 $cells[] = $this->formatCells($temp_cells);
431 // Render specific field configuration COLUMN:
432 $temp_cells = array();
433 // Fieldname
434 if ($this->special == 'formtype_mail' && $confData['type'] == 'file') {
435 $confData['fieldname'] = 'attachment' . ++$this->attachmentCounter;
436 }
437 if (!GeneralUtility::inList('label', $confData['type'])) {
438 $temp_cells[$this->getLanguageService()->getLL('forms_fieldName')] = '<input type="text"' . $this->doc->formWidth(10) . ' name="FORMCFG[c][' . ($k + 1) * 2 . '][fieldname]" value="' . htmlspecialchars($confData['fieldname']) . '" title="' . $this->getLanguageService()->getLL('forms_fieldName', TRUE) . '" />';
439 }
440 // Field configuration depending on the fields type:
441 switch ((string)$confData['type']) {
442 case 'textarea':
443 $temp_cells[$this->getLanguageService()->getLL('forms_cols')] = '<input type="text"' . $this->doc->formWidth(5) . ' name="FORMCFG[c][' . ($k + 1) * 2 . '][cols]" value="' . htmlspecialchars($confData['cols']) . '" title="' . $this->getLanguageService()->getLL('forms_cols', TRUE) . '" />';
444 $temp_cells[$this->getLanguageService()->getLL('forms_rows')] = '<input type="text"' . $this->doc->formWidth(5) . ' name="FORMCFG[c][' . ($k + 1) * 2 . '][rows]" value="' . htmlspecialchars($confData['rows']) . '" title="' . $this->getLanguageService()->getLL('forms_rows', TRUE) . '" />';
445 $temp_cells[$this->getLanguageService()->getLL('forms_extra')] = '<input type="checkbox" name="FORMCFG[c][' . ($k + 1) * 2 . '][extra]" value="OFF"' . ($confData['extra'] == 'OFF' ? ' checked="checked"' : '') . ' title="' . $this->getLanguageService()->getLL('forms_extra', TRUE) . '" />';
446 break;
447 case 'input':
448
449 case 'password':
450 $temp_cells[$this->getLanguageService()->getLL('forms_size')] = '<input type="text"' . $this->doc->formWidth(5) . ' name="FORMCFG[c][' . ($k + 1) * 2 . '][size]" value="' . htmlspecialchars($confData['size']) . '" title="' . $this->getLanguageService()->getLL('forms_size', TRUE) . '" />';
451 $temp_cells[$this->getLanguageService()->getLL('forms_max')] = '<input type="text"' . $this->doc->formWidth(5) . ' name="FORMCFG[c][' . ($k + 1) * 2 . '][max]" value="' . htmlspecialchars($confData['max']) . '" title="' . $this->getLanguageService()->getLL('forms_max', TRUE) . '" />';
452 break;
453 case 'file':
454 $temp_cells[$this->getLanguageService()->getLL('forms_size')] = '<input type="text"' . $this->doc->formWidth(5) . ' name="FORMCFG[c][' . ($k + 1) * 2 . '][size]" value="' . htmlspecialchars($confData['size']) . '" title="' . $this->getLanguageService()->getLL('forms_size', TRUE) . '" />';
455 break;
456 case 'select':
457 $temp_cells[$this->getLanguageService()->getLL('forms_size')] = '<input type="text"' . $this->doc->formWidth(5) . ' name="FORMCFG[c][' . ($k + 1) * 2 . '][size]" value="' . htmlspecialchars($confData['size']) . '" title="' . $this->getLanguageService()->getLL('forms_size', TRUE) . '" />';
458 $temp_cells[$this->getLanguageService()->getLL('forms_autosize')] = '<input type="checkbox" name="FORMCFG[c][' . ($k + 1) * 2 . '][autosize]" value="1"' . ($confData['autosize'] ? ' checked="checked"' : '') . ' title="' . $this->getLanguageService()->getLL('forms_autosize', TRUE) . '" />';
459 $temp_cells[$this->getLanguageService()->getLL('forms_multiple')] = '<input type="checkbox" name="FORMCFG[c][' . ($k + 1) * 2 . '][multiple]" value="1"' . ($confData['multiple'] ? ' checked="checked"' : '') . ' title="' . $this->getLanguageService()->getLL('forms_multiple', TRUE) . '" />';
460 break;
461 }
462 // Field configuration depending on the fields type:
463 switch ((string)$confData['type']) {
464 case 'textarea':
465
466 case 'input':
467
468 case 'password':
469 if (trim($confData['specialEval']) !== '') {
470 $hiddenFields[] = '<input type="hidden" name="FORMCFG[c][' . ($k + 1) * 2 . '][specialEval]" value="' . htmlspecialchars($confData['specialEval']) . '" />';
471 }
472 break;
473 }
474 // Default data
475 if ($confData['type'] == 'select' || $confData['type'] == 'radio') {
476 $temp_cells[$this->getLanguageService()->getLL('forms_options')] = '<textarea ' . $this->doc->formWidth(15) . ' rows="4" name="FORMCFG[c][' . ($k + 1) * 2 . '][options]" title="' . $this->getLanguageService()->getLL('forms_options', TRUE) . '">' . htmlspecialchars($confData['default']) . '</textarea>';
477 } elseif ($confData['type'] == 'check') {
478 $temp_cells[$this->getLanguageService()->getLL('forms_checked')] = '<input type="checkbox" name="FORMCFG[c][' . ($k + 1) * 2 . '][default]" value="1"' . (trim($confData['default']) ? ' checked="checked"' : '') . ' title="' . $this->getLanguageService()->getLL('forms_checked', TRUE) . '" />';
479 } elseif ($confData['type'] && $confData['type'] != 'file') {
480 $temp_cells[$this->getLanguageService()->getLL('forms_default')] = '<input type="text"' . $this->doc->formWidth(15) . ' name="FORMCFG[c][' . ($k + 1) * 2 . '][default]" value="' . htmlspecialchars($confData['default']) . '" title="' . $this->getLanguageService()->getLL('forms_default', TRUE) . '" />';
481 }
482 $cells[] = $confData['type'] ? $this->formatCells($temp_cells) : '';
483 // CTRL panel for an item (move up/down/around):
484 $ctrl = '';
485 $onClick = 'document.wizardForm.action+=\'#ANC_' . (($k + 1) * 2 - 2) . '\';';
486 $onClick = ' onclick="' . htmlspecialchars($onClick) . '"';
487 // @todo $inputStyle undefined
488 $brTag = $inputStyle ? '' : '<br />';
489 if ($k != 1) {
490 $ctrl .= '<button name="FORMCFG[row_top][' . ($k + 1) * 2 . ']"' . $onClick . ' title="' . $this->getLanguageService()->getLL('table_top', TRUE) . '">' . $this->iconFactory->getIcon('actions-move-to-top', Icon::SIZE_SMALL)->render() . '</button>' . $brTag;
491 $ctrl .= '<button name="FORMCFG[row_up][' . ($k + 1) * 2 . ']"' . $onClick . ' title="' . $this->getLanguageService()->getLL('table_up', TRUE) . '">' . $this->iconFactory->getIcon('actions-move-up', Icon::SIZE_SMALL)->render() . '</button>' . $brTag;
492 }
493 $ctrl .= '<button name="FORMCFG[row_remove][' . ($k + 1) * 2 . ']" ' . $onClick . ' title = "' . $this->getLanguageService()->getLL('table_removeRow', TRUE) . '">' . $this->iconFactory->getIcon('actions-edit-delete', Icon::SIZE_LARGE)->render() . '</button>' . $brTag;
494
495 if ($k != (count($formCfgArray)/2)) {
496 $ctrl .= '<button name="FORMCFG[row_down][' . ($k + 1) * 2 . ']"' . $onClick . ' title="' . $this->getLanguageService()->getLL('table_down', TRUE) . '">' . $this->iconFactory->getIcon('actions-move-down', Icon::SIZE_SMALL)->render() . '</button>' . $brTag;
497 $ctrl .= '<button name="FORMCFG[row_bottom][' . ($k + 1) * 2 . ']"' . $onClick . ' title="' . $this->getLanguageService()->getLL('table_bottom', TRUE) . '">' . $this->iconFactory->getIcon('actions-move-to-bottom', Icon::SIZE_SMALL)->render() . '</button>' . $brTag;
498 }
499
500 $ctrl .= '<button name="FORMCFG[row_add][' . ($k + 1) * 2 . ']"' . $onClick . ' title="' . $this->getLanguageService()->getLL('table_addRow', TRUE) . '">' . $this->iconFactory->getIcon('actions-template-new', Icon::SIZE_SMALL)->render() . '</button>' . $brTag;
501 $ctrl = '<span class="c-wizButtonsV">' . $ctrl . '</span>';
502 // Finally, put together the full row from the generated content above:
503 $bgC = $confData['type'] ? ' class="bgColor5"' : '';
504 $tRows[] = '
505 <tr' . $bgC . '>
506 <td><a name="ANC_' . ($k + 1) * 2 . '"></a>' . $ctrl . '</td>
507 <td class="bgColor4">' . implode('</td>
508 <td valign="top">', $cells) . '</td>
509 </tr>';
510 }
511 } else {
512 $hiddenFields[] = '<input type="hidden" name="FORMCFG[c][' . ($k + 1) * 2 . '][comment]" value="' . htmlspecialchars($confData['comment']) . '" />';
513 }
514 // Increment counter:
515 $k++;
516 }
517 // If the form is of the special type "formtype_mail" (used for tt_content elements):
518 if ($this->special == 'formtype_mail') {
519 // Blank spacer:
520 $tRows[] = '
521 <tr>
522 <td colspan="4">&nbsp;</td>
523 </tr>';
524 // Header:
525 $tRows[] = '
526 <tr>
527 <td colspan="2" class="bgColor2">&nbsp;</td>
528 <td colspan="2" class="bgColor2"><strong>' . $this->getLanguageService()->getLL('forms_special_eform', TRUE) . ':</strong>' . BackendUtility::cshItem('xMOD_csh_corebe', 'wizard_forms_wiz_formmail_info') . '</td>
529 </tr>';
530 // "FORM type":
531 $tRows[] = '
532 <tr class="bgColor5">
533 <td>&nbsp;</td>
534 <td class="bgColor4">&nbsp;</td>
535 <td>' . $this->getLanguageService()->getLL('forms_eform_formtype_mail', TRUE) . ':</td>
536 <td>
537 <input type="hidden" name="FORMCFG[c][' . 1000 * 2 . '][fieldname]" value="formtype_mail" />
538 <input type="hidden" name="FORMCFG[c][' . 1000 * 2 . '][type]" value="submit" />
539 <input type="text"' . $this->doc->formWidth(15) . ' name="FORMCFG[c][' . 1000 * 2 . '][default]" value="' . htmlspecialchars($specParts['formtype_mail']) . '" />
540 </td>
541 </tr>';
542 // "Send HTML mail":
543 $tRows[] = '
544 <tr class="bgColor5">
545 <td>&nbsp;</td>
546 <td class="bgColor4">&nbsp;</td>
547 <td>' . $this->getLanguageService()->getLL('forms_eform_html_enabled', TRUE) . ':</td>
548 <td>
549 <input type="hidden" name="FORMCFG[c][' . 1001 * 2 . '][fieldname]" value="html_enabled" />
550 <input type="hidden" name="FORMCFG[c][' . 1001 * 2 . '][type]" value="hidden" />
551 <input type="checkbox" name="FORMCFG[c][' . 1001 * 2 . '][default]" value="1"' . ($specParts['html_enabled'] ? ' checked="checked"' : '') . ' />
552 </td>
553 </tr>';
554 // "Subject":
555 $tRows[] = '
556 <tr class="bgColor5">
557 <td>&nbsp;</td>
558 <td class="bgColor4">&nbsp;</td>
559 <td>' . $this->getLanguageService()->getLL('forms_eform_subject', TRUE) . ':</td>
560 <td>
561 <input type="hidden" name="FORMCFG[c][' . 1002 * 2 . '][fieldname]" value="subject" />
562 <input type="hidden" name="FORMCFG[c][' . 1002 * 2 . '][type]" value="hidden" />
563 <input type="text"' . $this->doc->formWidth(15) . ' name="FORMCFG[c][' . 1002 * 2 . '][default]" value="' . htmlspecialchars($specParts['subject']) . '" />
564 </td>
565 </tr>';
566 // Recipient:
567 $tRows[] = '
568 <tr class="bgColor5">
569 <td>&nbsp;</td>
570 <td class="bgColor4">&nbsp;</td>
571 <td>' . $this->getLanguageService()->getLL('forms_eform_recipient', TRUE) . ':</td>
572 <td>
573 <input type="text"' . $this->doc->formWidth(15) . ' name="FORMCFG[recipient]" value="' . htmlspecialchars($row['subheader']) . '" />
574 </td>
575 </tr>';
576 }
577 $content = '';
578 // Implode all table rows into a string, wrapped in table tags.
579 $content .= '
580
581 <!--
582 Form wizard
583 -->
584 <table border="0" cellpadding="1" cellspacing="1" id="typo3-formwizard">
585 ' . implode('', $tRows) . '
586 </table>';
587 // Add hidden fields:
588 $content .= implode('', $hiddenFields);
589 // Return content:
590 return $content;
591 }
592
593 /**
594 * Detects if a control button (up/down/around/delete) has been pressed for an item and accordingly it will manipulate the internal FORMCFG array
595 *
596 * @return void
597 * @access private
598 */
599 public function changeFunc() {
600 if ($this->FORMCFG['row_remove']) {
601 $kk = key($this->FORMCFG['row_remove']);
602 $cmd = 'row_remove';
603 } elseif ($this->FORMCFG['row_add']) {
604 $kk = key($this->FORMCFG['row_add']);
605 $cmd = 'row_add';
606 } elseif ($this->FORMCFG['row_top']) {
607 $kk = key($this->FORMCFG['row_top']);
608 $cmd = 'row_top';
609 } elseif ($this->FORMCFG['row_bottom']) {
610 $kk = key($this->FORMCFG['row_bottom']);
611 $cmd = 'row_bottom';
612 } elseif ($this->FORMCFG['row_up']) {
613 $kk = key($this->FORMCFG['row_up']);
614 $cmd = 'row_up';
615 } elseif ($this->FORMCFG['row_down']) {
616 $kk = key($this->FORMCFG['row_down']);
617 $cmd = 'row_down';
618 }
619 if ($cmd && \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($kk)) {
620 if (substr($cmd, 0, 4) == 'row_') {
621 switch ($cmd) {
622 case 'row_remove':
623 unset($this->FORMCFG['c'][$kk]);
624 break;
625 case 'row_add':
626 $this->FORMCFG['c'][$kk + 1] = array();
627 break;
628 case 'row_top':
629 $this->FORMCFG['c'][1] = $this->FORMCFG['c'][$kk];
630 unset($this->FORMCFG['c'][$kk]);
631 break;
632 case 'row_bottom':
633 $this->FORMCFG['c'][1000000] = $this->FORMCFG['c'][$kk];
634 unset($this->FORMCFG['c'][$kk]);
635 break;
636 case 'row_up':
637 $this->FORMCFG['c'][$kk - 3] = $this->FORMCFG['c'][$kk];
638 unset($this->FORMCFG['c'][$kk]);
639 break;
640 case 'row_down':
641 $this->FORMCFG['c'][$kk + 3] = $this->FORMCFG['c'][$kk];
642 unset($this->FORMCFG['c'][$kk]);
643 break;
644 }
645 ksort($this->FORMCFG['c']);
646 }
647 }
648 }
649
650 /**
651 * Converts the input array to a configuration code string
652 *
653 * @param array $cfgArr Array of form configuration (follows the input structure from the form wizard POST form)
654 * @return string The array converted into a string with line-based configuration.
655 * @see cfgString2CfgArray()
656 */
657 public function cfgArray2CfgString($cfgArr) {
658 // Initialize:
659 $inLines = array();
660 // Traverse the elements of the form wizard and transform the settings into configuration code.
661 foreach ($cfgArr as $vv) {
662 // If "content" is found, then just pass it over.
663 if ($vv['comment']) {
664 $inLines[] = trim($vv['comment']);
665 } else {
666 // Begin to put together the single-line configuration code of this field:
667 // Reset:
668 $thisLine = array();
669 // Set Label:
670 $thisLine[0] = str_replace('|', '', $vv['label']);
671 // Set Type:
672 if ($vv['type']) {
673 $thisLine[1] = ($vv['required'] ? '*' : '') . str_replace(',', '', (($vv['fieldname'] ? $vv['fieldname'] . '=' : '') . $vv['type']));
674 // Default:
675 $tArr = array('', '', '', '', '', '');
676 switch ((string)$vv['type']) {
677 case 'textarea':
678 if ((int)$vv['cols']) {
679 $tArr[0] = (int)$vv['cols'];
680 }
681 if ((int)$vv['rows']) {
682 $tArr[1] = (int)$vv['rows'];
683 }
684 if (trim($vv['extra'])) {
685 $tArr[2] = trim($vv['extra']);
686 }
687 if ($vv['specialEval'] !== '') {
688 // Preset blank default value so position 3 can get a value...
689 $thisLine[2] = '';
690 $thisLine[3] = $vv['specialEval'];
691 }
692 break;
693 case 'input':
694 case 'password':
695 if ((int)$vv['size']) {
696 $tArr[0] = (int)$vv['size'];
697 }
698 if ((int)$vv['max']) {
699 $tArr[1] = (int)$vv['max'];
700 }
701 if ($vv['specialEval'] !== '') {
702 // Preset blank default value so position 3 can get a value...
703 $thisLine[2] = '';
704 $thisLine[3] = $vv['specialEval'];
705 }
706 break;
707 case 'file':
708 if ((int)$vv['size']) {
709 $tArr[0] = (int)$vv['size'];
710 }
711 break;
712 case 'select':
713 if ((int)$vv['size']) {
714 $tArr[0] = (int)$vv['size'];
715 }
716 if ($vv['autosize']) {
717 $tArr[0] = 'auto';
718 }
719 if ($vv['multiple']) {
720 $tArr[1] = 'm';
721 }
722 break;
723 }
724 $tArr = $this->cleanT($tArr);
725 if (!empty($tArr)) {
726 $thisLine[1] .= ',' . implode(',', $tArr);
727 }
728 $thisLine[1] = str_replace('|', '', $thisLine[1]);
729 // Default:
730 if ($vv['type'] == 'select' || $vv['type'] == 'radio') {
731 $options = str_replace(',', '', $vv['options']);
732 $options = str_replace(
733 array(CRLF, CR, LF),
734 ', ',
735 $options);
736 $thisLine[2] = $options;
737 } elseif ($vv['type'] == 'check') {
738 if ($vv['default']) {
739 $thisLine[2] = 1;
740 }
741 } elseif (trim($vv['default']) !== '') {
742 $thisLine[2] = $vv['default'];
743 }
744 if (isset($thisLine[2])) {
745 $thisLine[2] = str_replace('|', '', $thisLine[2]);
746 }
747 }
748 // Compile the final line:
749 $inLines[] = preg_replace('/[
750
751 ]*/', '', implode(' | ', $thisLine));
752 }
753 }
754 // Finally, implode the lines into a string, and return it:
755 return implode(LF, $inLines);
756 }
757
758 /**
759 * Converts the input configuration code string into an array
760 *
761 * @param string $cfgStr Configuration code
762 * @return array Configuration array
763 * @see cfgArray2CfgString()
764 */
765 public function cfgString2CfgArray($cfgStr) {
766 // Traverse the number of form elements:
767 $tLines = explode(LF, $cfgStr);
768 $attachmentCounter = 0;
769 foreach ($tLines as $k => $v) {
770 // Initialize:
771 $confData = array();
772 $val = trim($v);
773 // Accept a line as configuration if a) it is blank(! - because blank lines indicates new,
774 // unconfigured fields) or b) it is NOT a comment.
775 if (!$val || strcspn($val, '#/')) {
776 // Split:
777 $parts = GeneralUtility::trimExplode('|', $val);
778 // Label:
779 $confData['label'] = trim($parts[0]);
780 // Field:
781 $fParts = GeneralUtility::trimExplode(',', $parts[1]);
782 $fParts[0] = trim($fParts[0]);
783 if ($fParts[0][0] === '*') {
784 $confData['required'] = 1;
785 $fParts[0] = substr($fParts[0], 1);
786 }
787 $typeParts = GeneralUtility::trimExplode('=', $fParts[0]);
788 $confData['type'] = trim(strtolower(end($typeParts)));
789 if ($confData['type']) {
790 if (count($typeParts) === 1) {
791 $confData['fieldname'] = substr(preg_replace('/[^a-zA-Z0-9_]/', '', str_replace(' ', '_', trim($parts[0]))), 0, 30);
792 // Attachment names...
793 if ($confData['type'] == 'file') {
794 $confData['fieldname'] = 'attachment' . $attachmentCounter;
795 $attachmentCounter = (int)$attachmentCounter + 1;
796 }
797 } else {
798 $confData['fieldname'] = str_replace(' ', '_', trim($typeParts[0]));
799 }
800 switch ((string)$confData['type']) {
801 case 'select':
802 case 'radio':
803 $confData['default'] = implode(LF, GeneralUtility::trimExplode(',', $parts[2]));
804 break;
805 default:
806 $confData['default'] = trim($parts[2]);
807 }
808 // Field configuration depending on the fields type:
809 switch ((string)$confData['type']) {
810 case 'textarea':
811 $confData['cols'] = $fParts[1];
812 $confData['rows'] = $fParts[2];
813 $confData['extra'] = strtoupper($fParts[3]) == 'OFF' ? 'OFF' : '';
814 $confData['specialEval'] = trim($parts[3]);
815 break;
816 case 'input':
817 case 'password':
818 $confData['size'] = $fParts[1];
819 $confData['max'] = $fParts[2];
820 $confData['specialEval'] = trim($parts[3]);
821 break;
822 case 'file':
823 $confData['size'] = $fParts[1];
824 break;
825 case 'select':
826 $confData['size'] = (int)$fParts[1] ? $fParts[1] : '';
827 $confData['autosize'] = strtolower(trim($fParts[1])) === 'auto' ? 1 : 0;
828 $confData['multiple'] = strtolower(trim($fParts[2])) === 'm' ? 1 : 0;
829 break;
830 }
831 }
832 } else {
833 // No configuration, only a comment:
834 $confData = array(
835 'comment' => $val
836 );
837 }
838 // Adding config array:
839 $cfgArr[] = $confData;
840 }
841 // Return cfgArr
842 return $cfgArr;
843 }
844
845 /**
846 * Removes any "trailing elements" in the array which consists of whitespace (little like trim() does for strings, so this does for arrays)
847 *
848 * @param array $tArr Single dim array
849 * @return array Processed array
850 * @access private
851 */
852 public function cleanT($tArr) {
853 for ($a = count($tArr); $a > 0; $a--) {
854 if ((string)$tArr[$a - 1] !== '') {
855 break;
856 } else {
857 unset($tArr[$a - 1]);
858 }
859 }
860 return $tArr;
861 }
862
863 /**
864 * Wraps items in $fArr in table cells/rows, displaying them vertically.
865 *
866 * @param array $fArr Array of label/HTML pairs.
867 * @return string HTML table
868 * @access private
869 */
870 public function formatCells($fArr) {
871 // Traverse the elements in $fArr and wrap them in table cells:
872 $lines = array();
873 foreach ($fArr as $l => $c) {
874 $lines[] = '
875 <tr>
876 <td nowrap="nowrap">' . htmlspecialchars(($l . ':')) . '&nbsp;</td>
877 <td>' . $c . '</td>
878 </tr>';
879 }
880 // Add a cell which will set a minimum width:
881 $lines[] = '
882 <tr>
883 <td nowrap="nowrap"><span style="width: 70px; height: 1px;"></span></td>
884 <td></td>
885 </tr>';
886 // Wrap in table and return:
887 return '
888 <table border="0" cellpadding="0" cellspacing="0">
889 ' . implode('', $lines) . '
890 </table>';
891 }
892
893 }