1<?php
2
3namespace TYPO3\CMS\Frontend\Configuration\TypoScript\ConditionMatching;
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
18use Psr\Http\Message\ServerRequestInterface;
19use TYPO3\CMS\Core\Configuration\TypoScript\ConditionMatching\AbstractConditionMatcher;
20use TYPO3\CMS\Core\Context\Context;
21use TYPO3\CMS\Core\Context\UserAspect;
22use TYPO3\CMS\Core\Site\Entity\Site;
23use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
24use TYPO3\CMS\Core\Utility\GeneralUtility;
25
26/**
27 * Matching TypoScript conditions for frontend disposal.
28 *
29 * Used with the TypoScript parser. Matches browserinfo
30 * and IP numbers for use with templates.
31 */
32class ConditionMatcher extends AbstractConditionMatcher
33{
34    /**
35     * @var Context
36     */
37    protected $context;
38
39    /**
40     * @param Context $context optional context to fetch data from
41     */
42    public function __construct(Context $context = null)
43    {
44        $this->context = $context ?? GeneralUtility::makeInstance(Context::class);
45        $this->rootline = $this->determineRootline();
46        $this->initializeExpressionLanguageResolver();
47    }
48
49    protected function updateExpressionLanguageVariables(): void
50    {
51        $tree = new \stdClass();
52        $tree->level = $this->rootline ? count($this->rootline) - 1 : 0;
53        $tree->rootLine = $this->rootline;
54        $tree->rootLineIds = array_column($this->rootline, 'uid');
55
56        $frontendUserAspect = $this->context->getAspect('frontend.user');
57        $frontend = new \stdClass();
58        $frontend->user = new \stdClass();
59        $frontend->user->isLoggedIn = $frontendUserAspect->get('isLoggedIn');
60        $frontend->user->userId = $frontendUserAspect->get('id');
61        $frontend->user->userGroupList = implode(',', $frontendUserAspect->get('groupIds'));
62
63        $backendUserAspect = $this->context->getAspect('backend.user');
64        $backend = new \stdClass();
65        $backend->user = new \stdClass();
66        $backend->user->isAdmin = $backendUserAspect->get('isAdmin');
67        $backend->user->isLoggedIn = $backendUserAspect->get('isLoggedIn');
68        $backend->user->userId = $backendUserAspect->get('id');
69        $backend->user->userGroupList = implode(',', $backendUserAspect->get('groupIds'));
70
71        $workspaceAspect = $this->context->getAspect('workspace');
72        $workspace = new \stdClass();
73        $workspace->workspaceId = $workspaceAspect->get('id');
74        $workspace->isLive = $workspaceAspect->get('isLive');
75        $workspace->isOffline = $workspaceAspect->get('isOffline');
76
77        $this->expressionLanguageResolverVariables = [
78            'tree' => $tree,
79            'frontend' => $frontend,
80            'backend' => $backend,
81            'workspace' => $workspace,
82            'page' => $this->getPage(),
83        ];
84    }
85
86    /**
87     * Evaluates a TypoScript condition given as input,
88     * eg. "[browser=net][...(other conditions)...]"
89     *
90     * @param string $string The condition to match against its criteria.
91     * @return bool Whether the condition matched
92     * @see \TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser::parse()
93     * @throws \TYPO3\CMS\Core\Configuration\TypoScript\Exception\InvalidTypoScriptConditionException
94     * @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0.
95     */
96    protected function evaluateCondition($string)
97    {
98        if ($this->strictSyntaxEnabled()) {
99            trigger_error('The old condition syntax will be removed in TYPO3 v10.0, use the new expression language. Used condition: [' . $string . '].', E_USER_DEPRECATED);
100        }
101
102        list($key, $value) = GeneralUtility::trimExplode('=', $string, false, 2);
103        $result = $this->evaluateConditionCommon($key, $value);
104        if (is_bool($result)) {
105            return $result;
106        }
107
108        switch ($key) {
109            case 'usergroup':
110                $groupList = $this->getGroupList();
111                // '0,-1' is the default usergroups when not logged in!
112                if ($groupList !== '0,-1') {
113                    $values = GeneralUtility::trimExplode(',', $value, true);
114                    foreach ($values as $test) {
115                        if ($test === '*' || GeneralUtility::inList($groupList, $test)) {
116                            return true;
117                        }
118                    }
119                }
120                break;
121            case 'treeLevel':
122                $values = GeneralUtility::trimExplode(',', $value, true);
123                $treeLevel = count($this->rootline) - 1;
124                foreach ($values as $test) {
125                    if ($test == $treeLevel) {
126                        return true;
127                    }
128                }
129                break;
130            case 'PIDupinRootline':
131            case 'PIDinRootline':
132                $values = GeneralUtility::trimExplode(',', $value, true);
133                if ($key === 'PIDinRootline' || !in_array($this->pageId, $values)) {
134                    foreach ($values as $test) {
135                        foreach ($this->rootline as $rlDat) {
136                            if ($rlDat['uid'] == $test) {
137                                return true;
138                            }
139                        }
140                    }
141                }
142                break;
143            case 'site':
144                $site = $this->getCurrentSite();
145                if ($site instanceof Site) {
146                    $values = GeneralUtility::trimExplode(',', $value, true);
147                    foreach ($values as $test) {
148                        $point = strcspn($test, '=');
149                        $testValue = substr($test, $point + 1);
150                        $testValue = trim($testValue);
151                        $theVarName = trim(substr($test, 0, $point));
152                        $methodName = 'get' . ucfirst($theVarName);
153                        if (method_exists($site, $methodName)) {
154                            $sitePropertyValue = call_user_func([$site, $methodName]);
155                            // loose check on purpose in order to check for integer values
156                            if ($testValue == $sitePropertyValue) {
157                                return true;
158                            }
159                        }
160                    }
161                }
162                break;
163            case 'siteLanguage':
164                $siteLanguage = $this->getCurrentSiteLanguage();
165                if ($siteLanguage instanceof SiteLanguage) {
166                    $values = GeneralUtility::trimExplode(',', $value, true);
167                    foreach ($values as $test) {
168                        $point = strcspn($test, '=');
169                        $testValue = substr($test, $point + 1);
170                        $testValue = trim($testValue);
171                        $theVarName = trim(substr($test, 0, $point));
172                        $methodName = 'get' . ucfirst($theVarName);
173                        if (method_exists($siteLanguage, $methodName)) {
174                            $languagePropertyValue = call_user_func([$siteLanguage, $methodName]);
175                            // loose check on purpose in order to check for integer values
176                            if ($testValue == $languagePropertyValue) {
177                                return true;
178                            }
179                        }
180                    }
181                }
182                break;
183            default:
184                $conditionResult = $this->evaluateCustomDefinedCondition($string);
185                if ($conditionResult !== null) {
186                    return $conditionResult;
187                }
188        }
189
190        return false;
191    }
192
193    /**
194     * Returns GP / ENV / TSFE / session vars
195     *
196     * @example GP:L
197     * @example TSFE:fe_user|sesData|foo|bar
198     * @example TSFE:id
199     * @example ENV:HTTP_HOST
200     *
201     * @param string $var Identifier
202     * @return mixed|null The value of the variable pointed to or NULL if variable did not exist
203     * @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0.
204     */
205    protected function getVariable($var)
206    {
207        $vars = explode(':', $var, 2);
208        $val = $this->getVariableCommon($vars);
209        if ($val === null) {
210            $splitAgain = explode('|', $vars[1], 2);
211            $k = trim($splitAgain[0]);
212            if ($k) {
213                switch ((string)trim($vars[0])) {
214                    case 'TSFE':
215                        if (strpos($vars[1], 'fe_user|sesData|') === 0) {
216                            trigger_error(
217                                'Condition on TSFE|fe_user|sesData is deprecated and will be removed in TYPO3 v10.0.',
218                                E_USER_DEPRECATED
219                            );
220                            $val = $this->getSessionVariable(substr($vars[1], 16));
221                        } else {
222                            $val = $this->getGlobal('TSFE|' . $vars[1]);
223                        }
224                        break;
225                    case 'session':
226                        $val = $this->getSessionVariable($vars[1]);
227                        break;
228                    default:
229                }
230            }
231        }
232        return $val;
233    }
234
235    /**
236     * Return variable from current frontend user session
237     *
238     * @param string $var Session key
239     * @return mixed|null The value of the variable pointed to or NULL if variable did not exist
240     * @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0.
241     */
242    protected function getSessionVariable(string $var)
243    {
244        $retVal = null;
245        $keyParts = explode('|', $var);
246        $sessionKey = array_shift($keyParts);
247        $tsfe = $this->getTypoScriptFrontendController();
248        if ($tsfe && is_object($tsfe->fe_user)) {
249            $retVal = $tsfe->fe_user->getSessionData($sessionKey);
250            foreach ($keyParts as $keyPart) {
251                if (is_object($retVal)) {
252                    $retVal = $retVal->{$keyPart};
253                } elseif (is_array($retVal)) {
254                    $retVal = $retVal[$keyPart];
255                } else {
256                    break;
257                }
258            }
259        }
260        return $retVal;
261    }
262
263    /**
264     * Get the usergroup list of the current user.
265     *
266     * @return string The usergroup list of the current user
267     * @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0.
268     */
269    protected function getGroupList(): string
270    {
271        /** @var UserAspect $userAspect */
272        $userAspect = $this->context->getAspect('frontend.user');
273        return implode(',', $userAspect->getGroupIds());
274    }
275
276    /**
277     * Determines the current page Id.
278     *
279     * @return int The current page Id
280     * @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0.
281     */
282    protected function determinePageId()
283    {
284        return (int)($this->getTypoScriptFrontendController()->id ?? 0);
285    }
286
287    /**
288     * Gets the properties for the current page.
289     *
290     * @return array The properties for the current page.
291     * @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0.
292     */
293    protected function getPage()
294    {
295        return is_array($this->getTypoScriptFrontendController()->page)
296            ? $this->getTypoScriptFrontendController()->page
297            : [];
298    }
299
300    /**
301     * Determines the rootline for the current page.
302     *
303     * @return array The rootline for the current page.
304     * @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0.
305     */
306    protected function determineRootline()
307    {
308        return (array)$this->getTypoScriptFrontendController()->tmpl->rootLine;
309    }
310
311    /**
312     * Get the id of the current user.
313     *
314     * @return int The id of the current user
315     * @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0.
316     */
317    protected function getUserId(): int
318    {
319        $userAspect = $this->context->getAspect('frontend.user');
320        return $userAspect->get('id');
321    }
322
323    /**
324     * Determines if a user is logged in.
325     *
326     * @return bool Determines if a user is logged in
327     * @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0.
328     */
329    protected function isUserLoggedIn(): bool
330    {
331        /** @var UserAspect $userAspect */
332        $userAspect = $this->context->getAspect('frontend.user');
333        return $userAspect->isLoggedIn();
334    }
335
336    /**
337     * @return \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
338     * @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0.
339     */
340    protected function getTypoScriptFrontendController()
341    {
342        return $GLOBALS['TSFE'];
343    }
344
345    /**
346     * Returns the currently configured "site language" if a site is configured (= resolved) in the current request.
347     *
348     * @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0.
349     */
350    protected function getCurrentSiteLanguage(): ?SiteLanguage
351    {
352        if ($GLOBALS['TYPO3_REQUEST'] instanceof ServerRequestInterface
353            && $GLOBALS['TYPO3_REQUEST']->getAttribute('language') instanceof SiteLanguage) {
354            return $GLOBALS['TYPO3_REQUEST']->getAttribute('language');
355        }
356        return null;
357    }
358
359    /**
360     * Returns the currently configured site if a site is configured (= resolved) in the current request.
361     *
362     * @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0.
363     */
364    protected function getCurrentSite(): ?Site
365    {
366        if ($GLOBALS['TYPO3_REQUEST'] instanceof ServerRequestInterface
367            && $GLOBALS['TYPO3_REQUEST']->getAttribute('site') instanceof Site) {
368            return $GLOBALS['TYPO3_REQUEST']->getAttribute('site');
369        }
370        return null;
371    }
372}
373