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