Issues (202)

Classes/IndexQueue/Initializer/Page.php (1 issue)

Severity
1
<?php
2
namespace ApacheSolrForTypo3\Solr\IndexQueue\Initializer;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2011-2015 Ingo Renner <[email protected]>
8
 *  All rights reserved
9
 *
10
 *  This script is part of the TYPO3 project. The TYPO3 project is
11
 *  free software; you can redistribute it and/or modify
12
 *  it under the terms of the GNU General Public License as published by
13
 *  the Free Software Foundation; either version 3 of the License, or
14
 *  (at your option) any later version.
15
 *
16
 *  The GNU General Public License can be found at
17
 *  http://www.gnu.org/copyleft/gpl.html.
18
 *  A copy is found in the textfile GPL.txt and important notices to the license
19
 *  from the author is found in LICENSE.txt distributed with these scripts.
20
 *
21
 *
22
 *  This script is distributed in the hope that it will be useful,
23
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
24
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25
 *  GNU General Public License for more details.
26
 *
27
 *  This copyright notice MUST APPEAR in all copies of the script!
28
 ***************************************************************/
29
30
use ApacheSolrForTypo3\Solr\Domain\Index\Queue\QueueItemRepository;
31
use ApacheSolrForTypo3\Solr\Domain\Site\SiteRepository;
32
use ApacheSolrForTypo3\Solr\IndexQueue\Item;
33
use ApacheSolrForTypo3\Solr\IndexQueue\Queue;
34
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
35
use ApacheSolrForTypo3\Solr\System\Records\Pages\PagesRepository;
36
use Doctrine\DBAL\DBALException;
37
use TYPO3\CMS\Backend\Utility\BackendUtility;
38
use TYPO3\CMS\Core\Database\Connection;
39
use TYPO3\CMS\Core\Database\ConnectionPool;
40
use TYPO3\CMS\Core\Messaging\FlashMessage;
41
use TYPO3\CMS\Core\Utility\GeneralUtility;
42
43
/**
44
 * Index Queue initializer for pages which also covers resolution of mount
45
 * pages.
46
 *
47
 * @author Ingo Renner <[email protected]>
48
 */
49
class Page extends AbstractInitializer
50
{
51
    /**
52
     * The type of items this initializer is handling.
53
     * @var string
54
     */
55
    protected $type = 'pages';
56
57
    /**
58
     * @var PagesRepository
59
     */
60
    protected $pagesRepository;
61
62
    /**
63
     * Constructor, sets type and indexingConfigurationName to "pages".
64
     *
65
     * @param QueueItemRepository|null $queueItemRepository
66
     * @param PagesRepository|null $pagesRepository
67
     */
68 11
    public function __construct(QueueItemRepository $queueItemRepository = null, PagesRepository $pagesRepository = null)
69
    {
70 11
        parent::__construct($queueItemRepository);
71 11
        $this->pagesRepository = $pagesRepository ?? GeneralUtility::makeInstance(PagesRepository::class);
72 11
    }
73
74
    /**
75
     * Overrides the general setType() implementation, forcing type to "pages".
76
     *
77
     * @param string $type Type to initialize (ignored).
78
     */
79 10
    public function setType($type)
80
    {
81 10
        $this->type = 'pages';
82 10
    }
83
84
    /**
85
     * Initializes Index Queue page items for a site. Includes regular pages
86
     * and mounted pages - no nested mount page structures though.
87
     *
88
     * @return bool TRUE if initialization was successful, FALSE on error.
89
     */
90 10
    public function initialize()
91
    {
92 10
        $pagesInitialized = parent::initialize();
93 10
        $mountPagesInitialized = $this->initializeMountPages();
94
95 10
        return ($pagesInitialized && $mountPagesInitialized);
96
    }
97
98
    /**
99
     * Initialize a single page that is part of a mounted tree.
100
     *
101
     * @param array $mountProperties Array of mount point properties mountPageSource, mountPageDestination, and mountPageOverlayed
102
     * @param int $mountPageId The ID of the mounted page
103
     */
104 1
    public function initializeMountedPage(array $mountProperties, $mountPageId)
105
    {
106 1
        $mountedPages = [$mountPageId];
107
108 1
        $this->addMountedPagesToIndexQueue($mountedPages, $mountProperties);
109 1
        $this->addIndexQueueItemIndexingProperties($mountProperties, $mountedPages);
110 1
    }
111
112
    /**
113
     * Initializes Mount Pages to be indexed through the Index Queue. The Mount
114
     * Pages are searched and their mounted virtual sub-trees are then resolved
115
     * and added to the Index Queue as if they were actually present below the
116
     * Mount Page.
117
     *
118
     * @return bool TRUE if initialization of the Mount Pages was successful, FALSE otherwise
119
     */
120 10
    protected function initializeMountPages()
121
    {
122 10
        $mountPagesInitialized = false;
123 10
        $mountPages = $this->pagesRepository->findAllMountPagesByWhereClause($this->buildPagesClause() . $this->buildTcaWhereClause() . ' AND doktype = 7 AND no_search = 0');
124
125 10
        if (empty($mountPages)) {
126 4
            $mountPagesInitialized = true;
127 4
            return $mountPagesInitialized;
128
        }
129
130 6
        $databaseConnection = $this->queueItemRepository->getConnectionForAllInTransactionInvolvedTables(
131 6
            'tx_solr_indexqueue_item',
132 6
            'tx_solr_indexqueue_indexing_property'
133
        );
134
135 6
        foreach ($mountPages as $mountPage) {
136 6
            if (!$this->validateMountPage($mountPage)) {
137 3
                continue;
138
            }
139
140 6
            $mountedPages = $this->resolveMountPageTree($mountPage);
141
142
            // handling mount_pid_ol behavior
143 6
            if ($mountPage['mountPageOverlayed']) {
144
                // the page shows the mounted page's content
145 3
                $mountedPages[] = $mountPage['mountPageSource'];
146
            } else {
147
                // Add page like a regular page, as only the sub tree is
148
                // mounted. The page itself has its own content.
149 3
                $indexQueue = GeneralUtility::makeInstance(Queue::class);
150 3
                $indexQueue->updateItem($this->type, $mountPage['uid']);
151
            }
152
153
            // This can happen when the mount point does not show the content of the
154
            // mounted page and the mounted page does not have any subpages.
155 6
            if (empty($mountedPages)) {
156
                continue;
157
            }
158
159 6
            $databaseConnection->beginTransaction();
160
            try {
161 6
                $this->addMountedPagesToIndexQueue($mountedPages, $mountPage);
162 6
                $this->addIndexQueueItemIndexingProperties($mountPage, $mountedPages);
163
164 6
                $databaseConnection->commit();
165 6
                $mountPagesInitialized = true;
166
            } catch (\Exception $e) {
167
                $databaseConnection->rollBack();
168
169
                $this->logger->log(
170
                    SolrLogManager::ERROR,
171
                    'Index Queue initialization failed for mount pages',
172
                    [
173
                        $e->__toString()
174
                    ]
175
                );
176 6
                break;
177
            }
178
        }
179
180 6
        return $mountPagesInitialized;
181
    }
182
183
    /**
184
     * Checks whether a Mount Page is properly configured.
185
     *
186
     * @param array $mountPage A mount page
187
     * @return bool TRUE if the Mount Page is OK, FALSE otherwise
188
     */
189 6
    protected function validateMountPage(array $mountPage)
190
    {
191 6
        $isValidMountPage = true;
192
193 6
        if (empty($mountPage['mountPageSource'])) {
194 3
            $isValidMountPage = false;
195
196 3
            $flashMessage = GeneralUtility::makeInstance(
197 3
                FlashMessage::class,
198 3
                'Property "Mounted page" must not be empty. Invalid Mount Page configuration for page ID ' . $mountPage['uid'] . '.',
199 3
                'Failed to initialize Mount Page tree. ',
200 3
                FlashMessage::ERROR
201
            );
202 3
            // @extensionScannerIgnoreLine
203
            $this->flashMessageQueue->addMessage($flashMessage);
204
        }
205 6
206 3
        if (!$this->mountedPageExists($mountPage['mountPageSource'])) {
207
            $isValidMountPage = false;
208 3
209 3
            $flashMessage = GeneralUtility::makeInstance(
210
                FlashMessage::class,
211
                'The mounted page must be accessible in the frontend. '
212 3
                . 'Invalid Mount Page configuration for page ID '
213 3
                . $mountPage['uid'] . ', the mounted page with ID '
214 3
                . $mountPage['mountPageSource']
215 3
                . ' is not accessible in the frontend.',
216 3
                'Failed to initialize Mount Page tree. ',
217
                FlashMessage::ERROR
218 3
            );
219
            // @extensionScannerIgnoreLine
220
            $this->flashMessageQueue->addMessage($flashMessage);
221 6
        }
222
223
        return $isValidMountPage;
224
    }
225
226
    /**
227
     * Checks whether the mounted page (mount page source) exists. That is,
228
     * whether it accessible in the frontend. So the record must exist
229
     * (deleted = 0) and must not be hidden (hidden = 0).
230
     *
231
     * @param int $mountedPageId Mounted page ID
232 6
     * @return bool TRUE if the page is accessible in the frontend, FALSE otherwise.
233
     */
234 6
    protected function mountedPageExists($mountedPageId)
235
    {
236 6
        $mountedPageExists = false;
237 6
238 6
        $mountedPage = BackendUtility::getRecord('pages', $mountedPageId, 'uid', ' AND hidden = 0');
239
        if (!empty($mountedPage)) {
240
            $mountedPageExists = true;
241 6
        }
242
243
        return $mountedPageExists;
244
    }
245
246
    /**
247
     * Adds the virtual / mounted pages to the Index Queue as if they would
248
     * belong to the same site where they are mounted.
249
     *
250
     * @param array $mountedPages An array of mounted page IDs
251 7
     * @param array $mountProperties Array with mount point properties (mountPageSource, mountPageDestination, mountPageOverlayed)
252
     */
253 7
    protected function addMountedPagesToIndexQueue(array $mountedPages, array $mountProperties)
254 7
    {
255 7
        $mountPointIdentifier = $this->getMountPointIdentifier($mountProperties);
256
        $mountPointPageIsWithExistingQueueEntry = $this->queueItemRepository->findPageIdsOfExistingMountPagesByMountIdentifier($mountPointIdentifier);
257 7
        $mountedPagesThatNeedToBeAdded = array_diff($mountedPages, $mountPointPageIsWithExistingQueueEntry);
258
259
        if (count($mountedPagesThatNeedToBeAdded) === 0) {
260
            //nothing to do
261
            return;
262
        }
263 7
264
        /* @var Connection $connection */
265 7
        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tx_solr_indexqueue_item');
266
267 7
        $mountIdentifier = $this->getMountPointIdentifier($mountProperties);
268 7
        $initializationQuery = 'INSERT INTO tx_solr_indexqueue_item (root, item_type, item_uid, indexing_configuration, indexing_priority, changed, has_indexing_properties, pages_mountidentifier, errors) '
269 7
            . $this->buildSelectStatement() . ', 1, ' . $connection->quote($mountIdentifier, \PDO::PARAM_STR) . ',""'
270 7
            . 'FROM pages '
271 7
            . 'WHERE '
272 7
            . 'uid IN(' . implode(',', $mountedPagesThatNeedToBeAdded) . ') '
273 7
            . $this->buildTcaWhereClause()
274
            . $this->buildUserWhereClause();
275
        $logData = ['query' => $initializationQuery];
276 7
277
        try {
278
            $logData['rows'] = $this->queueItemRepository->initializeByNativeSQLStatement($initializationQuery);
279
        } catch (DBALException $DBALException) {
280
            $logData['error'] = $DBALException->getCode() . ': ' . $DBALException->getMessage();
281 7
        }
282 7
283
        $this->logInitialization($logData);
284
    }
285
286
    /**
287
     * Adds Index Queue item indexing properties for mounted pages. The page
288
     * indexer later needs to know that he's dealing with a mounted page, the
289
     * indexing properties will let make it possible for the indexer to
290
     * distinguish the mounted pages.
291
     *
292
     * @param array $mountPage An array with information about the root/destination Mount Page
293 7
     * @param array $mountedPages An array of mounted page IDs
294
     */
295 7
    protected function addIndexQueueItemIndexingProperties(array $mountPage, array $mountedPages)
296 7
    {
297
        $mountIdentifier = $this->getMountPointIdentifier($mountPage);
298 7
        $mountPageItems = $this->queueItemRepository->findAllIndexQueueItemsByRootPidAndMountIdentifierAndMountedPids($this->site->getRootPageId(), $mountIdentifier, $mountedPages);
299
300 7
        foreach ($mountPageItems as $mountPageItemRecord) {
301 7
            /* @var Item $mountPageItem */
302 7
            $mountPageItem = GeneralUtility::makeInstance(Item::class, /** @scrutinizer ignore-type */ $mountPageItemRecord);
303 7
            $mountPageItem->setIndexingProperty('mountPageSource', $mountPage['mountPageSource']);
304
            $mountPageItem->setIndexingProperty('mountPageDestination', $mountPage['mountPageDestination']);
305 7
            $mountPageItem->setIndexingProperty('isMountedPage', '1');
306
307 7
            $mountPageItem->storeIndexingProperties();
308
        }
309
    }
310
311
    /**
312
     * Builds an identifier of the given mount point properties.
313
     *
314
     * @param array $mountProperties Array with mount point properties (mountPageSource, mountPageDestination, mountPageOverlayed)
315 7
     * @return string String consisting of mountPageSource-mountPageDestination-mountPageOverlayed
316
     */
317 7
    protected function getMountPointIdentifier(array $mountProperties)
318 7
    {
319 7
        return $mountProperties['mountPageSource']
320
        . '-' . $mountProperties['mountPageDestination']
321
        . '-' . $mountProperties['mountPageOverlayed'];
322
    }
323
324
    // Mount Page resolution
325
    /**
326
     * Gets all the pages from a mounted page tree.
327
     *
328
     * @param array $mountPage
329 6
     * @return array An array of page IDs in the mounted page tree
330
     */
331 6
    protected function resolveMountPageTree($mountPage)
332 6
    {
333
        $mountPageSourceId = $mountPage['mountPageSource'];
334 6
        $mountPageIdentifier = $this->getMountPointIdentifier($mountPage);
335
336 6
        $siteRepository = GeneralUtility::makeInstance(SiteRepository::class);
337
        /* @var $siteRepository SiteRepository */
338 6
        $mountedSite = $siteRepository->getSiteByPageId($mountPageSourceId, $mountPageIdentifier);
339
340
        return $mountedSite ? $mountedSite->getPages($mountPageSourceId) : [];
0 ignored issues
show
$mountedSite is of type ApacheSolrForTypo3\Solr\Domain\Site\SiteInterface, thus it always evaluated to true.
Loading history...
341
    }
342
}
343