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
16namespace TYPO3\CMS\Backend\Configuration\TypoScript\ConditionMatching;
17
18use TYPO3\CMS\Backend\Utility\BackendUtility;
19use TYPO3\CMS\Core\Configuration\TypoScript\ConditionMatching\AbstractConditionMatcher;
20use TYPO3\CMS\Core\Context\Context;
21use TYPO3\CMS\Core\Utility\GeneralUtility;
22
23/**
24 * Matching TypoScript conditions for backend disposal.
25 *
26 * Used with the TypoScript parser.
27 * Matches browserinfo, IP numbers for use with templates
28 */
29class ConditionMatcher extends AbstractConditionMatcher
30{
31    /**
32     * @var Context
33     */
34    protected $context;
35
36    public function __construct(Context $context = null, int $pageId = null, array $rootLine = null)
37    {
38        $this->context = $context ?? GeneralUtility::makeInstance(Context::class);
39        $this->pageId = $pageId ?? $this->determinePageId();
40        if ($rootLine === null) {
41            $rootLine = BackendUtility::BEgetRootLine($this->pageId, '', true);
42            ksort($rootLine);
43        }
44        $this->rootline = $rootLine;
45        $this->initializeExpressionLanguageResolver();
46    }
47
48    protected function updateExpressionLanguageVariables(): void
49    {
50        $treeLevel = $this->rootline ? count($this->rootline) - 1 : 0;
51        $tree = new \stdClass();
52        $tree->level = $treeLevel;
53        $tree->rootLine = $this->rootline;
54        $tree->rootLineIds = array_column($this->rootline, 'uid');
55        $tree->rootLineParentIds = array_slice(array_column($this->rootline, 'pid'), 2);
56
57        $backendUserAspect = $this->context->getAspect('backend.user');
58        $backend = new \stdClass();
59        $backend->user = new \stdClass();
60        $backend->user->isAdmin = $backendUserAspect->get('isAdmin');
61        $backend->user->isLoggedIn = $backendUserAspect->get('isLoggedIn');
62        $backend->user->userId = $backendUserAspect->get('id');
63        $backend->user->userGroupList = implode(',', $backendUserAspect->get('groupIds'));
64
65        $workspaceAspect = $this->context->getAspect('workspace');
66        $workspace = new \stdClass();
67        $workspace->workspaceId = $workspaceAspect->get('id');
68        $workspace->isLive = $workspaceAspect->get('isLive');
69        $workspace->isOffline = $workspaceAspect->get('isOffline');
70
71        $this->expressionLanguageResolverVariables = [
72            'tree' => $tree,
73            'backend' => $backend,
74            'workspace' => $workspace,
75            'page' => BackendUtility::getRecord('pages', $this->pageId ?? $this->determinePageId()) ?: [],
76        ];
77    }
78
79    /**
80     * Tries to determine the ID of the page currently processed.
81     * When User/Group TS-Config is parsed when no specific page is handled
82     * (i.e. in the Extension Manager, etc.) this function will return "0", so that
83     * the accordant conditions (e.g. PIDinRootline) will return "FALSE"
84     *
85     * @return int The determined page id or otherwise 0
86     */
87    private function determinePageId(): int
88    {
89        $pageId = 0;
90        $editStatement = GeneralUtility::_GP('edit');
91        $commandStatement = GeneralUtility::_GP('cmd');
92        // Determine id from module that was called with an id:
93        if ($id = (int)GeneralUtility::_GP('id')) {
94            $pageId = $id;
95        } elseif (is_array($editStatement)) {
96            $table = key($editStatement);
97            $uidAndAction = current($editStatement);
98            $uid = key($uidAndAction);
99            $action = current($uidAndAction);
100            if ($action === 'edit') {
101                $pageId = $this->getPageIdByRecord($table, $uid);
102            } elseif ($action === 'new') {
103                $pageId = $this->getPageIdByRecord($table, $uid, true);
104            }
105        } elseif (is_array($commandStatement)) {
106            $table = key($commandStatement);
107            $uidActionAndTarget = current($commandStatement);
108            $uid = (int)key($uidActionAndTarget);
109            $actionAndTarget = current($uidActionAndTarget);
110            $action = key($actionAndTarget);
111            $target = current($actionAndTarget);
112            if ($action === 'delete') {
113                $pageId = $this->getPageIdByRecord($table, $uid);
114            } elseif ($action === 'copy' || $action === 'move') {
115                $pageId = $this->getPageIdByRecord($table, (int)($target['target'] ?? $target), true);
116            }
117        }
118        return $pageId;
119    }
120
121    /**
122     * Gets the page id by a record.
123     *
124     * @param string $table Name of the table
125     * @param int $id Id of the accordant record
126     * @param bool $ignoreTable Whether to ignore the page, if TRUE a positive
127     * @return int Id of the page the record is persisted on
128     */
129    private function getPageIdByRecord($table, $id, $ignoreTable = false): int
130    {
131        $pageId = 0;
132        $id = (int)$id;
133        if ($table && $id) {
134            if (($ignoreTable || $table === 'pages') && $id >= 0) {
135                $pageId = $id;
136            } else {
137                $record = BackendUtility::getRecordWSOL($table, abs($id), '*', '', false);
138                $pageId = (int)$record['pid'];
139            }
140        }
141        return $pageId;
142    }
143}
144