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