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