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

Page::initializeMountPointPages()   B

Complexity

Conditions 7
Paths 13

Size

Total Lines 61
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 7.6892

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 33
c 1
b 0
f 0
dl 0
loc 61
ccs 22
cts 29
cp 0.7586
rs 8.4586
cc 7
nc 13
nop 0
crap 7.6892

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Initializer;
17
18
use ApacheSolrForTypo3\Solr\IndexQueue\Item;
19
use ApacheSolrForTypo3\Solr\IndexQueue\Queue;
20
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
21
use Doctrine\DBAL\ConnectionException;
22
use Doctrine\DBAL\Driver\Exception as DBALDriverException;
23
use Doctrine\DBAL\Exception as DBALException;
24
use PDO;
25
use Throwable;
26
use TYPO3\CMS\Backend\Utility\BackendUtility;
27
use TYPO3\CMS\Core\Database\Connection;
28
use TYPO3\CMS\Core\Database\ConnectionPool;
29
use TYPO3\CMS\Core\Messaging\FlashMessage;
30
use TYPO3\CMS\Core\Utility\GeneralUtility;
31
32
/**
33
 * Index Queue initializer for pages which also covers resolution of mount
34
 * pages.
35
 *
36
 * @author Ingo Renner <[email protected]>
37
 */
38
class Page extends AbstractInitializer
39
{
40
    /**
41
     * The type of items this initializer is handling.
42
     * @var string
43
     */
44
    protected string $type = 'pages';
45
46
    /**
47
     * Overrides the general setType() implementation, forcing type to "pages".
48
     *
49
     * @param string $type Type to initialize (ignored).
50
     */
51 12
    public function setType(string $type)
52
    {
53 12
        $this->type = 'pages';
54
    }
55
56
    /**
57
     * Initializes Index Queue page items for a site. Includes regular pages
58
     * and mounted pages - no nested mount page structures though.
59
     *
60
     * @return bool TRUE if initialization was successful, FALSE on error.
61
     * @throws ConnectionException
62
     * @throws DBALDriverException
63
     * @throws DBALException|\Doctrine\DBAL\DBALException
64
     * @throws Throwable
65
     */
66 12
    public function initialize(): bool
67
    {
68 12
        $pagesInitialized = parent::initialize();
69 12
        $mountPagesInitialized = $this->initializeMountPointPages();
70
71 12
        return $pagesInitialized && $mountPagesInitialized;
72
    }
73
74
    /**
75
     * Initialize a single page that is part of a mounted tree.
76
     *
77
     * @param array $mountProperties Array of mount point properties mountPageSource, mountPageDestination, and mountPageOverlayed
78
     * @param int $mountedPageId The ID of the mounted page
79
     * @throws DBALDriverException
80
     * @throws DBALException|\Doctrine\DBAL\DBALException
81
     */
82 2
    public function initializeMountedPage(array $mountProperties, int $mountedPageId)
83
    {
84 2
        $mountedPages = [$mountedPageId];
85
86 2
        $this->addMountedPagesToIndexQueue($mountedPages, $mountProperties);
87 2
        $this->addIndexQueueItemIndexingProperties($mountProperties, $mountedPages);
88
    }
89
90
    /**
91
     * Initializes Mount Point(pages) to be indexed through the Index Queue. The Mount
92
     * Points are searched and their mounted virtual sub-trees are then resolved
93
     * and added to the Index Queue as if they were actually present below the
94
     * Mount Point.
95
     *
96
     * @return bool TRUE if initialization of the Mount Pages was successful, FALSE otherwise
97
     * @throws ConnectionException
98
     * @throws DBALDriverException
99
     * @throws DBALException|\Doctrine\DBAL\DBALException
100
     * @throws Throwable
101
     */
102 12
    protected function initializeMountPointPages(): bool
103
    {
104 12
        $mountPointsInitialized = false;
105 12
        $mountPoints = $this->pagesRepository->findAllMountPagesByWhereClause(
106 12
            $this->buildPagesClause()
107 12
            . $this->buildTcaWhereClause()
108
            . ' AND doktype = 7 AND no_search = 0'
109
        );
110
111 12
        if (empty($mountPoints)) {
112 10
            return true;
113
        }
114
115 6
        $databaseConnection = $this->queueItemRepository->getConnectionForAllInTransactionInvolvedTables(
116
            'tx_solr_indexqueue_item',
117
            'tx_solr_indexqueue_indexing_property'
118
        );
119
120 6
        foreach ($mountPoints as $mountPoint) {
121 6
            if (!$this->validateMountPoint($mountPoint)) {
122 3
                continue;
123
            }
124
125 6
            $mountedPages = $this->resolveMountPageTree($mountPoint);
126
127
            // handling mount_pid_ol behavior
128 6
            if (!$mountPoint['mountPageOverlayed']) {
129
                // Add page like a regular page, as only the sub-tree is mounted.
130
                // The page itself has its own content, which is handled like standard page.
131 3
                $indexQueue = GeneralUtility::makeInstance(Queue::class);
132 3
                $indexQueue->updateItem($this->type, $mountPoint['uid']);
133
            }
134
135
            // This can happen when the mount point does not show the content of the
136
            // mounted page and the mounted page does not have any subpages.
137 6
            if (empty($mountedPages)) {
138
                continue;
139
            }
140
141 6
            $databaseConnection->beginTransaction();
142
            try {
143 6
                $this->addMountedPagesToIndexQueue($mountedPages, $mountPoint);
144 6
                $this->addIndexQueueItemIndexingProperties($mountPoint, $mountedPages);
145
146 6
                $databaseConnection->commit();
147 6
                $mountPointsInitialized = true;
148
            } catch (Throwable $e) {
149
                $databaseConnection->rollBack();
150
151
                $this->logger->log(
152
                    SolrLogManager::ERROR,
153
                    'Index Queue initialization failed for mount pages',
154
                    [
155
                        $e->__toString(),
156
                    ]
157
                );
158
                break;
159
            }
160
        }
161
162 6
        return $mountPointsInitialized;
163
    }
164
165
    /**
166
     * Checks whether a Mount Point page is properly configured.
167
     *
168
     * @param array $mountPoint A mount page
169
     * @return bool TRUE if the Mount Page is OK, FALSE otherwise
170
     */
171 6
    protected function validateMountPoint(array $mountPoint): bool
172
    {
173 6
        $isValidMountPage = true;
174
175 6
        if (empty($mountPoint['mountPageSource'])) {
176 3
            $isValidMountPage = false;
177
178 3
            $flashMessage = GeneralUtility::makeInstance(
179
                FlashMessage::class,
180 3
                'Property "Mounted page" must not be empty. Invalid Mount Page configuration for page ID ' . $mountPoint['uid'] . '.',
181
                'Failed to initialize Mount Page tree. ',
182
                FlashMessage::ERROR
183
            );
184
            // @extensionScannerIgnoreLine
185 3
            $this->flashMessageQueue->addMessage($flashMessage);
186
        }
187
188 6
        if (!$this->mountedPageExists($mountPoint['mountPageSource'])) {
189 3
            $isValidMountPage = false;
190
191 3
            $flashMessage = GeneralUtility::makeInstance(
192
                FlashMessage::class,
193
                'The mounted page must be accessible in the frontend. '
194
                . 'Invalid Mount Page configuration for page ID '
195 3
                . $mountPoint['uid'] . ', the mounted page with ID '
196 3
                . $mountPoint['mountPageSource']
197
                . ' is not accessible in the frontend.',
198
                'Failed to initialize Mount Page tree. ',
199
                FlashMessage::ERROR
200
            );
201
            // @extensionScannerIgnoreLine
202 3
            $this->flashMessageQueue->addMessage($flashMessage);
203
        }
204
205 6
        return $isValidMountPage;
206
    }
207
208
    /**
209
     * Checks whether the mounted page (mount page source) exists. That is,
210
     * whether it is accessible in the frontend. So the record must exist
211
     * (deleted = 0) and must not be hidden (hidden = 0).
212
     *
213
     * @param int $mountedPageId Mounted page ID
214
     * @return bool TRUE if the page is accessible in the frontend, FALSE otherwise.
215
     */
216 6
    protected function mountedPageExists(int $mountedPageId): bool
217
    {
218 6
        $mountedPageExists = false;
219
220 6
        $mountedPage = BackendUtility::getRecord('pages', $mountedPageId, 'uid', ' AND hidden = 0');
221 6
        if (!empty($mountedPage)) {
222 6
            $mountedPageExists = true;
223
        }
224
225 6
        return $mountedPageExists;
226
    }
227
228
    /**
229
     * Adds the virtual / mounted pages to the Index Queue as if they would
230
     * belong to the same site where they are mounted.
231
     *
232
     * @param array $mountedPages An array of mounted page IDs
233
     * @param array $mountProperties Array with mount point properties (mountPageSource, mountPageDestination, mountPageOverlayed)
234
     * @throws DBALDriverException
235
     * @throws DBALException|\Doctrine\DBAL\DBALException
236
     */
237 8
    protected function addMountedPagesToIndexQueue(array $mountedPages, array $mountProperties)
238
    {
239 8
        $mountPointIdentifier = $this->getMountPointIdentifier($mountProperties);
240 8
        $mountPointPageIsWithExistingQueueEntry = $this->queueItemRepository->findPageIdsOfExistingMountPagesByMountIdentifier($mountPointIdentifier);
241 8
        $mountedPagesThatNeedToBeAdded = array_diff($mountedPages, $mountPointPageIsWithExistingQueueEntry);
242
243 8
        if (count($mountedPagesThatNeedToBeAdded) === 0) {
244
            //nothing to do
245
            return;
246
        }
247
248
        /* @var Connection $connection */
249 8
        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tx_solr_indexqueue_item');
250
251 8
        $mountIdentifier = $this->getMountPointIdentifier($mountProperties);
252 8
        $initializationQuery = 'INSERT INTO tx_solr_indexqueue_item (root, item_type, item_uid, indexing_configuration, indexing_priority, changed, has_indexing_properties, pages_mountidentifier, errors) '
253 8
            . $this->buildSelectStatement() . ', 1, ' . $connection->quote($mountIdentifier, PDO::PARAM_STR) . ',""'
254
            . 'FROM pages '
255
            . 'WHERE '
256 8
            . 'uid IN(' . implode(',', $mountedPagesThatNeedToBeAdded) . ') '
257 8
            . $this->buildTcaWhereClause()
258 8
            . $this->buildUserWhereClause();
259 8
        $logData = ['query' => $initializationQuery];
260
261
        try {
262 8
            $logData['rows'] = $this->queueItemRepository->initializeByNativeSQLStatement($initializationQuery);
263
        } catch (DBALException $DBALException) {
264
            $logData['error'] = $DBALException->getCode() . ': ' . $DBALException->getMessage();
265
        }
266
267 8
        $this->logInitialization($logData);
268
    }
269
270
    /**
271
     * Adds Index Queue item indexing properties for mounted pages. The page
272
     * indexer later needs to know that he's dealing with a mounted page, the
273
     * indexing properties will let make it possible for the indexer to
274
     * distinguish the mounted pages.
275
     *
276
     * @param array $mountPage An array with information about the root/destination Mount Page
277
     * @param array $mountedPages An array of mounted page IDs
278
     * @throws DBALDriverException
279
     * @throws DBALException|\Doctrine\DBAL\DBALException
280
     */
281 8
    protected function addIndexQueueItemIndexingProperties(array $mountPage, array $mountedPages)
282
    {
283 8
        $mountIdentifier = $this->getMountPointIdentifier($mountPage);
284 8
        $mountPageItems = $this->queueItemRepository->findAllIndexQueueItemsByRootPidAndMountIdentifierAndMountedPids($this->site->getRootPageId(), $mountIdentifier, $mountedPages);
0 ignored issues
show
Bug introduced by
The method getRootPageId() 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

284
        $mountPageItems = $this->queueItemRepository->findAllIndexQueueItemsByRootPidAndMountIdentifierAndMountedPids($this->site->/** @scrutinizer ignore-call */ getRootPageId(), $mountIdentifier, $mountedPages);

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...
285
286 8
        foreach ($mountPageItems as $mountPageItemRecord) {
287
            /* @var Item $mountPageItem */
288 8
            $mountPageItem = GeneralUtility::makeInstance(Item::class, /** @scrutinizer ignore-type */ $mountPageItemRecord);
289 8
            $mountPageItem->setIndexingProperty('mountPageSource', $mountPage['mountPageSource']);
290 8
            $mountPageItem->setIndexingProperty('mountPageDestination', $mountPage['mountPageDestination']);
291 8
            $mountPageItem->setIndexingProperty('isMountedPage', '1');
292
293 8
            $mountPageItem->storeIndexingProperties();
294
        }
295
    }
296
297
    /**
298
     * Builds an identifier of the given mount point properties.
299
     *
300
     * @param array $mountProperties Array with mount point properties (mountPageSource, mountPageDestination, mountPageOverlayed)
301
     * @return string String consisting of mountPageSource-mountPageDestination-mountPageOverlayed
302
     */
303 8
    protected function getMountPointIdentifier(array $mountProperties): string
304
    {
305 8
        return $mountProperties['mountPageSource']
306 8
        . '-' . $mountProperties['mountPageDestination']
307 8
        . '-' . $mountProperties['mountPageOverlayed'];
308
    }
309
310
    // Mount Page resolution
311
312
    /**
313
     * Gets all the pages from a mounted page tree.
314
     *
315
     * @param array $mountPage
316
     * @return array An array of page IDs in the mounted page tree
317
     * @throws DBALDriverException
318
     */
319 6
    protected function resolveMountPageTree(array $mountPage): array
320
    {
321 6
        $mountPageSourceId = (int)$mountPage['mountPageSource'];
322
323 6
        $mountPageTree = $this->site->getPages($mountPageSourceId, 'pages');
324
325
        // Do not include $mountPageSourceId in tree, if the mount point is not set to overlay.
326 6
        if (!empty($mountPageTree) && !$mountPage['mountPageOverlayed']) {
327 3
            $mountPageTree = array_diff($mountPageTree, [$mountPageSourceId]);
328
        }
329
330 6
        return $mountPageTree;
331
    }
332
}
333