Completed
Push — master ( e176d6...f9122f )
by
unknown
27:07 queued 09:58
created

getPageChildrenRecursive()   A

Complexity

Conditions 6
Paths 2

Size

Total Lines 26
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 19
nc 2
nop 4
dl 0
loc 26
rs 9.0111
c 0
b 0
f 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\Extbase\Configuration;
19
20
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
21
use TYPO3\CMS\Core\Database\ConnectionPool;
22
use TYPO3\CMS\Core\Database\Query\QueryHelper;
23
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
24
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
25
use TYPO3\CMS\Core\Type\Bitmask\Permission;
26
use TYPO3\CMS\Core\TypoScript\TemplateService;
27
use TYPO3\CMS\Core\Utility\ArrayUtility;
28
use TYPO3\CMS\Core\Utility\GeneralUtility;
29
use TYPO3\CMS\Core\Utility\RootlineUtility;
30
31
/**
32
 * A general purpose configuration manager used in backend mode.
33
 * @internal only to be used within Extbase, not part of TYPO3 Core API.
34
 */
35
class BackendConfigurationManager extends AbstractConfigurationManager
36
{
37
    /**
38
     * @var array
39
     */
40
    protected $typoScriptSetupCache = [];
41
42
    /**
43
     * stores the current page ID
44
     * @var int
45
     */
46
    protected $currentPageId;
47
48
    /**
49
     * Returns TypoScript Setup array from current Environment.
50
     *
51
     * @return array the raw TypoScript setup
52
     */
53
    public function getTypoScriptSetup(): array
54
    {
55
        $pageId = $this->getCurrentPageId();
56
57
        if (!array_key_exists($pageId, $this->typoScriptSetupCache)) {
58
            /** @var TemplateService $template */
59
            $template = GeneralUtility::makeInstance(TemplateService::class);
60
            // do not log time-performance information
61
            $template->tt_track = false;
62
            // Explicitly trigger processing of extension static files
63
            $template->setProcessExtensionStatics(true);
64
            // Get the root line
65
            $rootline = [];
66
            if ($pageId > 0) {
67
                try {
68
                    $rootline = GeneralUtility::makeInstance(RootlineUtility::class, $pageId)->get();
69
                } catch (\RuntimeException $e) {
70
                    $rootline = [];
71
                }
72
            }
73
            // This generates the constants/config + hierarchy info for the template.
74
            $template->runThroughTemplates($rootline, 0);
75
            $template->generateConfig();
76
            $this->typoScriptSetupCache[$pageId] = $template->setup;
77
        }
78
        return $this->typoScriptSetupCache[$pageId];
79
    }
80
81
    /**
82
     * Returns the TypoScript configuration found in module.tx_yourextension_yourmodule
83
     * merged with the global configuration of your extension from module.tx_yourextension
84
     *
85
     * @param string $extensionName
86
     * @param string $pluginName in BE mode this is actually the module signature. But we're using it just like the plugin name in FE
87
     * @return array
88
     */
89
    protected function getPluginConfiguration(string $extensionName, string $pluginName = null): array
90
    {
91
        $setup = $this->getTypoScriptSetup();
92
        $pluginConfiguration = [];
93
        if (is_array($setup['module.']['tx_' . strtolower($extensionName) . '.'] ?? false)) {
94
            $pluginConfiguration = $this->typoScriptService->convertTypoScriptArrayToPlainArray($setup['module.']['tx_' . strtolower($extensionName) . '.']);
95
        }
96
        if ($pluginName !== null) {
97
            $pluginSignature = strtolower($extensionName . '_' . $pluginName);
98
            if (is_array($setup['module.']['tx_' . $pluginSignature . '.'] ?? false)) {
99
                $overruleConfiguration = $this->typoScriptService->convertTypoScriptArrayToPlainArray($setup['module.']['tx_' . $pluginSignature . '.']);
100
                ArrayUtility::mergeRecursiveWithOverrule($pluginConfiguration, $overruleConfiguration);
101
            }
102
        }
103
        return $pluginConfiguration;
104
    }
105
106
    /**
107
     * Returns the configured controller/action configuration of the specified module in the format
108
     * array(
109
     * 'Controller1' => array('action1', 'action2'),
110
     * 'Controller2' => array('action3', 'action4')
111
     * )
112
     *
113
     * @param string $extensionName
114
     * @param string $pluginName in BE mode this is actually the module signature. But we're using it just like the plugin name in FE
115
     * @return array
116
     */
117
    protected function getControllerConfiguration(string $extensionName, string $pluginName): array
118
    {
119
        $controllerConfiguration = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['modules'][$pluginName]['controllers'] ?? false;
120
        if (!is_array($controllerConfiguration)) {
121
            $controllerConfiguration = [];
122
        }
123
        return $controllerConfiguration;
124
    }
125
126
    /**
127
     * Returns the page uid of the current page.
128
     * If no page is selected, we'll return the uid of the first root page.
129
     *
130
     * @return int current page id. If no page is selected current root page id is returned
131
     */
132
    protected function getCurrentPageId(): int
133
    {
134
        if ($this->currentPageId !== null) {
135
            return $this->currentPageId;
136
        }
137
138
        $this->currentPageId = $this->getCurrentPageIdFromGetPostData() ?: $this->getCurrentPageIdFromCurrentSiteRoot();
139
        $this->currentPageId = $this->currentPageId ?: $this->getCurrentPageIdFromRootTemplate();
140
        $this->currentPageId = $this->currentPageId ?: self::DEFAULT_BACKEND_STORAGE_PID;
141
142
        return $this->currentPageId;
143
    }
144
145
    /**
146
     * Gets the current page ID from the GET/POST data.
147
     *
148
     * @return int the page UID, will be 0 if none has been set
149
     */
150
    protected function getCurrentPageIdFromGetPostData(): int
151
    {
152
        return (int)GeneralUtility::_GP('id');
153
    }
154
155
    /**
156
     * Gets the current page ID from the first site root in tree.
157
     *
158
     * @return int the page UID, will be 0 if none has been set
159
     */
160
    protected function getCurrentPageIdFromCurrentSiteRoot(): int
161
    {
162
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
163
            ->getQueryBuilderForTable('pages');
164
165
        $queryBuilder
166
            ->getRestrictions()
167
            ->removeAll()
168
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
169
            ->add(GeneralUtility::makeInstance(HiddenRestriction::class));
170
171
        $rootPage = $queryBuilder
172
            ->select('uid')
173
            ->from('pages')
174
            ->where(
175
                $queryBuilder->expr()->eq('is_siteroot', $queryBuilder->createNamedParameter(1, \PDO::PARAM_INT)),
176
                $queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)),
177
                // Only consider live root page IDs, never return a versioned root page ID
178
                $queryBuilder->expr()->eq('t3ver_oid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)),
179
                $queryBuilder->expr()->eq('t3ver_wsid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT))
180
            )
181
            ->orderBy('sorting')
182
            ->execute()
183
            ->fetch();
184
185
        if (empty($rootPage)) {
186
            return 0;
187
        }
188
189
        return (int)$rootPage['uid'];
190
    }
191
192
    /**
193
     * Gets the current page ID from the first created root template.
194
     *
195
     * @return int the page UID, will be 0 if none has been set
196
     */
197
    protected function getCurrentPageIdFromRootTemplate(): int
198
    {
199
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
200
            ->getQueryBuilderForTable('sys_template');
201
202
        $queryBuilder
203
            ->getRestrictions()
204
            ->removeAll()
205
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
206
            ->add(GeneralUtility::makeInstance(HiddenRestriction::class));
207
208
        $rootTemplate = $queryBuilder
209
            ->select('pid')
210
            ->from('sys_template')
211
            ->where(
212
                $queryBuilder->expr()->eq('root', $queryBuilder->createNamedParameter(1, \PDO::PARAM_INT))
213
            )
214
            ->orderBy('crdate')
215
            ->execute()
216
            ->fetch();
217
218
        if (empty($rootTemplate)) {
219
            return 0;
220
        }
221
222
        return (int)$rootTemplate['pid'];
223
    }
224
225
    /**
226
     * Returns the default backend storage pid
227
     *
228
     * @return int
229
     */
230
    public function getDefaultBackendStoragePid(): int
231
    {
232
        // todo: fallback to parent::getDefaultBackendStoragePid() would make sense here.
233
        return $this->getCurrentPageId();
234
    }
235
236
    /**
237
     * We need to set some default request handler if the framework configuration
238
     * could not be loaded; to make sure Extbase also works in Backend modules
239
     * in all contexts.
240
     *
241
     * @param array $frameworkConfiguration
242
     * @return array
243
     */
244
    protected function getContextSpecificFrameworkConfiguration(array $frameworkConfiguration): array
245
    {
246
        return $frameworkConfiguration;
247
    }
248
249
    /**
250
     * Returns an array of storagePIDs that are below a list of storage pids.
251
     *
252
     * @param int[] $storagePids Storage PIDs to start at; multiple PIDs possible as comma-separated list
253
     * @param int $recursionDepth Maximum number of levels to search, 0 to disable recursive lookup
254
     * @return int[] Uid list including the start $storagePids
255
     */
256
    protected function getRecursiveStoragePids(array $storagePids, int $recursionDepth = 0): array
257
    {
258
        if ($recursionDepth <= 0) {
259
            return $storagePids;
260
        }
261
        $permsClause = QueryHelper::stripLogicalOperatorPrefix(
262
            $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW)
263
        );
264
        $recursiveStoragePids = [];
265
        foreach ($storagePids as $startPid) {
266
            $startPid = abs($startPid);
267
            $recursiveStoragePids = array_merge(
268
                $recursiveStoragePids,
269
                [ $startPid ],
270
                $this->getPageChildrenRecursive($startPid, $recursionDepth, 0, $permsClause)
0 ignored issues
show
Bug introduced by
It seems like $startPid can also be of type double; however, parameter $pid of TYPO3\CMS\Extbase\Config...PageChildrenRecursive() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

270
                $this->getPageChildrenRecursive(/** @scrutinizer ignore-type */ $startPid, $recursionDepth, 0, $permsClause)
Loading history...
271
            );
272
        }
273
        return array_unique($recursiveStoragePids);
274
    }
275
276
    /**
277
     * Recursively fetch all children of a given page
278
     *
279
     * @param int $pid uid of the page
280
     * @param int $depth
281
     * @param int $begin
282
     * @param string $permsClause
283
     * @return int[] List of child row $uid's
284
     */
285
    protected function getPageChildrenRecursive(int $pid, int $depth, int $begin, string $permsClause): array
286
    {
287
        $children = [];
288
        if ($pid && $depth > 0) {
289
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
290
            $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
291
            $statement = $queryBuilder->select('uid')
292
                ->from('pages')
293
                ->where(
294
                    $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pid, \PDO::PARAM_INT)),
295
                    $queryBuilder->expr()->eq('sys_language_uid', 0),
296
                    $permsClause
297
                )
298
                ->orderBy('uid')
299
                ->execute();
300
            while ($row = $statement->fetch()) {
301
                if ($begin <= 0) {
302
                    $children[] = (int)$row['uid'];
303
                }
304
                if ($depth > 1) {
305
                    $theSubList = $this->getPageChildrenRecursive((int)$row['uid'], $depth - 1, $begin - 1, $permsClause);
306
                    $children = array_merge($children, $theSubList);
307
                }
308
            }
309
        }
310
        return $children;
311
    }
312
313
    /**
314
     * @return BackendUserAuthentication
315
     */
316
    protected function getBackendUser(): BackendUserAuthentication
317
    {
318
        return $GLOBALS['BE_USER'];
319
    }
320
}
321