Passed
Branch main (7dbc49)
by Rafael
04:08
created

PageIndexer::getIndexQueueItem()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 5
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
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 ApacheSolrForTypo3\Solr\IndexQueue\FrontendHelper;
17
18
use ApacheSolrForTypo3\Solr\Access\Rootline;
19
use ApacheSolrForTypo3\Solr\ConnectionManager;
20
use ApacheSolrForTypo3\Solr\IndexQueue\Item;
21
use ApacheSolrForTypo3\Solr\IndexQueue\Queue;
22
use ApacheSolrForTypo3\Solr\NoSolrConnectionFoundException;
23
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
24
use ApacheSolrForTypo3\Solr\System\Solr\SolrConnection;
25
use ApacheSolrForTypo3\Solr\Typo3PageIndexer;
26
use ApacheSolrForTypo3\Solr\Util;
27
use Doctrine\DBAL\Driver\Exception as DBALDriverException;
28
use Doctrine\DBAL\Exception as DBALException;
29
use Throwable;
30
use TYPO3\CMS\Core\Context\Exception\AspectNotFoundException;
31
use TYPO3\CMS\Core\SingletonInterface;
32
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
33
use TYPO3\CMS\Core\Utility\GeneralUtility;
34
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
35
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
36
use UnexpectedValueException;
37
38
/**
39
 * Index Queue Page Indexer frontend helper to ask the frontend page indexer to
40
 * index the page.
41
 *
42
 * @author Ingo Renner <[email protected]>
43
 */
44
class PageIndexer extends AbstractFrontendHelper implements SingletonInterface
45
{
46
47
    /**
48
     * This frontend helper's executed action.
49
     *
50
     * @var string
51
     */
52
    protected string $action = 'indexPage';
53
54
    /**
55
     * the page currently being indexed.
56
     *
57
     * @var TypoScriptFrontendController
58
     */
59
    protected TypoScriptFrontendController $page;
60
61
    /**
62
     * Response data
63
     *
64
     * @var array
65
     */
66
    protected array $responseData = [];
67
68
    /**
69
     * Activates a frontend helper by registering for hooks and other
70
     * resources required by the frontend helper to work.
71
     *
72
     * @noinspection PhpUnused
73
     */
74 10
    public function activate()
75
    {
76 10
        $pageIndexingHookRegistration = PageIndexer::class;
77
78 10
        $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-all'][__CLASS__] = $pageIndexingHookRegistration . '->hook_indexContent';
79
80
        // indexes fields defined in plugin.tx_solr.index.queue.pages.fields
81 10
        $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['Indexer']['indexPageSubstitutePageDocument'][PageFieldMappingIndexer::class] = PageFieldMappingIndexer::class;
82
83 10
        $this->registerAuthorizationService();
84
    }
85
86
    /**
87
     * Returns the status of whether a page was indexed.
88
     *
89
     * @return array Page indexed status.
90
     * @noinspection PhpUnused
91
     */
92
    public function getData(): array
93
    {
94
        return $this->responseData;
95
    }
96
97
    //
98
    // Indexer authorisation for access restricted pages / content
99
    //
100
101
    /**
102
     * Fakes a logged in user to retrieve access restricted content.
103
     *
104
     * @noinspection PhpUnused
105
     */
106
    public function authorizeFrontendUser()
107
    {
108
        $accessRootline = $this->getAccessRootline();
109
        $stringAccessRootline = (string)$accessRootline;
110
111
        if (empty($stringAccessRootline)) {
112
            return;
113
        }
114
115
        if (!is_array($GLOBALS['TSFE']->fe_user->user)) {
116
            $GLOBALS['TSFE']->fe_user->user = [];
117
        }
118
119
        $groups = $accessRootline->getGroups();
120
        $groupList = implode(',', $groups);
121
122
        $GLOBALS['TSFE']->fe_user->user['username'] = AuthorizationService::SOLR_INDEXER_USERNAME;
123
        $GLOBALS['TSFE']->fe_user->user['usergroup'] = $groupList;
124
125
        $this->responseData['authorization'] = [
126
            'username' => $GLOBALS['TSFE']->fe_user->user['username'],
127
            'usergroups' => $GLOBALS['TSFE']->fe_user->user['usergroup'],
128
        ];
129
    }
130
131
    /**
132
     * Gets the access rootline as defined by the request.
133
     *
134
     * @return Rootline The access rootline to use for indexing.
135
     */
136 10
    protected function getAccessRootline(): Rootline
137
    {
138 10
        $stringAccessRootline = '';
139
140 10
        if ($this->request->getParameter('accessRootline')) {
0 ignored issues
show
Bug introduced by
The method getParameter() 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

140
        if ($this->request->/** @scrutinizer ignore-call */ getParameter('accessRootline')) {

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...
141
            $stringAccessRootline = $this->request->getParameter('accessRootline');
142
        }
143
144 10
        return GeneralUtility::makeInstance(Rootline::class, /** @scrutinizer ignore-type */ $stringAccessRootline);
145
    }
146
147
    /**
148
     * Registers an authentication service to authorize / grant the indexer to
149
     * access protected pages.
150
     */
151 10
    protected function registerAuthorizationService()
152
    {
153 10
        $overrulingPriority = $this->getHighestAuthenticationServicePriority() + 1;
154
155 10
        ExtensionManagementUtility::addService(
156
            'solr', // extension key
157
            'auth', // service type
158
            AuthorizationService::class,
159
            // service key
160
            [// service meta data
161 10
                'title' => 'Solr Indexer Authorization',
162
                'description' => 'Authorizes the Solr Index Queue indexer to access protected pages.',
163
164
                'subtype' => 'getUserFE,authUserFE,getGroupsFE',
165
166
                'available' => true,
167
                'priority' => $overrulingPriority,
168
                'quality' => 100,
169
170
                'os' => '',
171
                'exec' => '',
172
173 10
                'classFile' => ExtensionManagementUtility::extPath('solr') . 'Classes/IndexQueue/FrontendHelper/AuthorizationService.php',
174
                'className' => AuthorizationService::class,
175
            ]
176
        );
177
    }
178
179
    /**
180
     * Determines the highest priority of all registered authentication
181
     * services.
182
     *
183
     * @return int Highest priority of all registered authentication service
184
     */
185 10
    protected function getHighestAuthenticationServicePriority(): int
186
    {
187 10
        $highestPriority = 0;
188
189 10
        if (is_array($GLOBALS['T3_SERVICES']['auth'] ?? null)) {
190 10
            foreach ($GLOBALS['T3_SERVICES']['auth'] as $service) {
191 10
                if ($service['priority'] > $highestPriority) {
192 10
                    $highestPriority = $service['priority'];
193
                }
194
            }
195
        }
196
197 10
        return $highestPriority;
198
    }
199
200
    //
201
    // Indexing
202
    //
203
204
    /**
205
     * Generates the current page's URL.
206
     *
207
     * Uses the provided GET parameters, page id and language id.
208
     *
209
     * @return string URL of the current page.
210
     */
211 10
    protected function generatePageUrl(): string
212
    {
213 10
        if ($this->request->getParameter('overridePageUrl')) {
214
            return $this->request->getParameter('overridePageUrl');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->request->g...eter('overridePageUrl') could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
215
        }
216
217
        /** @var $contentObject ContentObjectRenderer */
218 10
        $contentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
219
220 10
        $typolinkConfiguration = [
221 10
            'parameter' => (int)($this->page->id),
222
            'linkAccessRestrictedPages' => '1',
223
        ];
224
225 10
        $language = GeneralUtility::_GET('L');
226 10
        if (!empty($language)) {
227
            $typolinkConfiguration['additionalParams'] = '&L=' . $language;
228
        }
229
230 10
        $url = $contentObject->typoLink_URL($typolinkConfiguration);
231
232
        // clean up
233 10
        if ($url == '') {
234
            $url = '/';
235
        }
236
237 10
        return $url;
238
    }
239
240
    /**
241
     * Handles the indexing of the page content during post-processing of a
242
     * generated page.
243
     *
244
     * @param array $params unused
245
     * @param TypoScriptFrontendController $page TypoScript frontend
246
     * @noinspection PhpUnused
247
     * @noinspection PhpUnusedParameterInspection
248
     */
249 10
    public function hook_indexContent(array $params, TypoScriptFrontendController $page)
250
    {
251 10
        $this->logger = GeneralUtility::makeInstance(SolrLogManager::class, /** @scrutinizer ignore-type */ __CLASS__);
252
253 10
        $this->page = $page;
254 10
        $configuration = Util::getSolrConfiguration();
255
256 10
        $logPageIndexed = $configuration->getLoggingIndexingPageIndexed();
257 10
        if (!$this->page->config['config']['index_enable']) {
258
            if ($logPageIndexed) {
259
                $this->logger->log(
0 ignored issues
show
Bug introduced by
The method log() 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

259
                $this->logger->/** @scrutinizer ignore-call */ 
260
                               log(

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...
260
                    SolrLogManager::ERROR,
261
                    'Indexing is disabled. Set config.index_enable = 1 .'
262
                );
263
            }
264
            return;
265
        }
266
267
        try {
268 10
            $indexQueueItem = $this->getIndexQueueItem();
269 10
            if (is_null($indexQueueItem)) {
270
                throw new UnexpectedValueException('Can not get index queue item', 1482162337);
271
            }
272
273 10
            $solrConnection = $this->getSolrConnection($indexQueueItem);
274
275
            /** @var $indexer Typo3PageIndexer */
276 10
            $indexer = GeneralUtility::makeInstance(Typo3PageIndexer::class, /** @scrutinizer ignore-type */ $page);
277 10
            $indexer->setSolrConnection($solrConnection);
278 10
            $indexer->setPageAccessRootline($this->getAccessRootline());
279 10
            $indexer->setPageUrl($this->generatePageUrl());
280 10
            $indexer->setMountPointParameter($GLOBALS['TSFE']->MP);
281 10
            $indexer->setIndexQueueItem($indexQueueItem);
282
283 10
            $this->responseData['pageIndexed'] = (int)$indexer->indexPage();
284 10
            $this->responseData['originalPageDocument'] = (array)$indexer->getPageSolrDocument();
285 10
            $this->responseData['solrConnection'] = [
286 10
                'rootPage' => $indexQueueItem->getRootPageUid(),
287 10
                'sys_language_uid' => Util::getLanguageUid(),
288 10
                'solr' => (string)$solrConnection->getNode('write'),
289
            ];
290
291 10
            $documentsSentToSolr = $indexer->getDocumentsSentToSolr();
292 10
            foreach ($documentsSentToSolr as $document) {
293 10
                $this->responseData['documentsSentToSolr'][] = (array)$document;
294
            }
295
        } catch (Throwable $e) {
296
            if ($configuration->getLoggingExceptions()) {
297
                $this->logger->log(
298
                    SolrLogManager::ERROR,
299
                    'Exception while trying to index page ' . $page->id,
300
                    [
301
                        $e->__toString(),
302
                    ]
303
                );
304
            }
305
        }
306
307 10
        if ($logPageIndexed) {
308
            $success = $this->responseData['pageIndexed'] ? 'Success' : 'Failed';
309
            $severity = $this->responseData['pageIndexed'] ? SolrLogManager::NOTICE : SolrLogManager::ERROR;
310
311
            $this->logger->log(
312
                $severity,
313
                'Page indexed: ' . $success,
314
                $this->responseData
315
            );
316
        }
317
    }
318
319
    /**
320
     * Gets the solr connection to use for indexing the page based on the
321
     * Index Queue item's properties.
322
     *
323
     * @param Item $indexQueueItem
324
     * @return SolrConnection Solr server connection
325
     * @throws AspectNotFoundException
326
     * @throws NoSolrConnectionFoundException
327
     * @throws DBALDriverException
328
     */
329 10
    protected function getSolrConnection(Item $indexQueueItem): SolrConnection
330
    {
331
        /** @var $connectionManager ConnectionManager */
332 10
        $connectionManager = GeneralUtility::makeInstance(ConnectionManager::class);
333
334 10
        return $connectionManager->getConnectionByRootPageId(
335 10
            $indexQueueItem->getRootPageUid(),
0 ignored issues
show
Bug introduced by
It seems like $indexQueueItem->getRootPageUid() can also be of type null; however, parameter $pageId of ApacheSolrForTypo3\Solr\...onnectionByRootPageId() 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

335
            /** @scrutinizer ignore-type */ $indexQueueItem->getRootPageUid(),
Loading history...
336 10
            Util::getLanguageUid()
337
        );
338
    }
339
340
    /**
341
     * This method retrieves the item from the index queue, that is indexed in this request.
342
     *
343
     * @return Item|null
344
     * @throws DBALDriverException
345
     * @throws DBALException|\Doctrine\DBAL\DBALException
346
     */
347 10
    protected function getIndexQueueItem(): ?Item
348
    {
349
        /** @var $indexQueue Queue */
350 10
        $indexQueue = GeneralUtility::makeInstance(Queue::class);
351 10
        return $indexQueue->getItem($this->request->getParameter('item'));
0 ignored issues
show
Bug introduced by
It seems like $this->request->getParameter('item') can also be of type null; however, parameter $itemId of ApacheSolrForTypo3\Solr\...xQueue\Queue::getItem() 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

351
        return $indexQueue->getItem(/** @scrutinizer ignore-type */ $this->request->getParameter('item'));
Loading history...
352
    }
353
}
354