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