2 /***************************************************************
5 * (c) 1999-2011 Kasper Skårhøj (kasperYYYY@typo3.com)
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
28 * Wizard to help make tables (eg. for tt_content elements) of type "table".
29 * Each line is a table row, each cell divided by a |
31 * Revised for TYPO3 3.6 November/2003 by Kasper Skårhøj
34 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
41 require ('template.php');
42 $GLOBALS['LANG']->includeLLFile('EXT:lang/locallang_wizards.xml');
55 * Script Class for rendering the Table Wizard
57 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
61 class SC_wizard_table
{
65 * document template object
70 var $content; // Content accumulation for the module.
71 var $include_once=array(); // List of files to include.
72 var $inputStyle=0; // TRUE, then <input> fields are shown, not textareas.
76 var $xmlStorage=0; // If set, the string version of the content is interpreted/written as XML instead of the original linebased kind. This variable still needs binding to the wizard parameters - but support is ready!
77 var $numNewRows=1; // Number of new rows to add in bottom of wizard
78 var $colsFieldName='cols'; // Name of field in parent record which MAY contain the number of columns for the table - here hardcoded to the value of tt_content. Should be set by TCEform parameters (from P)
81 // Internal, static: GPvars
82 var $P; // Wizard parameters, coming from TCEforms linking to the wizard.
83 var $TABLECFG; // The array which is constantly submitted by the multidimensional form of this wizard.
86 var $tableParsing_quote; // quoting of table cells
87 var $tableParsing_delimiter; // delimiter between table cells
94 * Initialization of the class
100 $this->P
= t3lib_div
::_GP('P');
101 $this->TABLECFG
= t3lib_div
::_GP('TABLE');
104 $this->xmlStorage
= $this->P
['params']['xmlOutput'];
105 $this->numNewRows
= t3lib_utility_Math
::forceIntegerInRange($this->P
['params']['numNewRows'],1,50,5);
107 // Textareas or input fields:
108 $this->inputStyle
=isset($this->TABLECFG
['textFields']) ?
$this->TABLECFG
['textFields'] : 1;
110 // Document template object:
111 $this->doc
= t3lib_div
::makeInstance('template');
112 $this->doc
->backPath
= $GLOBALS['BACK_PATH'];
113 $this->doc
->setModuleTemplate('templates/wizard_table.html');
114 $this->doc
->JScode
=$this->doc
->wrapScriptTags('
115 function jumpToUrl(URL,formEl) { //
116 window.location.href = URL;
121 list($rUri) = explode('#',t3lib_div
::getIndpEnv('REQUEST_URI'));
122 $this->doc
->form
='<form action="'.htmlspecialchars($rUri).'" method="post" name="wizardForm">';
124 // If save command found, include tcemain:
125 if ($_POST['savedok_x'] ||
$_POST['saveandclosedok_x']) {
126 $this->include_once[]=PATH_t3lib
.'class.t3lib_tcemain.php';
129 $this->tableParsing_delimiter
= '|';
130 $this->tableParsing_quote
= '';
134 * Main function, rendering the table wizard
139 if ($this->P
['table'] && $this->P
['field'] && $this->P
['uid']) {
140 $this->content
.= $this->doc
->section($GLOBALS['LANG']->getLL('table_title'), $this->tableWizard(), 0, 1);
142 $this->content
.= $this->doc
->section($GLOBALS['LANG']->getLL('table_title'), '<span class="typo3-red">' . $GLOBALS['LANG']->getLL('table_noData',1) . '</span>', 0, 1);
145 // Setting up the buttons and markers for docheader
146 $docHeaderButtons = $this->getButtons();
147 $markers['CSH'] = $docHeaderButtons['csh'];
148 $markers['CONTENT'] = $this->content
;
150 // Build the <body> for the module
151 $this->content
= $this->doc
->startPage('Table');
152 $this->content
.= $this->doc
->moduleBody($this->pageinfo
, $docHeaderButtons, $markers);
153 $this->content
.= $this->doc
->endPage();
154 $this->content
= $this->doc
->insertStylesAndJS($this->content
);
158 * Outputting the accumulated content to screen
162 function printContent() {
167 * Create the panel of buttons for submitting the form or otherwise perform operations.
169 * @return array all available buttons as an assoc. array
171 protected function getButtons() {
181 if ($this->P
['table'] && $this->P
['field'] && $this->P
['uid']) {
183 $buttons['csh'] = t3lib_BEfunc
::cshItem('xMOD_csh_corebe', 'wizard_table_wiz', $GLOBALS['BACK_PATH'], '');
186 $buttons['csh_buttons'] = t3lib_BEfunc
::cshItem('xMOD_csh_corebe', 'wizard_table_wiz_buttons', $GLOBALS['BACK_PATH'], '');
189 $buttons['close'] = '<a href="#" onclick="' . htmlspecialchars('jumpToUrl(unescape(\'' . rawurlencode(t3lib_div
::sanitizeLocalUrl($this->P
['returnUrl'])) . '\')); return false;') . '">' .
190 t3lib_iconWorks
::getSpriteIcon('actions-document-close', array('title' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:rm.closeDoc', TRUE
))) .
194 $buttons['save'] = '<input type="image" class="c-inputButton" name="savedok"' . t3lib_iconWorks
::skinImg($this->doc
->backPath
, 'gfx/savedok.gif') . ' title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:rm.saveDoc', 1) . '" />';
197 $buttons['save_close'] = '<input type="image" class="c-inputButton" name="saveandclosedok"' . t3lib_iconWorks
::skinImg($this->doc
->backPath
, 'gfx/saveandclosedok.gif') . ' title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:rm.saveCloseDoc', 1) . '" />';
200 $buttons['reload'] = '<input type="image" class="c-inputButton" name="_refresh"' . t3lib_iconWorks
::skinImg($this->doc
->backPath
, 'gfx/refresh_n.gif') . ' title="' . $GLOBALS['LANG']->getLL('forms_refresh', 1) . '" />';
207 * Draws the table wizard content
209 * @return string HTML content for the form.
211 function tableWizard() {
213 // First, check the references by selecting the record:
214 $row = t3lib_BEfunc
::getRecord($this->P
['table'],$this->P
['uid']);
215 if (!is_array($row)) {
216 throw new RuntimeException('Wizard Error: No reference to record', 1294587125);
219 // This will get the content of the form configuration code field to us - possibly cleaned up, saved to database etc. if the form has been submitted in the meantime.
220 $tableCfgArray = $this->getConfigCode($row);
222 // Generation of the Table Wizards HTML code:
223 $content = $this->getTableHTML($tableCfgArray,$row);
235 /***************************
239 ***************************/
242 * Will get and return the configuration code string
243 * Will also save (and possibly redirect/exit) the content if a save button has been pressed
245 * @param array Current parent record row
246 * @return array Table config code in an array
249 function getConfigCode($row) {
251 // get delimiter settings
252 $flexForm = t3lib_div
::xml2array($row['pi_flexform']);
254 if (is_array($flexForm)) {
255 $this->tableParsing_quote
= $flexForm['data']['s_parsing']['lDEF']['tableparsing_quote']['vDEF']?
chr(intval($flexForm['data']['s_parsing']['lDEF']['tableparsing_quote']['vDEF'])):'';
256 $this->tableParsing_delimiter
= $flexForm['data']['s_parsing']['lDEF']['tableparsing_delimiter']['vDEF']?
chr(intval($flexForm['data']['s_parsing']['lDEF']['tableparsing_delimiter']['vDEF'])):'|';
259 // If some data has been submitted, then construct
260 if (isset($this->TABLECFG
['c'])) {
266 // Convert to string (either line based or XML):
267 if ($this->xmlStorage
) {
268 // Convert the input array to XML:
269 $bodyText = t3lib_div
::array2xml_cs($this->TABLECFG
['c'],'T3TableWizard');
271 // Setting cfgArr directly from the input:
272 $cfgArr = $this->TABLECFG
['c'];
274 // Convert the input array to a string of configuration code:
275 $bodyText = $this->cfgArray2CfgString($this->TABLECFG
['c']);
277 // Create cfgArr from the string based configuration - that way it is cleaned up and any incompatibilities will be removed!
278 $cfgArr = $this->cfgString2CfgArray($bodyText,$row[$this->colsFieldName
]);
281 // If a save button has been pressed, then save the new field content:
282 if ($_POST['savedok_x'] ||
$_POST['saveandclosedok_x']) {
284 // Make TCEmain object:
285 $tce = t3lib_div
::makeInstance('t3lib_TCEmain');
286 $tce->stripslashes_values
=0;
288 // Put content into the data array:
290 $data[$this->P
['table']][$this->P
['uid']][$this->P
['field']]=$bodyText;
292 // Perform the update:
293 $tce->start($data,array());
294 $tce->process_datamap();
296 // If the save/close button was pressed, then redirect the screen:
297 if ($_POST['saveandclosedok_x']) {
298 t3lib_utility_Http
::redirect(t3lib_div
::sanitizeLocalUrl($this->P
['returnUrl']));
301 } else { // If nothing has been submitted, load the $bodyText variable from the selected database row:
302 if ($this->xmlStorage
) {
303 $cfgArr = t3lib_div
::xml2array($row[$this->P
['field']]);
304 } else { // Regular linebased table configuration:
305 $cfgArr = $this->cfgString2CfgArray($row[$this->P
['field']],$row[$this->colsFieldName
]);
307 $cfgArr = is_array($cfgArr) ?
$cfgArr : array();
314 * Creates the HTML for the Table Wizard:
316 * @param array Table config array
317 * @param array Current parent record array
318 * @return string HTML for the table wizard
321 function getTableHTML($cfgArr,$row) {
322 // Traverse the rows:
325 foreach($cfgArr as $cellArr) {
326 if (is_array($cellArr)) {
331 // Traverse the columns:
332 foreach($cellArr as $cellContent) {
333 if ($this->inputStyle
) {
334 $cells[]='<input type="text"'.$this->doc
->formWidth(20).' name="TABLE[c]['.(($k+
1)*2).']['.(($a+
1)*2).']" value="'.htmlspecialchars($cellContent).'" />';
336 $cellContent=preg_replace('/<br[ ]?[\/]?>/i',LF
,$cellContent);
337 $cells[]='<textarea '.$this->doc
->formWidth(20).' rows="5" name="TABLE[c]['.(($k+
1)*2).']['.(($a+
1)*2).']">'.t3lib_div
::formatForTextarea($cellContent).'</textarea>';
340 // Increment counter:
344 // CTRL panel for a table row (move up/down/around):
345 $onClick="document.wizardForm.action+='#ANC_".(($k+
1)*2-2)."';";
346 $onClick=' onclick="'.htmlspecialchars($onClick).'"';
349 $brTag=$this->inputStyle?
'':'<br />';
351 $ctrl.='<input type="image" name="TABLE[row_up]['.(($k+
1)*2).']"'.t3lib_iconWorks
::skinImg($this->doc
->backPath
,'gfx/pil2up.gif','').$onClick.' title="'.$GLOBALS['LANG']->getLL('table_up',1).'" />'.$brTag;
353 $ctrl.='<input type="image" name="TABLE[row_bottom]['.(($k+
1)*2).']"'.t3lib_iconWorks
::skinImg($this->doc
->backPath
,'gfx/turn_up.gif','').$onClick.' title="'.$GLOBALS['LANG']->getLL('table_bottom',1).'" />'.$brTag;
355 $ctrl.='<input type="image" name="TABLE[row_remove]['.(($k+
1)*2).']"'.t3lib_iconWorks
::skinImg($this->doc
->backPath
,'gfx/garbage.gif','').$onClick.' title="'.$GLOBALS['LANG']->getLL('table_removeRow',1).'" />'.$brTag;
357 // FIXME what is $tLines? See wizard_forms.php for the same.
358 if (($k+
1)!=count($tLines)) {
359 $ctrl.='<input type="image" name="TABLE[row_down]['.(($k+
1)*2).']"'.t3lib_iconWorks
::skinImg($this->doc
->backPath
,'gfx/pil2down.gif','').$onClick.' title="'.$GLOBALS['LANG']->getLL('table_down',1).'" />'.$brTag;
361 $ctrl.='<input type="image" name="TABLE[row_top]['.(($k+
1)*2).']"'.t3lib_iconWorks
::skinImg($this->doc
->backPath
,'gfx/turn_down.gif','').$onClick.' title="'.$GLOBALS['LANG']->getLL('table_top',1).'" />'.$brTag;
363 $ctrl.='<input type="image" name="TABLE[row_add]['.(($k+
1)*2).']"'.t3lib_iconWorks
::skinImg($this->doc
->backPath
,'gfx/add.gif','').$onClick.' title="'.$GLOBALS['LANG']->getLL('table_addRow',1).'" />'.$brTag;
366 <tr class="bgColor4">
367 <td class="bgColor5"><a name="ANC_'.(($k+
1)*2).'"></a><span class="c-wizButtonsV">'.$ctrl.'</span></td>
372 // Increment counter:
377 // CTRL panel for a table column (move left/right/around/delete)
380 // Finding first row:
381 $firstRow = reset($cfgArr);
382 if (is_array($firstRow)) {
386 $cols=count($firstRow);
388 // Traverse first row:
389 foreach($firstRow as $temp) {
392 $ctrl.='<input type="image" name="TABLE[col_left]['.(($a+
1)*2).']"'.t3lib_iconWorks
::skinImg($this->doc
->backPath
,'gfx/pil2left.gif','').' title="'.$GLOBALS['LANG']->getLL('table_left',1).'" />';
394 $ctrl.='<input type="image" name="TABLE[col_end]['.(($a+
1)*2).']"'.t3lib_iconWorks
::skinImg($this->doc
->backPath
,'gfx/turn_left.gif','').' title="'.$GLOBALS['LANG']->getLL('table_end',1).'" />';
396 $ctrl.='<input type="image" name="TABLE[col_remove]['.(($a+
1)*2).']"'.t3lib_iconWorks
::skinImg($this->doc
->backPath
,'gfx/garbage.gif','').' title="'.$GLOBALS['LANG']->getLL('table_removeColumn',1).'" />';
398 $ctrl.='<input type="image" name="TABLE[col_right]['.(($a+
1)*2).']"'.t3lib_iconWorks
::skinImg($this->doc
->backPath
,'gfx/pil2right.gif','').' title="'.$GLOBALS['LANG']->getLL('table_right',1).'" />';
400 $ctrl.='<input type="image" name="TABLE[col_start]['.(($a+
1)*2).']"'.t3lib_iconWorks
::skinImg($this->doc
->backPath
,'gfx/turn_right.gif','').' title="'.$GLOBALS['LANG']->getLL('table_start',1).'" />';
402 $ctrl.='<input type="image" name="TABLE[col_add]['.(($a+
1)*2).']"'.t3lib_iconWorks
::skinImg($this->doc
->backPath
,'gfx/add.gif','').' title="'.$GLOBALS['LANG']->getLL('table_addColumn',1).'" />';
403 $cells[]='<span class="c-wizButtonsH">'.$ctrl.'</span>';
409 <tr class="bgColor5">
410 <td align="center">'.implode('</td>
411 <td align="center">',$cells).'</td>
417 // Implode all table rows into a string, wrapped in table tags.
424 <table border="0" cellpadding="0" cellspacing="1" id="typo3-tablewizard">
425 '.implode('',$tRows).'
428 // Input type checkbox:
432 Input mode check box:
434 <div id="c-inputMode">
436 '<input type="hidden" name="TABLE[textFields]" value="0" />'.
437 '<input type="checkbox" name="TABLE[textFields]" id="textFields" value="1"'.($this->inputStyle?
' checked="checked"':'').' /> <label for="textFields">'.
438 $GLOBALS['LANG']->getLL('table_smallFields').'</label>
449 * Detects if a control button (up/down/around/delete) has been pressed for an item and accordingly it will manipulate the internal TABLECFG array
454 function changeFunc() {
455 if ($this->TABLECFG
['col_remove']) {
456 $kk = key($this->TABLECFG
['col_remove']);
458 } elseif ($this->TABLECFG
['col_add']) {
459 $kk = key($this->TABLECFG
['col_add']);
461 } elseif ($this->TABLECFG
['col_start']) {
462 $kk = key($this->TABLECFG
['col_start']);
464 } elseif ($this->TABLECFG
['col_end']) {
465 $kk = key($this->TABLECFG
['col_end']);
467 } elseif ($this->TABLECFG
['col_left']) {
468 $kk = key($this->TABLECFG
['col_left']);
470 } elseif ($this->TABLECFG
['col_right']) {
471 $kk = key($this->TABLECFG
['col_right']);
473 } elseif ($this->TABLECFG
['row_remove']) {
474 $kk = key($this->TABLECFG
['row_remove']);
476 } elseif ($this->TABLECFG
['row_add']) {
477 $kk = key($this->TABLECFG
['row_add']);
479 } elseif ($this->TABLECFG
['row_top']) {
480 $kk = key($this->TABLECFG
['row_top']);
482 } elseif ($this->TABLECFG
['row_bottom']) {
483 $kk = key($this->TABLECFG
['row_bottom']);
485 } elseif ($this->TABLECFG
['row_up']) {
486 $kk = key($this->TABLECFG
['row_up']);
488 } elseif ($this->TABLECFG
['row_down']) {
489 $kk = key($this->TABLECFG
['row_down']);
493 if ($cmd && t3lib_utility_Math
::canBeInterpretedAsInteger($kk)) {
494 if (substr($cmd,0,4)=='row_') {
497 unset($this->TABLECFG
['c'][$kk]);
500 for($a=1;$a<=$this->numNewRows
;$a++
) {
501 if (!isset($this->TABLECFG
['c'][$kk+
$a])) { // Checking if set: The point is that any new row inbetween existing rows will be TRUE after one row is added while if rows are added in the bottom of the table there will be no existing rows to stop the addition of new rows which means it will add up to $this->numNewRows rows then.
502 $this->TABLECFG
['c'][$kk+
$a] = array();
509 $this->TABLECFG
['c'][1]=$this->TABLECFG
['c'][$kk];
510 unset($this->TABLECFG
['c'][$kk]);
513 $this->TABLECFG
['c'][10000000]=$this->TABLECFG
['c'][$kk];
514 unset($this->TABLECFG
['c'][$kk]);
517 $this->TABLECFG
['c'][$kk-3]=$this->TABLECFG
['c'][$kk];
518 unset($this->TABLECFG
['c'][$kk]);
521 $this->TABLECFG
['c'][$kk+
3]=$this->TABLECFG
['c'][$kk];
522 unset($this->TABLECFG
['c'][$kk]);
525 ksort($this->TABLECFG
['c']);
527 if (substr($cmd,0,4)=='col_') {
528 foreach ($this->TABLECFG
['c'] as $cAK => $value) {
531 unset($this->TABLECFG
['c'][$cAK][$kk]);
534 $this->TABLECFG
['c'][$cAK][$kk+
1]='';
537 $this->TABLECFG
['c'][$cAK][1]=$this->TABLECFG
['c'][$cAK][$kk];
538 unset($this->TABLECFG
['c'][$cAK][$kk]);
541 $this->TABLECFG
['c'][$cAK][1000000]=$this->TABLECFG
['c'][$cAK][$kk];
542 unset($this->TABLECFG
['c'][$cAK][$kk]);
545 $this->TABLECFG
['c'][$cAK][$kk-3]=$this->TABLECFG
['c'][$cAK][$kk];
546 unset($this->TABLECFG
['c'][$cAK][$kk]);
549 $this->TABLECFG
['c'][$cAK][$kk+
3]=$this->TABLECFG
['c'][$cAK][$kk];
550 unset($this->TABLECFG
['c'][$cAK][$kk]);
553 ksort($this->TABLECFG
['c'][$cAK]);
558 // Convert line breaks to <br /> tags:
559 foreach ($this->TABLECFG
['c'] as $a => $value) {
560 foreach ($this->TABLECFG
['c'][$a] as $b => $value2) {
561 $this->TABLECFG
['c'][$a][$b] = str_replace(LF
,'<br />',str_replace(CR
,'',$this->TABLECFG
['c'][$a][$b]));
567 * Converts the input array to a configuration code string
569 * @param array Array of table configuration (follows the input structure from the table wizard POST form)
570 * @return string The array converted into a string with line-based configuration.
571 * @see cfgString2CfgArray()
573 function cfgArray2CfgString($cfgArr) {
578 // Traverse the elements of the table wizard and transform the settings into configuration code.
579 foreach ($this->TABLECFG
['c'] as $a => $value) {
581 foreach ($this->TABLECFG
['c'][$a] as $b => $value) {
582 $thisLine[]=$this->tableParsing_quote
.str_replace($this->tableParsing_delimiter
,'',$this->TABLECFG
['c'][$a][$b]).$this->tableParsing_quote
;
584 $inLines[]=implode($this->tableParsing_delimiter
,$thisLine);
587 // Finally, implode the lines into a string:
588 $bodyText = implode(LF
,$inLines);
590 // Return the configuration code:
595 * Converts the input configuration code string into an array
597 * @param string Configuration code
598 * @param integer Default number of columns
599 * @return array Configuration array
600 * @see cfgArray2CfgString()
602 function cfgString2CfgArray($cfgStr,$cols) {
604 // Explode lines in the configuration code - each line is a table row.
605 $tLines=explode(LF
,$cfgStr);
607 // Setting number of columns
608 if (!$cols && trim($tLines[0])) { // auto...
609 $cols = count(explode($this->tableParsing_delimiter
,$tLines[0]));
613 // Traverse the number of table elements:
615 foreach($tLines as $k => $v) {
618 $vParts = explode($this->tableParsing_delimiter
,$v);
621 for ($a=0;$a<$cols;$a++
) {
622 if ($this->tableParsing_quote
&& substr($vParts[$a],0,1) == $this->tableParsing_quote
&& substr($vParts[$a],-1,1) == $this->tableParsing_quote
) {
623 $vParts[$a] = substr(trim($vParts[$a]),1,-1);
625 $cfgArr[$k][$a]=$vParts[$a];
629 // Return configuration array:
635 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE
]['XCLASS']['typo3/wizard_table.php'])) {
636 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE
]['XCLASS']['typo3/wizard_table.php']);
642 $SOBE = t3lib_div
::makeInstance('SC_wizard_table');
646 foreach($SOBE->include_once as $INC_FILE) include_once($INC_FILE);
649 $SOBE->printContent();