015a918073bf84bf2ea313613e28f281916a8e57
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Controller / Wizard / TableController.php
1 <?php
2 namespace TYPO3\CMS\Backend\Controller\Wizard;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use Psr\Http\Message\ServerRequestInterface;
18 use Psr\Http\Message\ResponseInterface;
19 use TYPO3\CMS\Core\Http\Response;
20 use TYPO3\CMS\Backend\Template\DocumentTemplate;
21 use TYPO3\CMS\Backend\Utility\BackendUtility;
22 use TYPO3\CMS\Backend\Utility\IconUtility;
23 use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
24 use TYPO3\CMS\Core\DataHandling\DataHandler;
25 use TYPO3\CMS\Core\Imaging\Icon;
26 use TYPO3\CMS\Core\Imaging\IconFactory;
27 use TYPO3\CMS\Core\Utility\GeneralUtility;
28 use TYPO3\CMS\Core\Utility\HttpUtility;
29 use TYPO3\CMS\Core\Utility\MathUtility;
30 use TYPO3\CMS\Core\Utility\StringUtility;
31
32 /**
33 * Script Class for rendering the Table Wizard
34 */
35 class TableController extends AbstractWizardController implements \TYPO3\CMS\Core\Http\ControllerInterface {
36
37 /**
38 * Document template object
39 *
40 * @var DocumentTemplate
41 */
42 public $doc;
43
44 /**
45 * Content accumulation for the module.
46 *
47 * @var string
48 */
49 public $content;
50
51 /**
52 * If TRUE, <input> fields are shown instead of textareas.
53 *
54 * @var bool
55 */
56 public $inputStyle = FALSE;
57
58 /**
59 * If set, the string version of the content is interpreted/written as XML
60 * instead of the original line-based kind. This variable still needs binding
61 * to the wizard parameters - but support is ready!
62 *
63 * @var int
64 */
65 public $xmlStorage = 0;
66
67 /**
68 * Number of new rows to add in bottom of wizard
69 *
70 * @var int
71 */
72 public $numNewRows = 1;
73
74 /**
75 * Name of field in parent record which MAY contain the number of columns for the table
76 * here hardcoded to the value of tt_content. Should be set by FormEngine parameters (from P)
77 *
78 * @var string
79 */
80 public $colsFieldName = 'cols';
81
82 /**
83 * Wizard parameters, coming from FormEngine linking to the wizard.
84 *
85 * @var array
86 */
87 public $P;
88
89 /**
90 * The array which is constantly submitted by the multidimensional form of this wizard.
91 *
92 * @var array
93 */
94 public $TABLECFG;
95
96 /**
97 * Table parsing
98 * quoting of table cells
99 *
100 * @var string
101 */
102 public $tableParsing_quote;
103
104 /**
105 * delimiter between table cells
106 *
107 * @var string
108 */
109 public $tableParsing_delimiter;
110
111 /**
112 * @var IconFactory
113 */
114 protected $iconFactory;
115
116 /**
117 * Constructor
118 */
119 public function __construct() {
120 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
121 $this->getLanguageService()->includeLLFile('EXT:lang/locallang_wizards.xlf');
122 $GLOBALS['SOBE'] = $this;
123
124 $this->init();
125 }
126
127 /**
128 * Initialization of the class
129 *
130 * @return void
131 */
132 protected function init() {
133 // GPvars:
134 $this->P = GeneralUtility::_GP('P');
135 $this->TABLECFG = GeneralUtility::_GP('TABLE');
136 // Setting options:
137 $this->xmlStorage = $this->P['params']['xmlOutput'];
138 $this->numNewRows = MathUtility::forceIntegerInRange($this->P['params']['numNewRows'], 1, 50, 5);
139 // Textareas or input fields:
140 $this->inputStyle = isset($this->TABLECFG['textFields']) ? (bool)$this->TABLECFG['textFields'] : TRUE;
141 // Document template object:
142 $this->doc = GeneralUtility::makeInstance(DocumentTemplate::class);
143 $this->doc->setModuleTemplate('EXT:backend/Resources/Private/Templates/wizard_table.html');
144 // Setting form tag:
145 list($rUri) = explode('#', GeneralUtility::getIndpEnv('REQUEST_URI'));
146 $this->doc->form = '<form action="' . htmlspecialchars($rUri) . '" method="post" name="wizardForm">';
147 $this->tableParsing_delimiter = '|';
148 $this->tableParsing_quote = '';
149 }
150
151 /**
152 * Injects the request object for the current request or subrequest
153 * As this controller goes only through the main() method, it is rather simple for now
154 *
155 * @param ServerRequestInterface $request
156 * @return ResponseInterface $response
157 */
158 public function processRequest(ServerRequestInterface $request) {
159 $this->main();
160
161 /** @var Response $response */
162 $response = GeneralUtility::makeInstance(Response::class);
163 $response->getBody()->write($this->content);
164 return $response;
165 }
166
167 /**
168 * Main function, rendering the table wizard
169 *
170 * @return void
171 */
172 public function main() {
173 if ($this->P['table'] && $this->P['field'] && $this->P['uid']) {
174 $this->content .= $this->doc->section($this->getLanguageService()->getLL('table_title'), $this->tableWizard(), 0, 1);
175 } else {
176 $this->content .= $this->doc->section($this->getLanguageService()->getLL('table_title'), '<span class="text-danger">' . $this->getLanguageService()->getLL('table_noData', TRUE) . '</span>', 0, 1);
177 }
178 // Setting up the buttons and markers for docHeader
179 $docHeaderButtons = $this->getButtons();
180 $markers['CSH'] = $docHeaderButtons['csh'];
181 $markers['CONTENT'] = $this->content;
182 // Build the <body> for the module
183 $this->content = $this->doc->startPage('Table');
184 $this->content .= $this->doc->moduleBody(array(), $docHeaderButtons, $markers);
185 $this->content .= $this->doc->endPage();
186 $this->content = $this->doc->insertStylesAndJS($this->content);
187 }
188
189 /**
190 * Outputting the accumulated content to screen
191 *
192 * @return void
193 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8, use processRequest() instead
194 */
195 public function printContent() {
196 GeneralUtility::logDeprecatedFunction();
197 echo $this->content;
198 }
199
200 /**
201 * Create the panel of buttons for submitting the form or otherwise perform operations.
202 *
203 * @return array All available buttons as an associative array
204 */
205 protected function getButtons() {
206 $buttons = array(
207 'csh' => '',
208 'csh_buttons' => '',
209 'close' => '',
210 'save' => '',
211 'save_close' => '',
212 'reload' => ''
213 );
214 if ($this->P['table'] && $this->P['field'] && $this->P['uid']) {
215 // CSH
216 $buttons['csh'] = BackendUtility::cshItem('xMOD_csh_corebe', 'wizard_table_wiz');
217 // CSH Buttons
218 $buttons['csh_buttons'] = BackendUtility::cshItem('xMOD_csh_corebe', 'wizard_table_wiz_buttons');
219 // Close
220 $title = 'title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:rm.closeDoc', TRUE) . '"';
221 $buttons['close'] = '<a href="#" onclick="' . htmlspecialchars(('jumpToUrl(' . GeneralUtility::quoteJSvalue(GeneralUtility::sanitizeLocalUrl($this->P['returnUrl'])) . '); return false;')) . '" ' . $title . '>' . $this->iconFactory->getIcon('actions-document-close', Icon::SIZE_SMALL) . '</a>';
222 // Save
223 $buttons['save'] = '<button class="c-inputButton" name="savedok" value="1" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveDoc', TRUE) . '">'
224 . $this->iconFactory->getIcon('actions-document-save', Icon::SIZE_SMALL)
225 . '</button>';
226 // Save & Close
227 $buttons['save_close'] = '<button class="c-inputButton" name="saveandclosedok" value="1" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveCloseDoc', TRUE) . '">'
228 . $this->iconFactory->getIcon('actions-document-save-close', Icon::SIZE_SMALL)
229 . '</button>';
230 // Reload
231 $buttons['reload'] = IconUtility::getSpriteIcon('actions-system-refresh', array('html' => '<button class="c-inputButton" name="_refresh" value="1"></button>', 'title' => $this->getLanguageService()->getLL('forms_refresh', TRUE)));
232 }
233 return $buttons;
234 }
235
236 /**
237 * Draws the table wizard content
238 *
239 * @return string HTML content for the form.
240 * @throws \RuntimeException
241 */
242 public function tableWizard() {
243 if (!$this->checkEditAccess($this->P['table'], $this->P['uid'])) {
244 throw new \RuntimeException('Wizard Error: No access', 1349692692);
245 }
246 // First, check the references by selecting the record:
247 $row = BackendUtility::getRecord($this->P['table'], $this->P['uid']);
248 if (!is_array($row)) {
249 throw new \RuntimeException('Wizard Error: No reference to record', 1294587125);
250 }
251 // This will get the content of the form configuration code field to us - possibly cleaned up,
252 // saved to database etc. if the form has been submitted in the meantime.
253 $tableCfgArray = $this->getConfigCode($row);
254 // Generation of the Table Wizards HTML code:
255 $content = $this->getTableHTML($tableCfgArray);
256 // Return content:
257 return $content;
258 }
259
260 /*
261 *
262 * Helper functions
263 *
264 */
265
266 /**
267 * Will get and return the configuration code string
268 * Will also save (and possibly redirect/exit) the content if a save button has been pressed
269 *
270 * @param array $row Current parent record row
271 * @return array Table config code in an array
272 * @internal
273 */
274 public function getConfigCode($row) {
275 // Get delimiter settings
276 $flexForm = GeneralUtility::xml2array($row['pi_flexform']);
277 if (is_array($flexForm)) {
278 $this->tableParsing_quote = $flexForm['data']['s_parsing']['lDEF']['tableparsing_quote']['vDEF'] ? chr((int)$flexForm['data']['s_parsing']['lDEF']['tableparsing_quote']['vDEF']) : '';
279 $this->tableParsing_delimiter = $flexForm['data']['s_parsing']['lDEF']['tableparsing_delimiter']['vDEF'] ? chr((int)$flexForm['data']['s_parsing']['lDEF']['tableparsing_delimiter']['vDEF']) : '|';
280 }
281 // If some data has been submitted, then construct
282 if (isset($this->TABLECFG['c'])) {
283 // Process incoming:
284 $this->changeFunc();
285 // Convert to string (either line based or XML):
286 if ($this->xmlStorage) {
287 // Convert the input array to XML:
288 $bodyText = GeneralUtility::array2xml_cs($this->TABLECFG['c'], 'T3TableWizard');
289 // Setting cfgArr directly from the input:
290 $configuration = $this->TABLECFG['c'];
291 } else {
292 // Convert the input array to a string of configuration code:
293 $bodyText = $this->cfgArray2CfgString($this->TABLECFG['c']);
294 // Create cfgArr from the string based configuration - that way it is cleaned up and any incompatibilities will be removed!
295 $configuration = $this->cfgString2CfgArray($bodyText, $row[$this->colsFieldName]);
296 }
297 // If a save button has been pressed, then save the new field content:
298 if ($_POST['savedok'] || $_POST['saveandclosedok']) {
299 // Get DataHandler object:
300 /** @var DataHandler $dataHandler */
301 $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
302 $dataHandler->stripslashes_values = FALSE;
303 // Put content into the data array:
304 $data = array();
305 if ($this->P['flexFormPath']) {
306 // Current value of flexForm path:
307 $currentFlexFormData = GeneralUtility::xml2array($row[$this->P['field']]);
308 /** @var FlexFormTools $flexFormTools */
309 $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
310 $flexFormTools->setArrayValueByPath($this->P['flexFormPath'], $currentFlexFormData, $bodyText);
311 $data[$this->P['table']][$this->P['uid']][$this->P['field']] = $currentFlexFormData;
312 } else {
313 $data[$this->P['table']][$this->P['uid']][$this->P['field']] = $bodyText;
314 }
315 // Perform the update:
316 $dataHandler->start($data, array());
317 $dataHandler->process_datamap();
318 // If the save/close button was pressed, then redirect the screen:
319 if ($_POST['saveandclosedok']) {
320 HttpUtility::redirect(GeneralUtility::sanitizeLocalUrl($this->P['returnUrl']));
321 }
322 }
323 } else {
324 // If nothing has been submitted, load the $bodyText variable from the selected database row:
325 if ($this->xmlStorage) {
326 $configuration = GeneralUtility::xml2array($row[$this->P['field']]);
327 } else {
328 if ($this->P['flexFormPath']) {
329 // Current value of flexForm path:
330 $currentFlexFormData = GeneralUtility::xml2array($row[$this->P['field']]);
331 /** @var FlexFormTools $flexFormTools */
332 $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
333 $configuration = $flexFormTools->getArrayValueByPath($this->P['flexFormPath'], $currentFlexFormData);
334 $configuration = $this->cfgString2CfgArray($configuration, 0);
335 } else {
336 // Regular line based table configuration:
337 $configuration = $this->cfgString2CfgArray($row[$this->P['field']], $row[$this->colsFieldName]);
338 }
339 }
340 $configuration = is_array($configuration) ? $configuration : array();
341 }
342 return $configuration;
343 }
344
345 /**
346 * Creates the HTML for the Table Wizard:
347 *
348 * @param array $configuration Table config array
349 * @return string HTML for the table wizard
350 * @internal
351 */
352 public function getTableHTML($configuration) {
353 // Traverse the rows:
354 $tRows = array();
355 $k = 0;
356 $countLines = count($configuration);
357 foreach ($configuration as $cellArr) {
358 if (is_array($cellArr)) {
359 // Initialize:
360 $cells = array();
361 $a = 0;
362 // Traverse the columns:
363 foreach ($cellArr as $cellContent) {
364 if ($this->inputStyle) {
365 $cells[] = '<input class="form-control" type="text"' . $this->doc->formWidth(20) . ' name="TABLE[c][' . ($k + 1) * 2 . '][' . ($a + 1) * 2 . ']" value="' . htmlspecialchars($cellContent) . '" />';
366 } else {
367 $cellContent = preg_replace('/<br[ ]?[\\/]?>/i', LF, $cellContent);
368 $cells[] = '<textarea class="form-control" ' . $this->doc->formWidth(20) . ' rows="6" name="TABLE[c][' . ($k + 1) * 2 . '][' . ($a + 1) * 2 . ']">' . htmlspecialchars($cellContent) . '</textarea>';
369 }
370 // Increment counter:
371 $a++;
372 }
373 // CTRL panel for a table row (move up/down/around):
374 $onClick = 'document.wizardForm.action+=' . GeneralUtility::quoteJSvalue('#ANC_' . (($k + 1) * 2 - 2)) . ';';
375 $onClick = ' onclick="' . htmlspecialchars($onClick) . '"';
376 $ctrl = '';
377 if ($k !== 0) {
378 $ctrl .= '<button class="btn btn-default" name="TABLE[row_up][' . ($k + 1) * 2 . ']" title="' . $this->getLanguageService()->getLL('table_up', TRUE) . '"'. $onClick . '><span class="t3-icon fa fa-fw fa-angle-up"></span></button>';
379 } else {
380 $ctrl .= '<button class="btn btn-default" name="TABLE[row_bottom][' . ($k + 1) * 2 . ']" title="' . $this->getLanguageService()->getLL('table_bottom', TRUE) . '"'. $onClick . '><span class="t3-icon fa fa-fw fa-angle-double-down"></span></button>';
381 }
382 if ($k + 1 !== $countLines) {
383 $ctrl .= '<button class="btn btn-default" name="TABLE[row_down][' . ($k + 1) * 2 . ']" title="' . $this->getLanguageService()->getLL('table_down', TRUE) . '"'. $onClick . '><span class="t3-icon fa fa-fw fa-angle-down"></span></button>';
384 } else {
385 $ctrl .= '<button class="btn btn-default" name="TABLE[row_top][' . ($k + 1) * 2 . ']" title="' . $this->getLanguageService()->getLL('table_top', TRUE) . '"'. $onClick . '><span class="t3-icon fa fa-fw fa-angle-double-up"></span></button>';
386 }
387 $ctrl .= '<button class="btn btn-default" name="TABLE[row_remove][' . ($k + 1) * 2 . ']" title="' . $this->getLanguageService()->getLL('table_removeRow', TRUE) . '"'. $onClick . '><span class="t3-icon fa fa-fw fa-trash"></span></button>';
388 $ctrl .= '<button class="btn btn-default" name="TABLE[row_add][' . ($k + 1) * 2 . ']" title="' . $this->getLanguageService()->getLL('table_addRow', TRUE) . '"'. $onClick . '><span class="t3-icon fa fa-fw fa-plus"></span></button>';
389 $tRows[] = '
390 <tr>
391 <td>
392 <a name="ANC_' . ($k + 1) * 2 . '"></a>
393 <span class="btn-group' . ($this->inputStyle ? '' : '-vertical') . '">' . $ctrl . '</span>
394 </td>
395 <td>' . implode('</td>
396 <td>', $cells) . '</td>
397 </tr>';
398 // Increment counter:
399 $k++;
400 }
401 }
402 // CTRL panel for a table column (move left/right/around/delete)
403 $cells = array();
404 $cells[] = '';
405 // Finding first row:
406 $firstRow = reset($configuration);
407 if (is_array($firstRow)) {
408 $cols = count($firstRow);
409 for ($a = 1; $a <= $cols; $a++) {
410 $b = $a * 2;
411 $ctrl = '';
412 if ($a !== 1) {
413 $ctrl .= '<button class="btn btn-default" name="TABLE[col_left][' . $b . ']" title="' . $this->getLanguageService()->getLL('table_left', TRUE) . '"><span class="t3-icon fa fa-fw fa-angle-left"></span></button>';
414 } else {
415 $ctrl .= '<button class="btn btn-default" name="TABLE[col_end][' . $b . ']" title="' . $this->getLanguageService()->getLL('table_end', TRUE) . '"><span class="t3-icon fa fa-fw fa-angle-double-right"></span></button>';
416 }
417 if ($a != $cols) {
418 $ctrl .= '<button class="btn btn-default" name="TABLE[col_right][' . $b . ']" title="' . $this->getLanguageService()->getLL('table_right', TRUE) . '"><span class="t3-icon fa fa-fw fa-angle-right"></span></button>';
419 } else {
420 $ctrl .= '<button class="btn btn-default" name="TABLE[col_start][' . $b . ']" title="' . $this->getLanguageService()->getLL('table_start', TRUE) . '"><span class="t3-icon fa fa-fw fa-angle-double-left"></span></button>';
421 }
422 $ctrl .= '<button class="btn btn-default" name="TABLE[col_remove][' . $b . ']" title="' . $this->getLanguageService()->getLL('table_removeColumn', TRUE) . '"><span class="t3-icon fa fa-fw fa-trash"></span></button>';
423 $ctrl .= '<button class="btn btn-default" name="TABLE[col_add][' . $b . ']" title="' . $this->getLanguageService()->getLL('table_addColumn', TRUE) . '"><span class="t3-icon fa fa-fw fa-plus"></span></button>';
424 $cells[] = '<span class="btn-group">' . $ctrl . '</span>';
425 }
426 $tRows[] = '
427 <tfoot>
428 <tr>
429 <td>' . implode('</td>
430 <td>', $cells) . '</td>
431 </tr>
432 </tfoot>';
433 }
434 $content = '';
435 // Implode all table rows into a string, wrapped in table tags.
436 $content .= '
437
438 <!-- Table wizard -->
439 <div class="table-fit table-fit-inline-block">
440 <table id="typo3-tablewizard" class="table table-center">
441 ' . implode('', $tRows) . '
442 </table>
443 </div>';
444 // Input type checkbox:
445 $content .= '
446
447 <!-- Input mode check box: -->
448 <div class="checkbox">
449 <input type="hidden" name="TABLE[textFields]" value="0" />
450 <label for="textFields">
451 <input type="checkbox" name="TABLE[textFields]" id="textFields" value="1"' . ($this->inputStyle ? ' checked="checked"' : '') . ' />
452 ' . $this->getLanguageService()->getLL('table_smallFields') . '
453 </label>
454 </div>';
455 return $content;
456 }
457
458 /**
459 * Detects if a control button (up/down/around/delete) has been pressed for an item and accordingly it will manipulate the internal TABLECFG array
460 *
461 * @return void
462 * @internal
463 */
464 public function changeFunc() {
465 if ($this->TABLECFG['col_remove']) {
466 $kk = key($this->TABLECFG['col_remove']);
467 $cmd = 'col_remove';
468 } elseif ($this->TABLECFG['col_add']) {
469 $kk = key($this->TABLECFG['col_add']);
470 $cmd = 'col_add';
471 } elseif ($this->TABLECFG['col_start']) {
472 $kk = key($this->TABLECFG['col_start']);
473 $cmd = 'col_start';
474 } elseif ($this->TABLECFG['col_end']) {
475 $kk = key($this->TABLECFG['col_end']);
476 $cmd = 'col_end';
477 } elseif ($this->TABLECFG['col_left']) {
478 $kk = key($this->TABLECFG['col_left']);
479 $cmd = 'col_left';
480 } elseif ($this->TABLECFG['col_right']) {
481 $kk = key($this->TABLECFG['col_right']);
482 $cmd = 'col_right';
483 } elseif ($this->TABLECFG['row_remove']) {
484 $kk = key($this->TABLECFG['row_remove']);
485 $cmd = 'row_remove';
486 } elseif ($this->TABLECFG['row_add']) {
487 $kk = key($this->TABLECFG['row_add']);
488 $cmd = 'row_add';
489 } elseif ($this->TABLECFG['row_top']) {
490 $kk = key($this->TABLECFG['row_top']);
491 $cmd = 'row_top';
492 } elseif ($this->TABLECFG['row_bottom']) {
493 $kk = key($this->TABLECFG['row_bottom']);
494 $cmd = 'row_bottom';
495 } elseif ($this->TABLECFG['row_up']) {
496 $kk = key($this->TABLECFG['row_up']);
497 $cmd = 'row_up';
498 } elseif ($this->TABLECFG['row_down']) {
499 $kk = key($this->TABLECFG['row_down']);
500 $cmd = 'row_down';
501 } else {
502 $kk = '';
503 $cmd = '';
504 }
505 if ($cmd && MathUtility::canBeInterpretedAsInteger($kk)) {
506 if (StringUtility::beginsWith($cmd, 'row_')) {
507 switch ($cmd) {
508 case 'row_remove':
509 unset($this->TABLECFG['c'][$kk]);
510 break;
511 case 'row_add':
512 for ($a = 1; $a <= $this->numNewRows; $a++) {
513 // Checking if set: The point is that any new row between existing rows
514 // will be TRUE after one row is added while if rows are added in the bottom
515 // of the table there will be no existing rows to stop the addition of new rows
516 // which means it will add up to $this->numNewRows rows then.
517 if (!isset($this->TABLECFG['c'][($kk + $a)])) {
518 $this->TABLECFG['c'][$kk + $a] = array();
519 } else {
520 break;
521 }
522 }
523 break;
524 case 'row_top':
525 $this->TABLECFG['c'][1] = $this->TABLECFG['c'][$kk];
526 unset($this->TABLECFG['c'][$kk]);
527 break;
528 case 'row_bottom':
529 $this->TABLECFG['c'][10000000] = $this->TABLECFG['c'][$kk];
530 unset($this->TABLECFG['c'][$kk]);
531 break;
532 case 'row_up':
533 $this->TABLECFG['c'][$kk - 3] = $this->TABLECFG['c'][$kk];
534 unset($this->TABLECFG['c'][$kk]);
535 break;
536 case 'row_down':
537 $this->TABLECFG['c'][$kk + 3] = $this->TABLECFG['c'][$kk];
538 unset($this->TABLECFG['c'][$kk]);
539 break;
540 }
541 ksort($this->TABLECFG['c']);
542 }
543 if (StringUtility::beginsWith($cmd, 'col_')) {
544 foreach ($this->TABLECFG['c'] as $cAK => $value) {
545 switch ($cmd) {
546 case 'col_remove':
547 unset($this->TABLECFG['c'][$cAK][$kk]);
548 break;
549 case 'col_add':
550 $this->TABLECFG['c'][$cAK][$kk + 1] = '';
551 break;
552 case 'col_start':
553 $this->TABLECFG['c'][$cAK][1] = $this->TABLECFG['c'][$cAK][$kk];
554 unset($this->TABLECFG['c'][$cAK][$kk]);
555 break;
556 case 'col_end':
557 $this->TABLECFG['c'][$cAK][1000000] = $this->TABLECFG['c'][$cAK][$kk];
558 unset($this->TABLECFG['c'][$cAK][$kk]);
559 break;
560 case 'col_left':
561 $this->TABLECFG['c'][$cAK][$kk - 3] = $this->TABLECFG['c'][$cAK][$kk];
562 unset($this->TABLECFG['c'][$cAK][$kk]);
563 break;
564 case 'col_right':
565 $this->TABLECFG['c'][$cAK][$kk + 3] = $this->TABLECFG['c'][$cAK][$kk];
566 unset($this->TABLECFG['c'][$cAK][$kk]);
567 break;
568 }
569 ksort($this->TABLECFG['c'][$cAK]);
570 }
571 }
572 }
573 // Convert line breaks to <br /> tags:
574 foreach ($this->TABLECFG['c'] as $a => $value) {
575 foreach ($this->TABLECFG['c'][$a] as $b => $value2) {
576 $this->TABLECFG['c'][$a][$b] = str_replace(LF, '<br />', str_replace(CR, '', $this->TABLECFG['c'][$a][$b]));
577 }
578 }
579 }
580
581 /**
582 * Converts the input array to a configuration code string
583 *
584 * @param array $cfgArr Array of table configuration (follows the input structure from the table wizard POST form)
585 * @return string The array converted into a string with line-based configuration.
586 * @see cfgString2CfgArray()
587 */
588 public function cfgArray2CfgString($cfgArr) {
589 $inLines = array();
590 // Traverse the elements of the table wizard and transform the settings into configuration code.
591 foreach ($cfgArr as $valueA) {
592 $thisLine = array();
593 foreach ($valueA as $valueB) {
594 $thisLine[] = $this->tableParsing_quote . str_replace($this->tableParsing_delimiter, '', $valueB) . $this->tableParsing_quote;
595 }
596 $inLines[] = implode($this->tableParsing_delimiter, $thisLine);
597 }
598 // Finally, implode the lines into a string:
599 return implode(LF, $inLines);
600 }
601
602 /**
603 * Converts the input configuration code string into an array
604 *
605 * @param string $configurationCode Configuration code
606 * @param int $columns Default number of columns
607 * @return array Configuration array
608 * @see cfgArray2CfgString()
609 */
610 public function cfgString2CfgArray($configurationCode, $columns) {
611 // Explode lines in the configuration code - each line is a table row.
612 $tableLines = explode(LF, $configurationCode);
613 // Setting number of columns
614 // auto...
615 if (!$columns && trim($tableLines[0])) {
616 $columns = count(explode($this->tableParsing_delimiter, $tableLines[0]));
617 }
618 $columns = $columns ?: 4;
619 // Traverse the number of table elements:
620 $configurationArray = array();
621 foreach ($tableLines as $key => $value) {
622 // Initialize:
623 $valueParts = explode($this->tableParsing_delimiter, $value);
624 // Traverse columns:
625 for ($a = 0; $a < $columns; $a++) {
626 if ($this->tableParsing_quote && $valueParts[$a][0] === $this->tableParsing_quote && substr($valueParts[$a], -1, 1) === $this->tableParsing_quote) {
627 $valueParts[$a] = substr(trim($valueParts[$a]), 1, -1);
628 }
629 $configurationArray[$key][$a] = $valueParts[$a];
630 }
631 }
632 return $configurationArray;
633 }
634
635 }