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