Passed
Push — master ( 04d844...b6278a )
by
unknown
14:01
created

PageInformationController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
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\Backend\View\BackendLayoutView;
23
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
24
use TYPO3\CMS\Core\Database\ConnectionPool;
25
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
26
use TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction;
27
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
28
use TYPO3\CMS\Core\Imaging\Icon;
29
use TYPO3\CMS\Core\Imaging\IconFactory;
30
use TYPO3\CMS\Core\Localization\LanguageService;
31
use TYPO3\CMS\Core\Type\Bitmask\Permission;
32
use TYPO3\CMS\Core\Utility\GeneralUtility;
33
34
/**
35
 * Class for displaying page information (records, page record properties) in Web -> Info
36
 * @internal This class is a specific Backend controller implementation and is not part of the TYPO3's Core API.
37
 */
38
class PageInformationController
39
{
40
    /**
41
     * @var array
42
     */
43
    protected $fieldConfiguration = [];
44
45
    /**
46
     * @var int Value of the GET/POST var 'id'
47
     */
48
    protected $id;
49
50
    /**
51
     * @var InfoModuleController Contains a reference to the parent calling object
52
     */
53
    protected $pObj;
54
55
    protected IconFactory $iconFactory;
56
    protected UriBuilder $uriBuilder;
57
    protected ?BackendLayoutView $backendLayoutView = null;
58
59
    /**
60
     * @var array
61
     */
62
    protected $fieldArray;
63
64
    /**
65
     * Keys are fieldnames and values are td-css-classes to add in addElement();
66
     *
67
     * @var array
68
     */
69
    protected $addElement_tdCssClass = [];
70
71
    public function __construct(IconFactory $iconFactory, UriBuilder $uriBuilder)
72
    {
73
        $this->iconFactory = $iconFactory;
74
        $this->uriBuilder = $uriBuilder;
75
    }
76
77
    /**
78
     * Init, called from parent object
79
     *
80
     * @param InfoModuleController $pObj A reference to the parent (calling) object
81
     * @param ServerRequestInterface $request
82
     */
83
    public function init(InfoModuleController $pObj, ServerRequestInterface $request)
84
    {
85
        $this->pObj = $pObj;
86
        $this->id = (int)($request->getParsedBody()['id'] ?? $request->getQueryParams()['id'] ?? 0);
87
        // Setting MOD_MENU items as we need them for logging:
88
        $this->pObj->MOD_MENU = array_merge($this->pObj->MOD_MENU, $this->modMenu());
89
    }
90
91
    /**
92
     * Main, called from parent object
93
     *
94
     * @param ServerRequestInterface $request
95
     * @return string Output HTML for the module.
96
     */
97
    public function main(ServerRequestInterface $request)
98
    {
99
        $theOutput = '<h1>' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:info/Resources/Private/Language/locallang_webinfo.xlf:page_title')) . '</h1>';
100
101
        if (isset($this->fieldConfiguration[$this->pObj->MOD_SETTINGS['pages']])) {
102
            $this->fieldArray = $this->fieldConfiguration[$this->pObj->MOD_SETTINGS['pages']]['fields'];
103
        }
104
105
        $theOutput .= '
106
        <div class="row row-cols-auto mb-3 g-3 align-items-center">
107
            <div class="col">
108
                <label class="form-lable">' .
109
                    htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:info/Resources/Private/Language/locallang_webinfo.xlf:moduleFunctions.depth')) .
110
                '</label> ' .
111
                BackendUtility::getDropdownMenu($this->id, 'SET[depth]', $this->pObj->MOD_SETTINGS['depth'], $this->pObj->MOD_MENU['depth']) .
112
            '</div>
113
            <div class="col">
114
                <label class="form-lable">' .
115
                    htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:info/Resources/Private/Language/locallang_webinfo.xlf:moduleFunctions.type')) .
116
                '</label> ' .
117
                BackendUtility::getDropdownMenu($this->id, 'SET[pages]', $this->pObj->MOD_SETTINGS['pages'], $this->pObj->MOD_MENU['pages']) .
118
            '</div>' .
119
            BackendUtility::cshItem('_MOD_web_info', 'func_' . $this->pObj->MOD_SETTINGS['pages'], '', '<div class="col"><span class="btn btn-default btn-sm">|</span></div>') .
120
        '</div>'
121
            . $this->getTable_pages($this->id, (int)$this->pObj->MOD_SETTINGS['depth'], $request);
122
123
        // Additional footer content
124
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/web_info/class.tx_cms_webinfo.php']['drawFooterHook'] ?? [] as $hook) {
125
            $params = [
126
                'request' => $request
127
            ];
128
            $theOutput .= GeneralUtility::callUserFunction($hook, $params, $this);
129
        }
130
        return $theOutput;
131
    }
132
133
    /**
134
     * Returns the menu array
135
     *
136
     * @return array
137
     */
138
    protected function modMenu()
139
    {
140
        $menu = [
141
            'pages' => [],
142
            'depth' => [
143
                0 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_0'),
144
                1 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_1'),
145
                2 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_2'),
146
                3 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_3'),
147
                4 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_4'),
148
                999 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_infi')
149
            ]
150
        ];
151
152
        $this->fillFieldConfiguration($this->id);
153
        foreach ($this->fieldConfiguration as $key => $item) {
154
            $menu['pages'][$key] = $item['label'];
155
        }
156
        return $menu;
157
    }
158
159
    /**
160
     * Function, which returns all tables to
161
     * which the user has access. Also a set of standard tables (pages, sys_filemounts, etc...)
162
     * are filtered out. So what is left is basically all tables which makes sense to list content from.
163
     *
164
     * @return string
165
     */
166
    protected function cleanTableNames(): string
167
    {
168
        // Get all table names:
169
        $tableNames = array_flip(array_keys($GLOBALS['TCA']));
170
        // Unset common names:
171
        unset($tableNames['pages']);
172
        unset($tableNames['sys_filemounts']);
173
        unset($tableNames['sys_action']);
174
        unset($tableNames['sys_workflows']);
175
        unset($tableNames['be_users']);
176
        unset($tableNames['be_groups']);
177
        $allowedTableNames = [];
178
        // Traverse table names and set them in allowedTableNames array IF they can be read-accessed by the user.
179
        if (is_array($tableNames)) {
0 ignored issues
show
introduced by
The condition is_array($tableNames) is always true.
Loading history...
180
            foreach ($tableNames as $k => $v) {
181
                if (!($GLOBALS['TCA'][$k]['ctrl']['hideTable'] ?? false) && $this->getBackendUser()->check('tables_select', $k)) {
182
                    $allowedTableNames['table_' . $k] = $k;
183
                }
184
            }
185
        }
186
        return implode(',', array_keys($allowedTableNames));
187
    }
188
189
    /**
190
     * Generate configuration for field selection
191
     *
192
     * @param int $pageId current page id
193
     */
194
    protected function fillFieldConfiguration(int $pageId)
195
    {
196
        $modTSconfig = BackendUtility::getPagesTSconfig($pageId)['mod.']['web_info.']['fieldDefinitions.'] ?? [];
197
        foreach ($modTSconfig as $key => $item) {
198
            $fieldList = str_replace('###ALL_TABLES###', $this->cleanTableNames(), $item['fields']);
199
            $fields = GeneralUtility::trimExplode(',', $fieldList, true);
200
            $key = trim($key, '.');
201
            $this->fieldConfiguration[$key] = [
202
                'label' => $item['label'] ? $this->getLanguageService()->sL($item['label']) : $key,
203
                'fields' => $fields
204
            ];
205
        }
206
    }
207
208
    /**
209
     * Renders records from the pages table from page id
210
     *
211
     * @param int $id Page id
212
     * @param int $depth
213
     * @param ServerRequestInterface $request
214
     * @return string HTML for the listing
215
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
216
     */
217
    protected function getTable_pages($id, int $depth, ServerRequestInterface $request)
218
    {
219
        $out = '';
220
        $lang = $this->getLanguageService();
221
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
222
            ->getQueryBuilderForTable('pages');
223
        $queryBuilder->getRestrictions()
224
            ->removeAll()
225
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
226
        $row = $queryBuilder
227
            ->select('*')
228
            ->from('pages')
229
            ->where(
230
                $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)),
231
                $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW)
232
            )
233
            ->execute()
234
            ->fetch();
235
        BackendUtility::workspaceOL('pages', $row);
236
        // If there was found a page:
237
        if (is_array($row)) {
238
            // Creating elements
239
            $editUids = [];
240
            // Getting children
241
            $theRows = $this->getPageRecordsRecursive($row['uid'], $depth);
242
            if ($this->getBackendUser()->doesUserHaveAccess($row, Permission::PAGE_EDIT) && $row['uid'] > 0) {
243
                $editUids[] = $row['uid'];
244
            }
245
            $out .= $this->pages_drawItem($row, $this->fieldArray, $request);
246
            // Traverse all pages selected:
247
            foreach ($theRows as $sRow) {
248
                if ($this->getBackendUser()->doesUserHaveAccess($sRow, Permission::PAGE_EDIT)) {
249
                    $editUids[] = $sRow['uid'];
250
                }
251
                $out .= $this->pages_drawItem($sRow, $this->fieldArray, $request);
252
            }
253
            // Header line is drawn
254
            $headerCells = [];
255
            $editIdList = implode(',', $editUids);
256
            // Traverse fields (as set above) in order to create header values:
257
            foreach ($this->fieldArray as $field) {
258
                $editButton = '';
259
                if ($editIdList && isset($GLOBALS['TCA']['pages']['columns'][$field]) && $field !== 'uid') {
260
                    $iTitle = sprintf(
261
                        $lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:editThisColumn'),
262
                        rtrim(trim($lang->sL(BackendUtility::getItemLabel('pages', $field))), ':')
263
                    );
264
                    $urlParameters = [
265
                        'edit' => [
266
                            'pages' => [
267
                                $editIdList => 'edit'
268
                            ]
269
                        ],
270
                        'columnsOnly' => $field,
271
                        'returnUrl' => $request->getAttribute('normalizedParams')->getRequestUri()
272
                    ];
273
                    $url = (string)$this->uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
274
                    $editButton = '<a class="btn btn-default" href="' . htmlspecialchars($url)
275
                        . '" title="' . htmlspecialchars($iTitle) . '">'
276
                        . $this->iconFactory->getIcon('actions-document-open', Icon::SIZE_SMALL)->render() . '</a>';
277
                }
278
                switch ($field) {
279
                    case 'title':
280
                        $headerCells[$field] = $editButton . '&nbsp;<strong>'
281
                            . $lang->sL($GLOBALS['TCA']['pages']['columns'][$field]['label'])
282
                            . '</strong>';
283
                        break;
284
                    case 'uid':
285
                        $headerCells[$field] = '';
286
                        break;
287
                    case 'actual_backend_layout':
288
                        $headerCells[$field] = htmlspecialchars($lang->sL('LLL:EXT:info/Resources/Private/Language/locallang_webinfo.xlf:actual_backend_layout'));
289
                        break;
290
                    default:
291
                        if (strpos($field, 'table_') === 0) {
292
                            $f2 = substr($field, 6);
293
                            if ($GLOBALS['TCA'][$f2]) {
294
                                $headerCells[$field] = '&nbsp;' .
295
                                    '<span title="' .
296
                                    htmlspecialchars($lang->sL($GLOBALS['TCA'][$f2]['ctrl']['title'])) .
297
                                    '">' .
298
                                    $this->iconFactory->getIconForRecord($f2, [], Icon::SIZE_SMALL)->render() .
299
                                    '</span>';
300
                            }
301
                        } else {
302
                            $headerCells[$field] = $editButton . '&nbsp;<strong>'
303
                                . htmlspecialchars($lang->sL($GLOBALS['TCA']['pages']['columns'][$field]['label']))
304
                                . '</strong>';
305
                        }
306
                }
307
            }
308
            $out = '<div class="table-responsive">'
309
                . '<table class="table table-striped table-hover mb-0">'
310
                . '<thead>'
311
                . $this->addElement($headerCells)
312
                . '</thead>'
313
                . '<tbody>'
314
                . $out
315
                . '</tbody>'
316
                . '</table>'
317
                . '</div>';
318
        }
319
        return $out;
320
    }
321
322
    /**
323
     * Adds pages-rows to an array, selecting recursively in the page tree.
324
     *
325
     * @param int $pid Starting page id to select from
326
     * @param string $iconPrefix Prefix for icon code.
327
     * @param int $depth Depth (decreasing)
328
     * @param array $rows Array which will accumulate page rows
329
     * @return array $rows with added rows.
330
     */
331
    protected function getPageRecordsRecursive(int $pid, int $depth, string $iconPrefix = '', array $rows = []): array
332
    {
333
        $depth--;
334
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
335
        $queryBuilder->getRestrictions()
336
            ->removeAll()
337
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
338
            ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->getBackendUser()->workspace));
339
340
        $queryBuilder
341
            ->select('*')
342
            ->from('pages')
343
            ->where(
344
                $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pid, \PDO::PARAM_INT)),
345
                $queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)),
346
                $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW)
347
            );
348
349
        if (!empty($GLOBALS['TCA']['pages']['ctrl']['sortby'])) {
350
            $queryBuilder->orderBy($GLOBALS['TCA']['pages']['ctrl']['sortby']);
351
        }
352
353
        if ($depth >= 0) {
354
            $result = $queryBuilder->execute();
355
            $rowCount = $queryBuilder->count('uid')->execute()->fetchColumn(0);
356
            $count = 0;
357
            while ($row = $result->fetch()) {
358
                BackendUtility::workspaceOL('pages', $row);
359
                if (is_array($row)) {
360
                    $count++;
361
                    $row['treeIcons'] = $iconPrefix
362
                        . '<span class="treeline-icon treeline-icon-join'
363
                        . ($rowCount === $count ? 'bottom' : '')
364
                        . '"></span>';
365
                    $rows[] = $row;
366
                    // Get the branch
367
                    $spaceOutIcons = '<span class="treeline-icon treeline-icon-'
368
                        . ($rowCount === $count ? 'clear' : 'line')
369
                        . '"></span>';
370
                    $rows = $this->getPageRecordsRecursive(
371
                        $row['uid'],
372
                        $row['php_tree_stop'] ? 0 : $depth,
373
                        $iconPrefix . $spaceOutIcons,
374
                        $rows
375
                    );
376
                }
377
            }
378
        }
379
380
        return $rows;
381
    }
382
383
    /**
384
     * Adds a list item for the pages-rendering
385
     *
386
     * @param array $row Record array
387
     * @param array $fieldArr Field list
388
     * @param ServerRequestInterface $request
389
     * @return string HTML for the item
390
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
391
     */
392
    protected function pages_drawItem($row, $fieldArr, ServerRequestInterface $request)
393
    {
394
        $this->backendLayoutView = GeneralUtility::makeInstance(BackendLayoutView::class);
395
        $backendLayouts = $this->getBackendLayouts($row, 'backend_layout');
396
        $backendLayoutsNextLevel = $this->getBackendLayouts($row, 'backend_layout_next_level');
397
        $userTsConfig = $this->getBackendUser()->getTSConfig();
398
        $theIcon = $this->getIcon($row);
399
        // Preparing and getting the data-array
400
        $theData = [];
401
        foreach ($fieldArr as $field) {
402
            switch ($field) {
403
                case 'title':
404
                    $showPageId = !empty($userTsConfig['options.']['pageTree.']['showPageIdWithTitle']);
405
                    $pTitle = htmlspecialchars((string)BackendUtility::getProcessedValue('pages', $field, $row[$field], 20));
406
                    $theData[$field] = ($row['treeIcons'] ?? '') . $theIcon . ($showPageId ? '[' . $row['uid'] . '] ' : '') . $pTitle;
407
                    break;
408
                case 'php_tree_stop':
409
                    // Intended fall through
410
                case 'TSconfig':
411
                    $theData[$field] = $row[$field] ? '<strong>x</strong>' : '&nbsp;';
412
                    break;
413
                case 'actual_backend_layout':
414
                    $backendLayout = $this->backendLayoutView->getBackendLayoutForPage($row['uid']);
0 ignored issues
show
Bug introduced by
The method getBackendLayoutForPage() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

414
                    /** @scrutinizer ignore-call */ 
415
                    $backendLayout = $this->backendLayoutView->getBackendLayoutForPage($row['uid']);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
415
                    $theData[$field] = $backendLayout !== null
416
                        ? htmlspecialchars($this->getLanguageService()->sL($backendLayout->getTitle()))
417
                        : '';
418
                    break;
419
                case 'backend_layout':
420
                    $layout = $backendLayouts[$row[$field]] ?? null;
421
                    $theData[$field] = $layout ? htmlspecialchars($layout) : $this->getPagesTableFieldValue($field, $row);
422
                    break;
423
                case 'backend_layout_next_level':
424
                    $layout = $backendLayoutsNextLevel[$row[$field]] ?? null;
425
                    $theData[$field] = $layout ? htmlspecialchars($layout) : $this->getPagesTableFieldValue($field, $row);
426
                    break;
427
                case 'uid':
428
                    $uid = 0;
429
                    $editButton = '';
430
                    if ($this->getBackendUser()->doesUserHaveAccess($row, 2) && $row['uid'] > 0) {
431
                        $uid = (int)$row['uid'];
432
                        $urlParameters = [
433
                            'edit' => [
434
                                'pages' => [
435
                                    $row['uid'] => 'edit'
436
                                ]
437
                            ],
438
                            'returnUrl' => $request->getAttribute('normalizedParams')->getRequestUri()
439
                        ];
440
                        $url = (string)$this->uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
441
                        $attributes = PreviewUriBuilder::create((int)$row['uid'])
442
                            ->withRootLine(BackendUtility::BEgetRootLine($row['uid']))
443
                            ->serializeDispatcherAttributes();
444
                        $editButton =
445
                            '<a href="#" ' . $attributes . ' class="btn btn-default" title="' .
446
                            htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.showPage')) . '">' .
447
                            $this->iconFactory->getIcon('actions-view-page', Icon::SIZE_SMALL)->render() .
448
                            '</a>';
449
                        $editButton .=
450
                            '<a class="btn btn-default" href="' . htmlspecialchars($url) . '" title="' .
451
                            htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:editDefaultLanguagePage')) . '">' .
452
                            $this->iconFactory->getIcon('actions-page-open', Icon::SIZE_SMALL)->render() .
453
                            '</a>';
454
                    }
455
                    // Since the uid is overwritten with the edit button markup we need to store
456
                    // the actual uid to be able to add it as data attribute to the table data cell.
457
                    // This also makes distinction between record rows and the header line simpler.
458
                    $theData['_UID_'] = $uid;
459
                    $theData[$field] = '<div class="btn-group" role="group">' . $editButton . '</div>';
460
                    break;
461
                case 'shortcut':
462
                case 'shortcut_mode':
463
                    if ((int)$row['doktype'] === PageRepository::DOKTYPE_SHORTCUT) {
464
                        $theData[$field] = $this->getPagesTableFieldValue($field, $row);
465
                    }
466
                    break;
467
                default:
468
                    if (strpos($field, 'table_') === 0) {
469
                        $f2 = substr($field, 6);
470
                        if ($GLOBALS['TCA'][$f2]) {
471
                            $c = $this->numberOfRecords($f2, $row['uid']);
472
                            $theData[$field] = ($c ?: '');
473
                        }
474
                    } else {
475
                        $theData[$field] = $this->getPagesTableFieldValue($field, $row);
476
                    }
477
            }
478
        }
479
        $this->addElement_tdCssClass['title'] = $row['_CSSCLASS'] ?? '';
480
        return $this->addElement($theData);
481
    }
482
483
    /**
484
     * Creates the icon image tag for the page and wraps it in a link which will trigger the click menu.
485
     *
486
     * @param array $row Record array
487
     * @return string HTML for the icon
488
     */
489
    protected function getIcon($row)
490
    {
491
        // Initialization
492
        $toolTip = BackendUtility::getRecordToolTip($row, 'pages');
493
        $icon = '<span ' . $toolTip . '>' . $this->iconFactory->getIconForRecord('pages', $row, Icon::SIZE_SMALL)->render() . '</span>';
494
        // The icon with link
495
        if ($this->getBackendUser()->recordEditAccessInternals('pages', $row)) {
496
            $icon = BackendUtility::wrapClickMenuOnIcon($icon, 'pages', $row['uid']);
497
        }
498
        return $icon;
499
    }
500
    /**
501
     * Returns the HTML code for rendering a field in the pages table.
502
     * The row value is processed to a human readable form and the result is parsed through htmlspecialchars().
503
     *
504
     * @param string $field The name of the field of which the value should be rendered.
505
     * @param array $row The pages table row as an associative array.
506
     * @return string The rendered table field value.
507
     */
508
    protected function getPagesTableFieldValue($field, array $row)
509
    {
510
        return htmlspecialchars((string)BackendUtility::getProcessedValue('pages', $field, $row[$field]));
511
    }
512
513
    /**
514
     * Counts and returns the number of records on the page with $pid
515
     *
516
     * @param string $table Table name
517
     * @param int $pid Page id
518
     * @return int Number of records.
519
     */
520
    protected function numberOfRecords($table, $pid)
521
    {
522
        $count = 0;
523
        if ($GLOBALS['TCA'][$table]) {
524
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
525
                ->getQueryBuilderForTable($table);
526
            $queryBuilder->getRestrictions()
527
                ->removeAll()
528
                ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
529
                ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->getBackendUser()->workspace));
530
            $count = (int)$queryBuilder->count('uid')
531
                ->from($table)
532
                ->where(
533
                    $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pid, \PDO::PARAM_INT))
534
                )
535
                ->execute()
536
                ->fetchColumn();
537
        }
538
539
        return $count;
540
    }
541
542
    /**
543
     * Returns a table-row with the content from the fields in the input data array.
544
     * OBS: $this->fieldArray MUST be set! (represents the list of fields to display)
545
     *
546
     * @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
547
     * @return string HTML content for the table row
548
     */
549
    protected function addElement($data)
550
    {
551
        // Start up:
552
        $attributes = '';
553
        $rowTag = 'th';
554
        if (isset($data['_UID_'])) {
555
            $l10nParent = isset($data['_l10nparent_']) ? (int)$data['_l10nparent_'] : 0;
556
            $attributes = ' data-uid="' . $data['_UID_'] . '" data-l10nparent="' . $l10nParent . '"';
557
            $rowTag = 'td';
558
        }
559
        $out = '<tr' . $attributes . '>';
560
        // Init rendering.
561
        $colsp = '';
562
        $lastKey = '';
563
        $c = 0;
564
        // __label is used as the label key to circumvent problems with uid used as label (see #67756)
565
        // as it was introduced later on, check if it really exists before using it
566
        $fields = $this->fieldArray;
567
        if (array_key_exists('__label', $data)) {
568
            $fields[0] = '__label';
569
        }
570
        // Traverse field array which contains the data to present:
571
        foreach ($fields as $vKey) {
572
            if (isset($data[$vKey])) {
573
                if ($lastKey) {
574
                    $cssClass = $this->addElement_tdCssClass[$lastKey] ?? '';
575
                    $out .= '
576
						<' . $rowTag . ' class="' . $cssClass . ' nowrap"' . $colsp . '>' . $data[$lastKey] . '</' . $rowTag . '>';
577
                }
578
                $lastKey = $vKey;
579
                $c = 1;
580
            } else {
581
                if (!$lastKey) {
582
                    $lastKey = $vKey;
583
                }
584
                $c++;
585
            }
586
            if ($c > 1) {
587
                $colsp = ' colspan="' . $c . '"';
588
            } else {
589
                $colsp = '';
590
            }
591
        }
592
        if ($lastKey) {
593
            $cssClass = $this->addElement_tdCssClass[$lastKey] ?? '';
594
            $out .= '
595
				<' . $rowTag . ' class="' . $cssClass . ' nowrap"' . $colsp . '>' . $data[$lastKey] . '</' . $rowTag . '>';
596
        }
597
        $out .= '</tr>';
598
        return $out;
599
    }
600
601
    /**
602
     * @return BackendUserAuthentication
603
     */
604
    protected function getBackendUser(): BackendUserAuthentication
605
    {
606
        return $GLOBALS['BE_USER'];
607
    }
608
609
    /**
610
     * @return LanguageService|null
611
     */
612
    protected function getLanguageService(): ?LanguageService
613
    {
614
        return $GLOBALS['LANG'] ?? null;
615
    }
616
617
    protected function getBackendLayouts(array $row, string $field): array
618
    {
619
        if ($this->backendLayoutView === null) {
620
            $this->backendLayoutView = GeneralUtility::makeInstance(BackendLayoutView::class);
621
        }
622
        $configuration = ['row' => $row, 'table' => 'pages', 'field' => $field, 'items' => []];
623
        // Below we call the itemsProcFunc to retrieve properly resolved backend layout items,
624
        // including the translated labels and the correct field values (backend layout identifiers).
625
        $this->backendLayoutView->addBackendLayoutItems($configuration);
626
        $backendLayouts = [];
627
        foreach ($configuration['items'] ?? [] as $backendLayout) {
628
            if (($backendLayout[0] ?? false) && ($backendLayout[1] ?? false)) {
629
                $backendLayouts[$backendLayout[1]] = $backendLayout[0];
630
            }
631
        }
632
        return $backendLayouts;
633
    }
634
}
635