Passed
Push — master ( 2b355a...25a926 )
by
unknown
13:06
created

PageInformationController   F

Complexity

Total Complexity 70

Size/Duplication

Total Lines 546
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 70
eloc 289
dl 0
loc 546
rs 2.8
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A cleanTableNames() 0 21 5
A main() 0 24 3
B getPageRecordsRecursive() 0 50 8
C pages_drawItem() 0 72 16
A modMenu() 0 19 2
A getPagesTableFieldValue() 0 3 1
A numberOfRecords() 0 20 2
B addElement() 0 50 10
C getTable_pages() 0 100 14
A getIcon() 0 10 2
A __construct() 0 4 1
A getBackendUser() 0 3 1
A getLanguageService() 0 3 1
A fillFieldConfiguration() 0 10 3
A init() 0 6 1

How to fix   Complexity   

Complex Class

Complex classes like PageInformationController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PageInformationController, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Info\Controller;
17
18
use Psr\Http\Message\ServerRequestInterface;
19
use TYPO3\CMS\Backend\Routing\PreviewUriBuilder;
20
use TYPO3\CMS\Backend\Routing\UriBuilder;
21
use TYPO3\CMS\Backend\Utility\BackendUtility;
22
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
23
use TYPO3\CMS\Core\Database\ConnectionPool;
24
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
25
use TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction;
26
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
27
use TYPO3\CMS\Core\Imaging\Icon;
28
use TYPO3\CMS\Core\Imaging\IconFactory;
29
use TYPO3\CMS\Core\Localization\LanguageService;
30
use TYPO3\CMS\Core\Type\Bitmask\Permission;
31
use TYPO3\CMS\Core\Utility\GeneralUtility;
32
33
/**
34
 * Class for displaying page information (records, page record properties) in Web -> Info
35
 * @internal This class is a specific Backend controller implementation and is not part of the TYPO3's Core API.
36
 */
37
class PageInformationController
38
{
39
    /**
40
     * @var array
41
     */
42
    protected $fieldConfiguration = [];
43
44
    /**
45
     * @var int Value of the GET/POST var 'id'
46
     */
47
    protected $id;
48
49
    /**
50
     * @var InfoModuleController Contains a reference to the parent calling object
51
     */
52
    protected $pObj;
53
54
    protected IconFactory $iconFactory;
55
    protected UriBuilder $uriBuilder;
56
57
    /**
58
     * @var array
59
     */
60
    protected $fieldArray;
61
62
    /**
63
     * Keys are fieldnames and values are td-css-classes to add in addElement();
64
     *
65
     * @var array
66
     */
67
    protected $addElement_tdCssClass = [];
68
69
    public function __construct(IconFactory $iconFactory, UriBuilder $uriBuilder)
70
    {
71
        $this->iconFactory = $iconFactory;
72
        $this->uriBuilder = $uriBuilder;
73
    }
74
75
    /**
76
     * Init, called from parent object
77
     *
78
     * @param InfoModuleController $pObj A reference to the parent (calling) object
79
     * @param ServerRequestInterface $request
80
     */
81
    public function init(InfoModuleController $pObj, ServerRequestInterface $request)
82
    {
83
        $this->pObj = $pObj;
84
        $this->id = (int)($request->getParsedBody()['id'] ?? $request->getQueryParams()['id'] ?? 0);
85
        // Setting MOD_MENU items as we need them for logging:
86
        $this->pObj->MOD_MENU = array_merge($this->pObj->MOD_MENU, $this->modMenu());
87
    }
88
89
    /**
90
     * Main, called from parent object
91
     *
92
     * @param ServerRequestInterface $request
93
     * @return string Output HTML for the module.
94
     */
95
    public function main(ServerRequestInterface $request)
96
    {
97
        $theOutput = '<h1>' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:info/Resources/Private/Language/locallang_webinfo.xlf:page_title')) . '</h1>';
98
99
        if (isset($this->fieldConfiguration[$this->pObj->MOD_SETTINGS['pages']])) {
100
            $this->fieldArray = $this->fieldConfiguration[$this->pObj->MOD_SETTINGS['pages']]['fields'];
101
        }
102
103
        $theOutput .= '
104
        <div class="row row-cols-auto mb-3 g-3 align-items-center">
105
            <div class="col-auto">' . BackendUtility::getDropdownMenu($this->id, 'SET[depth]', $this->pObj->MOD_SETTINGS['depth'], $this->pObj->MOD_MENU['depth']) . '</div>
106
            <div class="col-auto">' . BackendUtility::getDropdownMenu($this->id, 'SET[pages]', $this->pObj->MOD_SETTINGS['pages'], $this->pObj->MOD_MENU['pages']) . '</div>
107
            <div class="col-auto">' . BackendUtility::cshItem('_MOD_web_info', 'func_' . $this->pObj->MOD_SETTINGS['pages'], '', '<span class="btn btn-default btn-sm">|</span>') . '</div>
108
        </div>'
109
            . $this->getTable_pages($this->id, (int)$this->pObj->MOD_SETTINGS['depth'], $request);
110
111
        // Additional footer content
112
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/web_info/class.tx_cms_webinfo.php']['drawFooterHook'] ?? [] as $hook) {
113
            $params = [
114
                'request' => $request
115
            ];
116
            $theOutput .= GeneralUtility::callUserFunction($hook, $params, $this);
117
        }
118
        return $theOutput;
119
    }
120
121
    /**
122
     * Returns the menu array
123
     *
124
     * @return array
125
     */
126
    protected function modMenu()
127
    {
128
        $menu = [
129
            'pages' => [],
130
            'depth' => [
131
                0 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_0'),
132
                1 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_1'),
133
                2 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_2'),
134
                3 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_3'),
135
                4 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_4'),
136
                999 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_infi')
137
            ]
138
        ];
139
140
        $this->fillFieldConfiguration($this->id);
141
        foreach ($this->fieldConfiguration as $key => $item) {
142
            $menu['pages'][$key] = $item['label'];
143
        }
144
        return $menu;
145
    }
146
147
    /**
148
     * Function, which returns all tables to
149
     * which the user has access. Also a set of standard tables (pages, sys_filemounts, etc...)
150
     * are filtered out. So what is left is basically all tables which makes sense to list content from.
151
     *
152
     * @return string
153
     */
154
    protected function cleanTableNames(): string
155
    {
156
        // Get all table names:
157
        $tableNames = array_flip(array_keys($GLOBALS['TCA']));
158
        // Unset common names:
159
        unset($tableNames['pages']);
160
        unset($tableNames['sys_filemounts']);
161
        unset($tableNames['sys_action']);
162
        unset($tableNames['sys_workflows']);
163
        unset($tableNames['be_users']);
164
        unset($tableNames['be_groups']);
165
        $allowedTableNames = [];
166
        // Traverse table names and set them in allowedTableNames array IF they can be read-accessed by the user.
167
        if (is_array($tableNames)) {
0 ignored issues
show
introduced by
The condition is_array($tableNames) is always true.
Loading history...
168
            foreach ($tableNames as $k => $v) {
169
                if (!$GLOBALS['TCA'][$k]['ctrl']['hideTable'] && $this->getBackendUser()->check('tables_select', $k)) {
170
                    $allowedTableNames['table_' . $k] = $k;
171
                }
172
            }
173
        }
174
        return implode(',', array_keys($allowedTableNames));
175
    }
176
177
    /**
178
     * Generate configuration for field selection
179
     *
180
     * @param int $pageId current page id
181
     */
182
    protected function fillFieldConfiguration(int $pageId)
183
    {
184
        $modTSconfig = BackendUtility::getPagesTSconfig($pageId)['mod.']['web_info.']['fieldDefinitions.'] ?? [];
185
        foreach ($modTSconfig as $key => $item) {
186
            $fieldList = str_replace('###ALL_TABLES###', $this->cleanTableNames(), $item['fields']);
187
            $fields = GeneralUtility::trimExplode(',', $fieldList, true);
188
            $key = trim($key, '.');
189
            $this->fieldConfiguration[$key] = [
190
                'label' => $item['label'] ? $this->getLanguageService()->sL($item['label']) : $key,
191
                'fields' => $fields
192
            ];
193
        }
194
    }
195
196
    /**
197
     * Renders records from the pages table from page id
198
     *
199
     * @param int $id Page id
200
     * @param int $depth
201
     * @param ServerRequestInterface $request
202
     * @return string HTML for the listing
203
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
204
     */
205
    protected function getTable_pages($id, int $depth, ServerRequestInterface $request)
206
    {
207
        $out = '';
208
        $lang = $this->getLanguageService();
209
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
210
            ->getQueryBuilderForTable('pages');
211
        $queryBuilder->getRestrictions()
212
            ->removeAll()
213
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
214
        $row = $queryBuilder
215
            ->select('*')
216
            ->from('pages')
217
            ->where(
218
                $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)),
219
                $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW)
220
            )
221
            ->execute()
222
            ->fetch();
223
        BackendUtility::workspaceOL('pages', $row);
224
        // If there was found a page:
225
        if (is_array($row)) {
226
            // Creating elements
227
            $editUids = [];
228
            // Getting children
229
            $theRows = $this->getPageRecordsRecursive($row['uid'], $depth);
230
            if ($this->getBackendUser()->doesUserHaveAccess($row, Permission::PAGE_EDIT) && $row['uid'] > 0) {
231
                $editUids[] = $row['uid'];
232
            }
233
            $out .= $this->pages_drawItem($row, $this->fieldArray, $request);
234
            // Traverse all pages selected:
235
            foreach ($theRows as $sRow) {
236
                if ($this->getBackendUser()->doesUserHaveAccess($sRow, Permission::PAGE_EDIT)) {
237
                    $editUids[] = $sRow['uid'];
238
                }
239
                $out .= $this->pages_drawItem($sRow, $this->fieldArray, $request);
240
            }
241
            // Header line is drawn
242
            $headerCells = [];
243
            $editIdList = implode(',', $editUids);
244
            // Traverse fields (as set above) in order to create header values:
245
            foreach ($this->fieldArray as $field) {
246
                $editButton = '';
247
                if ($editIdList && isset($GLOBALS['TCA']['pages']['columns'][$field]) && $field !== 'uid') {
248
                    $iTitle = sprintf(
249
                        $lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:editThisColumn'),
250
                        rtrim(trim($lang->sL(BackendUtility::getItemLabel('pages', $field))), ':')
251
                    );
252
                    $urlParameters = [
253
                        'edit' => [
254
                            'pages' => [
255
                                $editIdList => 'edit'
256
                            ]
257
                        ],
258
                        'columnsOnly' => $field,
259
                        'returnUrl' => $request->getAttribute('normalizedParams')->getRequestUri()
260
                    ];
261
                    $url = (string)$this->uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
262
                    $editButton = '<a class="btn btn-default" href="' . htmlspecialchars($url)
263
                        . '" title="' . htmlspecialchars($iTitle) . '">'
264
                        . $this->iconFactory->getIcon('actions-document-open', Icon::SIZE_SMALL)->render() . '</a>';
265
                }
266
                switch ($field) {
267
                    case 'title':
268
                        $headerCells[$field] = $editButton . '&nbsp;<strong>'
269
                            . $lang->sL($GLOBALS['TCA']['pages']['columns'][$field]['label'])
270
                            . '</strong>';
271
                        break;
272
                    case 'uid':
273
                        $headerCells[$field] = '';
274
                        break;
275
                    default:
276
                        if (strpos($field, 'table_') === 0) {
277
                            $f2 = substr($field, 6);
278
                            if ($GLOBALS['TCA'][$f2]) {
279
                                $headerCells[$field] = '&nbsp;' .
280
                                    '<span title="' .
281
                                    htmlspecialchars($lang->sL($GLOBALS['TCA'][$f2]['ctrl']['title'])) .
282
                                    '">' .
283
                                    $this->iconFactory->getIconForRecord($f2, [], Icon::SIZE_SMALL)->render() .
284
                                    '</span>';
285
                            }
286
                        } else {
287
                            $headerCells[$field] = $editButton . '&nbsp;<strong>'
288
                                . htmlspecialchars($lang->sL($GLOBALS['TCA']['pages']['columns'][$field]['label']))
289
                                . '</strong>';
290
                        }
291
                }
292
            }
293
            $out = '<div class="table-responsive">'
294
                . '<table class="table table-striped table-hover mb-0">'
295
                . '<thead>'
296
                . $this->addElement($headerCells)
297
                . '</thead>'
298
                . '<tbody>'
299
                . $out
300
                . '</tbody>'
301
                . '</table>'
302
                . '</div>';
303
        }
304
        return $out;
305
    }
306
307
    /**
308
     * Adds pages-rows to an array, selecting recursively in the page tree.
309
     *
310
     * @param int $pid Starting page id to select from
311
     * @param string $iconPrefix Prefix for icon code.
312
     * @param int $depth Depth (decreasing)
313
     * @param array $rows Array which will accumulate page rows
314
     * @return array $rows with added rows.
315
     */
316
    protected function getPageRecordsRecursive(int $pid, int $depth, string $iconPrefix = '', array $rows = []): array
317
    {
318
        $depth--;
319
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
320
        $queryBuilder->getRestrictions()
321
            ->removeAll()
322
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
323
            ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->getBackendUser()->workspace));
324
325
        $queryBuilder
326
            ->select('*')
327
            ->from('pages')
328
            ->where(
329
                $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pid, \PDO::PARAM_INT)),
330
                $queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)),
331
                $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW)
332
            );
333
334
        if (!empty($GLOBALS['TCA']['pages']['ctrl']['sortby'])) {
335
            $queryBuilder->orderBy($GLOBALS['TCA']['pages']['ctrl']['sortby']);
336
        }
337
338
        if ($depth >= 0) {
339
            $result = $queryBuilder->execute();
340
            $rowCount = $queryBuilder->count('uid')->execute()->fetchColumn(0);
341
            $count = 0;
342
            while ($row = $result->fetch()) {
343
                BackendUtility::workspaceOL('pages', $row);
344
                if (is_array($row)) {
345
                    $count++;
346
                    $row['treeIcons'] = $iconPrefix
347
                        . '<span class="treeline-icon treeline-icon-join'
348
                        . ($rowCount === $count ? 'bottom' : '')
349
                        . '"></span>';
350
                    $rows[] = $row;
351
                    // Get the branch
352
                    $spaceOutIcons = '<span class="treeline-icon treeline-icon-'
353
                        . ($rowCount === $count ? 'clear' : 'line')
354
                        . '"></span>';
355
                    $rows = $this->getPageRecordsRecursive(
356
                        $row['uid'],
357
                        $row['php_tree_stop'] ? 0 : $depth,
358
                        $iconPrefix . $spaceOutIcons,
359
                        $rows
360
                    );
361
                }
362
            }
363
        }
364
365
        return $rows;
366
    }
367
368
    /**
369
     * Adds a list item for the pages-rendering
370
     *
371
     * @param array $row Record array
372
     * @param array $fieldArr Field list
373
     * @param ServerRequestInterface $request
374
     * @return string HTML for the item
375
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
376
     */
377
    protected function pages_drawItem($row, $fieldArr, ServerRequestInterface $request)
378
    {
379
        $userTsConfig = $this->getBackendUser()->getTSConfig();
380
        $theIcon = $this->getIcon($row);
381
        // Preparing and getting the data-array
382
        $theData = [];
383
        foreach ($fieldArr as $field) {
384
            switch ($field) {
385
                case 'title':
386
                    $showPageId = !empty($userTsConfig['options.']['pageTree.']['showPageIdWithTitle']);
387
                    $pTitle = htmlspecialchars((string)BackendUtility::getProcessedValue('pages', $field, $row[$field], 20));
388
                    $theData[$field] = $row['treeIcons'] . $theIcon . ($showPageId ? '[' . $row['uid'] . '] ' : '') . $pTitle;
389
                    break;
390
                case 'php_tree_stop':
391
                    // Intended fall through
392
                case 'TSconfig':
393
                    $theData[$field] = $row[$field] ? '<strong>x</strong>' : '&nbsp;';
394
                    break;
395
                case 'uid':
396
                    $uid = 0;
397
                    $editButton = '';
398
                    if ($this->getBackendUser()->doesUserHaveAccess($row, 2) && $row['uid'] > 0) {
399
                        $uid = (int)$row['uid'];
400
                        $urlParameters = [
401
                            'edit' => [
402
                                'pages' => [
403
                                    $row['uid'] => 'edit'
404
                                ]
405
                            ],
406
                            'returnUrl' => $request->getAttribute('normalizedParams')->getRequestUri()
407
                        ];
408
                        $url = (string)$this->uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
409
                        $attributes = PreviewUriBuilder::create((int)$row['uid'])
410
                            ->withRootLine(BackendUtility::BEgetRootLine($row['uid']))
411
                            ->serializeDispatcherAttributes();
412
                        $editButton =
413
                            '<a href="#" ' . $attributes . ' class="btn btn-default" title="' .
414
                            htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.showPage')) . '">' .
415
                            $this->iconFactory->getIcon('actions-view-page', Icon::SIZE_SMALL)->render() .
416
                            '</a>';
417
                        $editButton .=
418
                            '<a class="btn btn-default" href="' . htmlspecialchars($url) . '" title="' .
419
                            htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:editDefaultLanguagePage')) . '">' .
420
                            $this->iconFactory->getIcon('actions-page-open', Icon::SIZE_SMALL)->render() .
421
                            '</a>';
422
                    }
423
                    // Since the uid is overwritten with the edit button markup we need to store
424
                    // the actual uid to be able to add it as data attribute to the table data cell.
425
                    // This also makes distinction between record rows and the header line simpler.
426
                    $theData['_UID_'] = $uid;
427
                    $theData[$field] = '<div class="btn-group" role="group">' . $editButton . '</div>';
428
                    break;
429
                case 'shortcut':
430
                case 'shortcut_mode':
431
                    if ((int)$row['doktype'] === PageRepository::DOKTYPE_SHORTCUT) {
432
                        $theData[$field] = $this->getPagesTableFieldValue($field, $row);
433
                    }
434
                    break;
435
                default:
436
                    if (strpos($field, 'table_') === 0) {
437
                        $f2 = substr($field, 6);
438
                        if ($GLOBALS['TCA'][$f2]) {
439
                            $c = $this->numberOfRecords($f2, $row['uid']);
440
                            $theData[$field] = ($c ?: '');
441
                        }
442
                    } else {
443
                        $theData[$field] = $this->getPagesTableFieldValue($field, $row);
444
                    }
445
            }
446
        }
447
        $this->addElement_tdCssClass['title'] = $row['_CSSCLASS'] ?? '';
448
        return $this->addElement($theData);
449
    }
450
451
    /**
452
     * Creates the icon image tag for the page and wraps it in a link which will trigger the click menu.
453
     *
454
     * @param array $row Record array
455
     * @return string HTML for the icon
456
     */
457
    protected function getIcon($row)
458
    {
459
        // Initialization
460
        $toolTip = BackendUtility::getRecordToolTip($row, 'pages');
461
        $icon = '<span ' . $toolTip . '>' . $this->iconFactory->getIconForRecord('pages', $row, Icon::SIZE_SMALL)->render() . '</span>';
462
        // The icon with link
463
        if ($this->getBackendUser()->recordEditAccessInternals('pages', $row)) {
464
            $icon = BackendUtility::wrapClickMenuOnIcon($icon, 'pages', $row['uid']);
465
        }
466
        return $icon;
467
    }
468
    /**
469
     * Returns the HTML code for rendering a field in the pages table.
470
     * The row value is processed to a human readable form and the result is parsed through htmlspecialchars().
471
     *
472
     * @param string $field The name of the field of which the value should be rendered.
473
     * @param array $row The pages table row as an associative array.
474
     * @return string The rendered table field value.
475
     */
476
    protected function getPagesTableFieldValue($field, array $row)
477
    {
478
        return htmlspecialchars((string)BackendUtility::getProcessedValue('pages', $field, $row[$field]));
479
    }
480
481
    /**
482
     * Counts and returns the number of records on the page with $pid
483
     *
484
     * @param string $table Table name
485
     * @param int $pid Page id
486
     * @return int Number of records.
487
     */
488
    protected function numberOfRecords($table, $pid)
489
    {
490
        $count = 0;
491
        if ($GLOBALS['TCA'][$table]) {
492
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
493
                ->getQueryBuilderForTable($table);
494
            $queryBuilder->getRestrictions()
495
                ->removeAll()
496
                ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
497
                ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->getBackendUser()->workspace));
498
            $count = (int)$queryBuilder->count('uid')
499
                ->from($table)
500
                ->where(
501
                    $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pid, \PDO::PARAM_INT))
502
                )
503
                ->execute()
504
                ->fetchColumn();
505
        }
506
507
        return $count;
508
    }
509
510
    /**
511
     * Returns a table-row with the content from the fields in the input data array.
512
     * OBS: $this->fieldArray MUST be set! (represents the list of fields to display)
513
     *
514
     * @param array $data Is the data array, record with the fields. Notice: These fields are (currently) NOT htmlspecialchar'ed before being wrapped in <td>-tags
515
     * @return string HTML content for the table row
516
     */
517
    protected function addElement($data)
518
    {
519
        // Start up:
520
        $attributes = '';
521
        $rowTag = 'th';
522
        if (isset($data['_UID_'])) {
523
            $l10nParent = isset($data['_l10nparent_']) ? (int)$data['_l10nparent_'] : 0;
524
            $attributes = ' data-uid="' . $data['_UID_'] . '" data-l10nparent="' . $l10nParent . '"';
525
            $rowTag = 'td';
526
        }
527
        $out = '<tr' . $attributes . '>';
528
        // Init rendering.
529
        $colsp = '';
530
        $lastKey = '';
531
        $c = 0;
532
        // __label is used as the label key to circumvent problems with uid used as label (see #67756)
533
        // as it was introduced later on, check if it really exists before using it
534
        $fields = $this->fieldArray;
535
        if (array_key_exists('__label', $data)) {
536
            $fields[0] = '__label';
537
        }
538
        // Traverse field array which contains the data to present:
539
        foreach ($fields as $vKey) {
540
            if (isset($data[$vKey])) {
541
                if ($lastKey) {
542
                    $cssClass = $this->addElement_tdCssClass[$lastKey];
543
                    $out .= '
544
						<' . $rowTag . ' class="' . $cssClass . ' nowrap"' . $colsp . '>' . $data[$lastKey] . '</' . $rowTag . '>';
545
                }
546
                $lastKey = $vKey;
547
                $c = 1;
548
            } else {
549
                if (!$lastKey) {
550
                    $lastKey = $vKey;
551
                }
552
                $c++;
553
            }
554
            if ($c > 1) {
555
                $colsp = ' colspan="' . $c . '"';
556
            } else {
557
                $colsp = '';
558
            }
559
        }
560
        if ($lastKey) {
561
            $cssClass = $this->addElement_tdCssClass[$lastKey];
562
            $out .= '
563
				<' . $rowTag . ' class="' . $cssClass . ' nowrap"' . $colsp . '>' . $data[$lastKey] . '</' . $rowTag . '>';
564
        }
565
        $out .= '</tr>';
566
        return $out;
567
    }
568
569
    /**
570
     * @return BackendUserAuthentication
571
     */
572
    protected function getBackendUser(): BackendUserAuthentication
573
    {
574
        return $GLOBALS['BE_USER'];
575
    }
576
577
    /**
578
     * @return LanguageService|null
579
     */
580
    protected function getLanguageService(): ?LanguageService
581
    {
582
        return $GLOBALS['LANG'] ?? null;
583
    }
584
}
585