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