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