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