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