Passed
Push — master ( 678dfc...3b3321 )
by
unknown
12:52
created

RecordProvider::parentPageCanBeViewed()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
c 0
b 0
f 0
dl 0
loc 13
rs 10
cc 3
nc 2
nop 0
1
<?php
2
3
declare(strict_types=1);
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
namespace TYPO3\CMS\Backend\ContextMenu\ItemProviders;
19
20
use TYPO3\CMS\Backend\Routing\UriBuilder;
21
use TYPO3\CMS\Backend\Utility\BackendUtility;
22
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
23
use TYPO3\CMS\Core\Routing\UnableToLinkToPageException;
24
use TYPO3\CMS\Core\Type\Bitmask\JsConfirmation;
25
use TYPO3\CMS\Core\Type\Bitmask\Permission;
26
use TYPO3\CMS\Core\Utility\GeneralUtility;
27
use TYPO3\CMS\Core\Versioning\VersionState;
28
29
/**
30
 * Class responsible for providing click menu items for db records which don't have custom provider (as e.g. pages)
31
 */
32
class RecordProvider extends AbstractProvider
33
{
34
    /**
35
     * Database record
36
     *
37
     * @var array
38
     */
39
    protected $record = [];
40
41
    /**
42
     * Database record of the page $this->record is placed on
43
     *
44
     * @var array
45
     */
46
    protected $pageRecord = [];
47
48
    /**
49
     * Local cache for the result of BackendUserAuthentication::calcPerms()
50
     *
51
     * @var Permission
52
     */
53
    protected $pagePermissions;
54
55
    /**
56
     * @var array
57
     */
58
    protected $itemsConfiguration = [
59
        'view' => [
60
            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.view',
61
            'iconIdentifier' => 'actions-view',
62
            'callbackAction' => 'viewRecord'
63
        ],
64
        'edit' => [
65
            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.edit',
66
            'iconIdentifier' => 'actions-open',
67
            'callbackAction' => 'editRecord'
68
        ],
69
        'new' => [
70
            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.new',
71
            'iconIdentifier' => 'actions-add',
72
            'callbackAction' => 'newRecord'
73
        ],
74
        'info' => [
75
            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.info',
76
            'iconIdentifier' => 'actions-document-info',
77
            'callbackAction' => 'openInfoPopUp'
78
        ],
79
        'divider1' => [
80
            'type' => 'divider'
81
        ],
82
        'copy' => [
83
            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.copy',
84
            'iconIdentifier' => 'actions-edit-copy',
85
            'callbackAction' => 'copy'
86
        ],
87
        'copyRelease' => [
88
            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.copy',
89
            'iconIdentifier' => 'actions-edit-copy-release',
90
            'callbackAction' => 'clipboardRelease'
91
        ],
92
        'cut' => [
93
            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.cut',
94
            'iconIdentifier' => 'actions-edit-cut',
95
            'callbackAction' => 'cut'
96
        ],
97
        'cutRelease' => [
98
            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.cutrelease',
99
            'iconIdentifier' => 'actions-edit-cut-release',
100
            'callbackAction' => 'clipboardRelease'
101
        ],
102
        'pasteAfter' => [
103
            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.pasteafter',
104
            'iconIdentifier' => 'actions-document-paste-after',
105
            'callbackAction' => 'pasteAfter'
106
        ],
107
        'divider2' => [
108
            'type' => 'divider'
109
        ],
110
        'more' => [
111
            'type' => 'submenu',
112
            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.more',
113
            'iconIdentifier' => '',
114
            'callbackAction' => 'openSubmenu',
115
            'childItems' => [
116
                'newWizard' => [
117
                    'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_misc.xlf:CM_newWizard',
118
                    'iconIdentifier' => 'actions-add',
119
                    'callbackAction' => 'newContentWizard',
120
                ],
121
                'openListModule' => [
122
                    'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_misc.xlf:CM_db_list',
123
                    'iconIdentifier' => 'actions-system-list-open',
124
                    'callbackAction' => 'openListModule',
125
                ],
126
            ],
127
        ],
128
        'divider3' => [
129
            'type' => 'divider'
130
        ],
131
        'enable' => [
132
            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:enable',
133
            'iconIdentifier' => 'actions-edit-unhide',
134
            'callbackAction' => 'enableRecord',
135
        ],
136
        'disable' => [
137
            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:disable',
138
            'iconIdentifier' => 'actions-edit-hide',
139
            'callbackAction' => 'disableRecord',
140
        ],
141
        'delete' => [
142
            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.delete',
143
            'iconIdentifier' => 'actions-edit-delete',
144
            'callbackAction' => 'deleteRecord',
145
        ],
146
        'history' => [
147
            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_misc.xlf:CM_history',
148
            'iconIdentifier' => 'actions-document-history-open',
149
            'callbackAction' => 'openHistoryPopUp',
150
        ],
151
    ];
152
153
    /**
154
     * Whether this provider should kick in
155
     *
156
     * @return bool
157
     */
158
    public function canHandle(): bool
159
    {
160
        if (in_array($this->table, ['sys_file', 'pages'], true)) {
161
            return false;
162
        }
163
        return isset($GLOBALS['TCA'][$this->table]);
164
    }
165
166
    /**
167
     * Initialize db record
168
     */
169
    protected function initialize()
170
    {
171
        parent::initialize();
172
        $this->record = BackendUtility::getRecordWSOL($this->table, (int)$this->identifier);
173
        $this->initPermissions();
174
    }
175
176
    /**
177
     * Priority is set to lower then default value, in order to skip this provider if there is less generic provider available.
178
     *
179
     * @return int
180
     */
181
    public function getPriority(): int
182
    {
183
        return 60;
184
    }
185
186
    /**
187
     * This provider works as a fallback if there is no provider dedicated for certain table, thus it's only kicking in when $items are empty.
188
     *
189
     * @param array $items
190
     * @return array
191
     */
192
    public function addItems(array $items): array
193
    {
194
        if (!empty($items)) {
195
            return $items;
196
        }
197
        $this->initialize();
198
        return $this->prepareItems($this->itemsConfiguration);
199
    }
200
201
    /**
202
     * Whether a given item can be rendered (e.g. user has enough permissions)
203
     *
204
     * @param string $itemName
205
     * @param string $type
206
     * @return bool
207
     */
208
    protected function canRender(string $itemName, string $type): bool
209
    {
210
        if (in_array($type, ['divider', 'submenu'], true)) {
211
            return true;
212
        }
213
        if (in_array($itemName, $this->disabledItems, true)) {
214
            return false;
215
        }
216
        $canRender = false;
217
        switch ($itemName) {
218
            case 'view':
219
                $canRender = $this->canBeViewed();
220
                break;
221
            case 'edit':
222
                $canRender = $this->canBeEdited();
223
                break;
224
            case 'new':
225
                $canRender = $this->canBeNew();
226
                break;
227
            case 'newWizard':
228
                $canRender = $this->canOpenNewCEWizard();
229
                break;
230
            case 'info':
231
                $canRender = $this->canShowInfo();
232
                break;
233
            case 'enable':
234
                $canRender = $this->canBeEnabled();
235
                break;
236
            case 'disable':
237
                $canRender = $this->canBeDisabled();
238
                break;
239
            case 'delete':
240
                $canRender = $this->canBeDeleted();
241
                break;
242
            case 'history':
243
                $canRender = $this->canShowHistory();
244
                break;
245
            case 'openListModule':
246
                $canRender = $this->canOpenListModule();
247
                break;
248
            case 'copy':
249
                $canRender = $this->canBeCopied();
250
                break;
251
            case 'copyRelease':
252
                $canRender = $this->isRecordInClipboard('copy');
253
                break;
254
            case 'cut':
255
                $canRender = $this->canBeCut();
256
                break;
257
            case 'cutRelease':
258
                $canRender = $this->isRecordInClipboard('cut');
259
                break;
260
            case 'pasteAfter':
261
                $canRender = $this->canBePastedAfter();
262
                break;
263
        }
264
        return $canRender;
265
    }
266
267
    /**
268
     * Saves calculated permissions for a page containing given record, to speed things up
269
     */
270
    protected function initPermissions()
271
    {
272
        $this->pageRecord = BackendUtility::getRecord('pages', $this->record['pid']) ?? [];
273
        $this->pagePermissions = new Permission($this->backendUser->calcPerms($this->pageRecord));
274
    }
275
276
    /**
277
     * Returns true if a current user have access to given permission
278
     *
279
     * @see BackendUserAuthentication::doesUserHaveAccess()
280
     * @param int $permission
281
     * @return bool
282
     */
283
    protected function hasPagePermission(int $permission): bool
284
    {
285
        return $this->backendUser->isAdmin() || $this->pagePermissions->isGranted($permission);
286
    }
287
288
    /**
289
     * Additional attributes for JS
290
     *
291
     * @param string $itemName
292
     * @return array
293
     */
294
    protected function getAdditionalAttributes(string $itemName): array
295
    {
296
        $attributes = [];
297
        if ($itemName === 'view') {
298
            $attributes += $this->getViewAdditionalAttributes();
299
        }
300
        if ($itemName === 'enable' || $itemName === 'disable') {
301
            $attributes += $this->getEnableDisableAdditionalAttributes();
302
        }
303
        if ($itemName === 'newWizard' && $this->table === 'tt_content') {
304
            $moduleName = BackendUtility::getPagesTSconfig($this->record['pid'])['mod.']['newContentElementWizard.']['override']
305
                ?? 'new_content_element_wizard';
306
            $urlParameters = [
307
                'id' => $this->record['pid'],
308
                'sys_language_uid' => $this->record['sys_language_uid'],
309
                'colPos' => $this->record['colPos'],
310
                'uid_pid' => -$this->record['uid']
311
            ];
312
            $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
313
            $url = (string)$uriBuilder->buildUriFromRoute($moduleName, $urlParameters);
314
            $attributes += [
315
                'data-new-wizard-url' => htmlspecialchars($url),
316
                'data-title' => $this->languageService->getLL('newContentElement'),
317
            ];
318
        }
319
        if ($itemName === 'delete') {
320
            $attributes += $this->getDeleteAdditionalAttributes();
321
        }
322
        if ($itemName === 'openListModule') {
323
            $attributes += [
324
                'data-page-uid' => $this->record['pid']
325
            ];
326
        }
327
        if ($itemName === 'pasteAfter') {
328
            $attributes += $this->getPasteAdditionalAttributes('after');
329
        }
330
        return $attributes;
331
    }
332
333
    /**
334
     * Additional attributes for the 'view' item
335
     *
336
     * @return array
337
     */
338
    protected function getViewAdditionalAttributes(): array
339
    {
340
        $attributes = [];
341
        $viewLink = $this->getViewLink();
342
        if ($viewLink) {
343
            $attributes += [
344
                'data-preview-url' => htmlspecialchars($viewLink),
345
            ];
346
        }
347
        return $attributes;
348
    }
349
350
    /**
351
     * Additional attributes for the hide & unhide items
352
     *
353
     * @return array
354
     */
355
    protected function getEnableDisableAdditionalAttributes(): array
356
    {
357
        return [
358
            'data-disable-field' => $GLOBALS['TCA'][$this->table]['ctrl']['enablecolumns']['disabled'] ?? ''
359
        ];
360
    }
361
362
    /**
363
     * Additional attributes for the pasteInto and pasteAfter items
364
     *
365
     * @param string $type "after" or "into"
366
     * @return array
367
     */
368
    protected function getPasteAdditionalAttributes(string $type): array
369
    {
370
        $closeText = $this->languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:cancel');
371
        $okText = $this->languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:ok');
372
        $attributes = [];
373
        if ($this->backendUser->jsConfirmation(JsConfirmation::COPY_MOVE_PASTE)) {
374
            $selItem = $this->clipboard->getSelectedRecord();
375
            $title = $this->languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:clip_paste');
376
377
            $confirmMessage = sprintf(
378
                $this->languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:mess.'
379
                    . ($this->clipboard->currentMode() === 'copy' ? 'copy' : 'move') . '_' . $type),
380
                GeneralUtility::fixed_lgd_cs($selItem['_RECORD_TITLE'], $this->backendUser->uc['titleLen']),
381
                GeneralUtility::fixed_lgd_cs(BackendUtility::getRecordTitle($this->table, $this->record), $this->backendUser->uc['titleLen'])
382
            );
383
            $attributes += [
384
                'data-title' => htmlspecialchars($title),
385
                'data-message' => htmlspecialchars($confirmMessage),
386
                'data-button-close-text' => htmlspecialchars($closeText),
387
                'data-button-ok-text' => htmlspecialchars($okText),
388
            ];
389
        }
390
        return $attributes;
391
    }
392
393
    /**
394
     * Additional data for a "delete" action (confirmation modal title and message)
395
     *
396
     * @return array
397
     */
398
    protected function getDeleteAdditionalAttributes(): array
399
    {
400
        $closeText = $this->languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:cancel');
401
        $okText = $this->languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:delete');
402
        $attributes = [];
403
        if ($this->backendUser->jsConfirmation(JsConfirmation::DELETE)) {
404
            $recordTitle = GeneralUtility::fixed_lgd_cs(BackendUtility::getRecordTitle($this->table, $this->record), $this->backendUser->uc['titleLen']);
405
406
            $title = $this->languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:delete');
407
            $confirmMessage = sprintf(
408
                $this->languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:mess.delete'),
409
                $recordTitle
410
            );
411
            $confirmMessage .= BackendUtility::referenceCount(
412
                $this->table,
413
                $this->record['uid'],
414
                ' ' . $this->languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.referencesToRecord')
415
            );
416
            $confirmMessage .= BackendUtility::translationCount(
417
                $this->table,
418
                $this->record['uid'],
419
                ' ' . $this->languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.translationsOfRecord')
420
            );
421
            $attributes += [
422
                'data-title' => htmlspecialchars($title),
423
                'data-message' => htmlspecialchars($confirmMessage),
424
                'data-button-close-text' => htmlspecialchars($closeText),
425
                'data-button-ok-text' => htmlspecialchars($okText),
426
            ];
427
        }
428
        return $attributes;
429
    }
430
431
    /**
432
     * Returns id of the Page used for preview
433
     *
434
     * @return int
435
     */
436
    protected function getPreviewPid(): int
437
    {
438
        return (int)$this->record['pid'];
439
    }
440
441
    /**
442
     * Returns the view link
443
     *
444
     * @return string
445
     */
446
    protected function getViewLink(): string
447
    {
448
        $anchorSection = '';
449
        $additionalParams = '';
450
        if ($this->table === 'tt_content') {
451
            $anchorSection = '#c' . $this->record['uid'];
452
            $language = (int)$this->record[$GLOBALS['TCA']['tt_content']['ctrl']['languageField']];
453
            if ($language > 0) {
454
                $additionalParams = '&L=' . $language;
455
            }
456
        }
457
458
        try {
459
            return BackendUtility::getPreviewUrl(
460
                $this->getPreviewPid(),
461
                '',
462
                null,
463
                $anchorSection,
464
                '',
465
                $additionalParams
466
            );
467
        } catch (UnableToLinkToPageException $e) {
468
            return '';
469
        }
470
    }
471
472
    /**
473
     * Checks if the page is allowed to show info
474
     *
475
     * @return bool
476
     */
477
    protected function canShowInfo(): bool
478
    {
479
        return true;
480
    }
481
482
    /**
483
     * Checks if the page is allowed to show info
484
     *
485
     * @return bool
486
     */
487
    protected function canShowHistory(): bool
488
    {
489
        $userTsConfig = $this->backendUser->getTSConfig();
490
        return (bool)trim($userTsConfig['options.']['showHistory.'][$this->table] ?? $userTsConfig['options.']['showHistory'] ?? '1');
491
    }
492
493
    /**
494
     * Checks if the record can be previewed in frontend
495
     *
496
     * @return bool
497
     */
498
    protected function canBeViewed(): bool
499
    {
500
        return $this->table === 'tt_content' && $this->parentPageCanBeViewed();
501
    }
502
503
    /**
504
     * Whether a record can be edited
505
     *
506
     * @return bool
507
     */
508
    protected function canBeEdited(): bool
509
    {
510
        if (isset($GLOBALS['TCA'][$this->table]['ctrl']['readOnly']) && $GLOBALS['TCA'][$this->table]['ctrl']['readOnly']) {
511
            return false;
512
        }
513
        if ($this->backendUser->isAdmin()) {
514
            return true;
515
        }
516
        if (isset($GLOBALS['TCA'][$this->table]['ctrl']['adminOnly']) && $GLOBALS['TCA'][$this->table]['ctrl']['adminOnly']) {
517
            return false;
518
        }
519
520
        $access = !$this->isRecordLocked()
521
            && $this->backendUser->check('tables_modify', $this->table)
522
            && $this->hasPagePermission(Permission::CONTENT_EDIT)
523
            && $this->backendUser->recordEditAccessInternals($this->table, $this->record);
524
        return $access;
525
    }
526
527
    /**
528
     * Whether a record can be created
529
     *
530
     * @return bool
531
     */
532
    protected function canBeNew(): bool
533
    {
534
        return $this->canBeEdited() && !$this->isRecordATranslation();
535
    }
536
537
    /**
538
     * Checks if disableDelete flag is set in TSConfig for the current table
539
     *
540
     * @return bool
541
     */
542
    protected function isDeletionDisabledInTS(): bool
543
    {
544
        return (bool)\trim(
545
            $this->backendUser->getTSConfig()['options.']['disableDelete.'][$this->table]
546
            ?? $this->backendUser->getTSConfig()['options.']['disableDelete']
547
            ?? ''
548
        );
549
    }
550
551
    /**
552
     * Checks if the user has the right to delete the record
553
     *
554
     * @return bool
555
     */
556
    protected function canBeDeleted(): bool
557
    {
558
        return !$this->isDeletionDisabledInTS()
559
            && !$this->isRecordCurrentBackendUser()
560
            && $this->canBeEdited();
561
    }
562
563
    /**
564
     * Returns true if current record can be unhidden/enabled
565
     *
566
     * @return bool
567
     */
568
    protected function canBeEnabled(): bool
569
    {
570
        return $this->hasDisableColumnWithValue(1) && $this->canBeEdited();
571
    }
572
573
    /**
574
     * Returns true if current record can be hidden
575
     *
576
     * @return bool
577
     */
578
    protected function canBeDisabled(): bool
579
    {
580
        return $this->hasDisableColumnWithValue(0)
581
            && !$this->isRecordCurrentBackendUser()
582
            && $this->canBeEdited();
583
    }
584
585
    /**
586
     * Returns true new content element wizard can be shown
587
     *
588
     * @return bool
589
     */
590
    protected function canOpenNewCEWizard(): bool
591
    {
592
        return $this->table === 'tt_content'
593
            && (bool)(BackendUtility::getPagesTSconfig($this->record['pid'])['mod.']['web_layout.']['disableNewContentElementWizard'] ?? true)
594
            && $this->canBeEdited() && !$this->isRecordATranslation();
595
    }
596
597
    /**
598
     * @return bool
599
     */
600
    protected function canOpenListModule(): bool
601
    {
602
        return $this->backendUser->check('modules', 'web_list');
603
    }
604
605
    /**
606
     * @return bool
607
     */
608
    protected function canBeCopied(): bool
609
    {
610
        return !$this->isRecordInClipboard('copy')
611
            && !$this->isRecordATranslation();
612
    }
613
614
    /**
615
     * @return bool
616
     */
617
    protected function canBeCut(): bool
618
    {
619
        return !$this->isRecordInClipboard('cut')
620
            && $this->canBeEdited()
621
            && !$this->isRecordATranslation();
622
    }
623
624
    /**
625
     * Paste after is only shown for records from the same table (comparing record in clipboard and record clicked)
626
     *
627
     * @return bool
628
     */
629
    protected function canBePastedAfter(): bool
630
    {
631
        $clipboardElementCount = count($this->clipboard->elFromTable($this->table));
632
633
        return $clipboardElementCount
634
            && $this->backendUser->check('tables_modify', $this->table)
635
            && $this->hasPagePermission(Permission::CONTENT_EDIT);
636
    }
637
638
    /**
639
     * Checks if table have "disable" column (e.g. "hidden"), if user has access to this column
640
     * and if it contains given value
641
     *
642
     * @param int $value
643
     * @return bool
644
     */
645
    protected function hasDisableColumnWithValue(int $value): bool
646
    {
647
        if (isset($GLOBALS['TCA'][$this->table]['ctrl']['enablecolumns']['disabled'])) {
648
            $hiddenFieldName = $GLOBALS['TCA'][$this->table]['ctrl']['enablecolumns']['disabled'];
649
            if (
650
                $hiddenFieldName !== '' && !empty($GLOBALS['TCA'][$this->table]['columns'][$hiddenFieldName])
651
                && (
652
                    empty($GLOBALS['TCA'][$this->table]['columns'][$hiddenFieldName]['exclude'])
653
                    || $this->backendUser->check('non_exclude_fields', $this->table . ':' . $hiddenFieldName)
654
                )
655
            ) {
656
                return (int)($this->record[$hiddenFieldName] ?? 0) === (int)$value;
657
            }
658
        }
659
        return false;
660
    }
661
662
    /**
663
     * Record is locked if page is locked or page is not locked but record is
664
     *
665
     * @return bool
666
     */
667
    protected function isRecordLocked(): bool
668
    {
669
        return (int)$this->pageRecord['editlock'] === 1
670
            || isset($GLOBALS['TCA'][$this->table]['ctrl']['editlock'])
671
            && (int)$this->record[$GLOBALS['TCA'][$this->table]['ctrl']['editlock']] === 1;
672
    }
673
674
    /**
675
     * Returns true is a current record is a delete placeholder
676
     *
677
     * @return bool
678
     */
679
    protected function isDeletePlaceholder(): bool
680
    {
681
        if (!isset($this->record['t3ver_state'])) {
682
            return false;
683
        }
684
        return VersionState::cast($this->record['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER);
685
    }
686
687
    /**
688
     * Checks if current record is in the "normal" pad of the clipboard
689
     *
690
     * @param string $mode "copy", "cut" or '' for any mode
691
     * @return bool
692
     */
693
    protected function isRecordInClipboard(string $mode = ''): bool
694
    {
695
        $isSelected = '';
696
        if ($this->clipboard->current === 'normal' && isset($this->record['uid'])) {
697
            $isSelected = $this->clipboard->isSelected($this->table, $this->record['uid']);
698
        }
699
        return $mode === '' ? !empty($isSelected) : $isSelected === $mode;
700
    }
701
702
    /**
703
     * Returns true is a record ia a translation
704
     *
705
     * @return bool
706
     */
707
    protected function isRecordATranslation(): bool
708
    {
709
        return BackendUtility::isTableLocalizable($this->table) && (int)$this->record[$GLOBALS['TCA'][$this->table]['ctrl']['transOrigPointerField']] !== 0;
710
    }
711
712
    /**
713
     * Return true in case the current record is the current backend user
714
     *
715
     * @return bool
716
     */
717
    protected function isRecordCurrentBackendUser(): bool
718
    {
719
        return $this->table === 'be_users'
720
            && (int)($this->record['uid'] ?? 0) === (int)$this->backendUser->user[$this->backendUser->userid_column];
721
    }
722
723
    /**
724
     * Check whether the elements' parent page can be viewed
725
     *
726
     * @return bool
727
     */
728
    protected function parentPageCanBeViewed(): bool
729
    {
730
        if (!isset($this->pageRecord['uid']) || !($this->pageRecord['doktype'] ?? false)) {
731
            // In case parent page record is invalid, the element can not be viewed
732
            return false;
733
        }
734
735
        // Finally, we check whether the parent page has a "no view doktype" assigned
736
        return !in_array((int)$this->pageRecord['doktype'], [
737
            PageRepository::DOKTYPE_SPACER,
738
            PageRepository::DOKTYPE_SYSFOLDER,
739
            PageRepository::DOKTYPE_RECYCLER
740
        ], true);
741
    }
742
743
    /**
744
     * @return string
745
     */
746
    protected function getIdentifier(): string
747
    {
748
        return $this->record['uid'];
749
    }
750
}
751