Passed
Push — master ( d9bcf6...d2e609 )
by Timo
04:20
created

Page::addMountedPagesToIndexQueue()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 32
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 3.1214

Importance

Changes 0
Metric Value
dl 0
loc 32
rs 8.8571
c 0
b 0
f 0
ccs 16
cts 21
cp 0.7619
cc 3
eloc 21
nc 3
nop 2
crap 3.1214
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 2 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\Site\SiteRepository;
31
use ApacheSolrForTypo3\Solr\IndexQueue\Item;
32
use ApacheSolrForTypo3\Solr\IndexQueue\Queue;
33
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
34
use ApacheSolrForTypo3\Solr\Utility\DatabaseUtility;
35
use TYPO3\CMS\Backend\Utility\BackendUtility;
36
use TYPO3\CMS\Core\Messaging\FlashMessage;
37
use TYPO3\CMS\Core\Utility\GeneralUtility;
38
39
/**
40
 * Index Queue initializer for pages which also covers resolution of mount
41
 * pages.
42
 *
43
 * @author Ingo Renner <[email protected]>
44
 */
45
class Page extends AbstractInitializer
46
{
47
    /**
48
     * @var \ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager
49
     */
50
    protected $logger = null;
51
52
    /**
53
     * Constructor, sets type and indexingConfigurationName to "pages".
54
     *
55
     */
56 11
    public function __construct()
57
    {
58 11
        parent::__construct();
59
60 11
        $this->type = 'pages';
61 11
        $this->logger = GeneralUtility::makeInstance(SolrLogManager::class, __CLASS__);
62 11
    }
63
64
    /**
65
     * Overrides the general setType() implementation, forcing type to "pages".
66
     *
67
     * @param string $type Type to initialize (ignored).
68
     */
69 10
    public function setType($type)
70
    {
71 10
        $this->type = 'pages';
72 10
    }
73
74
    /**
75
     * Initializes Index Queue page items for a site. Includes regular pages
76
     * and mounted pages - no nested mount page structures though.
77
     *
78
     * @return bool TRUE if initialization was successful, FALSE on error.
79
     */
80 10
    public function initialize()
81
    {
82 10
        $pagesInitialized = parent::initialize();
83 10
        $mountPagesInitialized = $this->initializeMountPages();
84
85 10
        return ($pagesInitialized && $mountPagesInitialized);
86
    }
87
88
    /**
89
     * Initialize a single page that is part of a mounted tree.
90
     *
91
     * @param array $mountProperties Array of mount point properties mountPageSource, mountPageDestination, and mountPageOverlayed
92
     * @param int $mountPageId The ID of the mounted page
93
     */
94 1
    public function initializeMountedPage(array $mountProperties, $mountPageId)
95
    {
96 1
        $mountedPages = [$mountPageId];
97
98 1
        $this->addMountedPagesToIndexQueue($mountedPages, $mountProperties);
99 1
        $this->addIndexQueueItemIndexingProperties($mountProperties, $mountedPages);
100 1
    }
101
102
    /**
103
     * Initializes Mount Pages to be indexed through the Index Queue. The Mount
104
     * Pages are searched and their mounted virtual sub-trees are then resolved
105
     * and added to the Index Queue as if they were actually present below the
106
     * Mount Page.
107
     *
108
     * @return bool TRUE if initialization of the Mount Pages was successful, FALSE otherwise
109
     */
110 10
    protected function initializeMountPages()
111
    {
112 10
        $mountPagesInitialized = false;
113 10
        $mountPages = $this->findMountPages();
114
115 10
        if (empty($mountPages)) {
116 4
            $mountPagesInitialized = true;
117 4
            return $mountPagesInitialized;
118
        }
119
120 6
        foreach ($mountPages as $mountPage) {
121 6
            if (!$this->validateMountPage($mountPage)) {
122 3
                continue;
123
            }
124
125
126 6
            $mountedPages = $this->resolveMountPageTree($mountPage);
127
128
            // handling mount_pid_ol behavior
129 6
            if ($mountPage['mountPageOverlayed']) {
130
                // the page shows the mounted page's content
131 3
                $mountedPages[] = $mountPage['mountPageSource'];
132
            } else {
133
                // Add page like a regular page, as only the sub tree is
134
                // mounted. The page itself has its own content.
135 3
                $indexQueue = GeneralUtility::makeInstance(Queue::class);
136 3
                $indexQueue->updateItem($this->type, $mountPage['uid']);
137
            }
138
139
            // This can happen when the mount point does not show the content of the
140
            // mounted page and the mounted page does not have any subpages.
141 6
            if (empty($mountedPages)) {
142
                continue;
143
            }
144
145 6
            DatabaseUtility::transactionStart();
146
            try {
147 6
                $this->addMountedPagesToIndexQueue($mountedPages, $mountPage);
148 6
                $this->addIndexQueueItemIndexingProperties($mountPage, $mountedPages);
149
150 6
                DatabaseUtility::transactionCommit();
151 6
                $mountPagesInitialized = true;
152
            } catch (\Exception $e) {
153
                DatabaseUtility::transactionRollback();
154
155
                $this->logger->log(
156
                    SolrLogManager::ERROR,
157
                    'Index Queue initialization failed for mount pages',
158
                    [
159
                        $e->__toString()
160
                    ]
161
                );
162 6
                break;
163
            }
164
        }
165
166 6
        return $mountPagesInitialized;
167
    }
168
169
    /**
170
     * Checks whether a Mount Page is properly configured.
171
     *
172
     * @param array $mountPage A mount page
173
     * @return bool TRUE if the Mount Page is OK, FALSE otherwise
174
     */
175 6
    protected function validateMountPage(array $mountPage)
176
    {
177 6
        $isValidMountPage = true;
178
179 6
        if (empty($mountPage['mountPageSource'])) {
180 3
            $isValidMountPage = false;
181
182 3
            $flashMessage = GeneralUtility::makeInstance(
183 3
                FlashMessage::class,
184 3
                'Property "Mounted page" must not be empty. Invalid Mount Page configuration for page ID ' . $mountPage['uid'] . '.',
185 3
                'Failed to initialize Mount Page tree. ',
186 3
                FlashMessage::ERROR
187
            );
188 3
            $this->flashMessageQueue->addMessage($flashMessage);
189
        }
190
191 6
        if (!$this->mountedPageExists($mountPage['mountPageSource'])) {
192 3
            $isValidMountPage = false;
193
194 3
            $flashMessage = GeneralUtility::makeInstance(
195 3
                FlashMessage::class,
196
                'The mounted page must be accessible in the frontend. '
197
                . 'Invalid Mount Page configuration for page ID '
198 3
                . $mountPage['uid'] . ', the mounted page with ID '
199 3
                . $mountPage['mountPageSource']
200 3
                . ' is not accessible in the frontend.',
201 3
                'Failed to initialize Mount Page tree. ',
202 3
                FlashMessage::ERROR
203
            );
204 3
            $this->flashMessageQueue->addMessage($flashMessage);
205
        }
206
207 6
        return $isValidMountPage;
208
    }
209
210
    /**
211
     * Checks whether the mounted page (mount page source) exists. That is,
212
     * whether it accessible in the frontend. So the record must exist
213
     * (deleted = 0) and must not be hidden (hidden = 0).
214
     *
215
     * @param int $mountedPageId Mounted page ID
216
     * @return bool TRUE if the page is accessible in the frontend, FALSE otherwise.
217
     */
218 6
    protected function mountedPageExists($mountedPageId)
219
    {
220 6
        $mountedPageExists = false;
221
222 6
        $mountedPage = BackendUtility::getRecord('pages', $mountedPageId, 'uid', ' AND hidden = 0');
223 6
        if (!empty($mountedPage)) {
224 6
            $mountedPageExists = true;
225
        }
226
227 6
        return $mountedPageExists;
228
    }
229
230
    /**
231
     * Adds the virtual / mounted pages to the Index Queue as if they would
232
     * belong to the same site where they are mounted.
233
     *
234
     * @param array $mountedPages An array of mounted page IDs
235
     * @param array $mountProperties Array with mount point properties (mountPageSource, mountPageDestination, mountPageOverlayed)
236
     */
237 7
    protected function addMountedPagesToIndexQueue(array $mountedPages, array $mountProperties)
238
    {
239 7
        $mountPointPageIsWithExistingQueueEntry = $this->getPageIdsOfExistingMountPages($mountProperties);
240 7
        $mountedPagesThatNeedToBeAdded = array_diff($mountedPages, $mountPointPageIsWithExistingQueueEntry);
241
242 7
        if (count($mountedPagesThatNeedToBeAdded) === 0) {
243
            //nothing to do
244
            return;
245
        }
246
247 7
        $mountIdentifier = $this->getMountPointIdentifier($mountProperties);
248
        $initializationQuery = 'INSERT INTO tx_solr_indexqueue_item (root, item_type, item_uid, indexing_configuration, indexing_priority, changed, has_indexing_properties, pages_mountidentifier, errors) '
249 7
            . $this->buildSelectStatement() . ', 1, ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($mountIdentifier,
250 7
                'tx_solr_indexqueue_item') . ',""'
251 7
            . 'FROM pages '
252 7
            . 'WHERE '
253 7
            . 'uid IN(' . implode(',', $mountedPagesThatNeedToBeAdded) . ') '
254 7
            . $this->buildTcaWhereClause()
255 7
            . $this->buildUserWhereClause();
256
257 7
        $GLOBALS['TYPO3_DB']->sql_query($initializationQuery);
258 7
259 7
        $logData = [
260
            'query' => $initializationQuery,
261
            'rows' => $GLOBALS['TYPO3_DB']->sql_affected_rows()
262
        ];
263
        if ($GLOBALS['TYPO3_DB']->sql_errno()) {
264
            $logData['error'] = $GLOBALS['TYPO3_DB']->sql_errno() . ': ' . $GLOBALS['TYPO3_DB']->sql_error();
265
        }
266
267 7
        $this->logInitialization($logData);
268
    }
269 7
270 7
    /**
271 7
     * Retrieves an array of pageIds from mountPoints that allready have a queue entry.
272 7
     *
273 7
     * @param array $mountProperties
274 7
     * @return array
275 7
     */
276 7
    protected function getPageIdsOfExistingMountPages($mountProperties)
277 7
    {
278
        $identifier = $this->getMountPointIdentifier($mountProperties);
279
        $queueItemsOfExistingMountPoints = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
280 7
            'COUNT(*) AS queueItemCount,item_uid',
281 7
            'tx_solr_indexqueue_item',
282
            'item_type="pages" AND pages_mountidentifier = '. $identifier,
283
            'item_uid',
284
            '',
285
            '',
286
            'item_uid'
287 7
        );
288
289
        $mountedPagesIdsWithQueueItems = [];
290
        foreach ($queueItemsOfExistingMountPoints as $id => $queueItemsOfExistingMountPoint) {
291
            if (((int)$queueItemsOfExistingMountPoint['queueItemCount']) > 0) {
292
                $mountedPagesIdsWithQueueItems[] = (int)$id;
293
            }
294
        }
295
296
        return $mountedPagesIdsWithQueueItems;
297
    }
298
299 7
    /**
300
     * Adds Index Queue item indexing properties for mounted pages. The page
301 7
     * indexer later needs to know that he's dealing with a mounted page, the
302 7
     * indexing properties will let make it possible for the indexer to
303 7
     * distinguish the mounted pages.
304 7
     *
305 7
     * @param array $mountPage An array with information about the root/destination Mount Page
306 7
     * @param array $mountedPages An array of mounted page IDs
307 7
     */
308 7
    protected function addIndexQueueItemIndexingProperties(array $mountPage, array $mountedPages)
309 7
    {
310 7
        $mountIdentifier = $this->getMountPointIdentifier($mountPage);
311
        $mountPageItems = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
312
            '*',
313 7
            'tx_solr_indexqueue_item',
314
            'root = ' . intval($this->site->getRootPageId()) . ' '
315
            . 'AND item_type = \'pages\' '
316
            . 'AND item_uid IN(' . implode(',', $mountedPages) . ') '
317 7
            . 'AND has_indexing_properties = 1 '
318 7
            . 'AND pages_mountidentifier=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($mountIdentifier,
319 7
                'tx_solr_indexqueue_item')
320 7
        );
321 7
322
        if (!is_array($mountPageItems)) {
323 7
            return;
324
        }
325 7
326
        foreach ($mountPageItems as $mountPageItemRecord) {
327
            $mountPageItem = GeneralUtility::makeInstance(Item::class, $mountPageItemRecord);
328
            $mountPageItem->setIndexingProperty('mountPageSource', $mountPage['mountPageSource']);
329
            $mountPageItem->setIndexingProperty('mountPageDestination', $mountPage['mountPageDestination']);
330
            $mountPageItem->setIndexingProperty('isMountedPage', '1');
331
332
            $mountPageItem->storeIndexingProperties();
333 7
        }
334
    }
335 7
336 7
    /**
337 7
     * Builds an identifier of the given mount point properties.
338
     *
339
     * @param array $mountProperties Array with mount point properties (mountPageSource, mountPageDestination, mountPageOverlayed)
340
     * @return string String consisting of mountPageSource-mountPageDestination-mountPageOverlayed
341
     */
342
    protected function getMountPointIdentifier(array $mountProperties)
343
    {
344
        return $mountProperties['mountPageSource']
345
        . '-' . $mountProperties['mountPageDestination']
346
        . '-' . $mountProperties['mountPageOverlayed'];
347 10
    }
348
349 10
    // Mount Page resolution
350
351
    /**
352
     * Finds the mount pages in the current site.
353
     *
354 10
     * @return array An array of mount pages
355 10
     */
356 10
    protected function findMountPages()
357 10
    {
358 10
        return $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
359
            'uid,
360
				\'1\'        as isMountPage,
361
				mount_pid    as mountPageSource,
362
				uid          as mountPageDestination,
363
				mount_pid_ol as mountPageOverlayed',
364
            'pages',
365
            $this->buildPagesClause()
366
            . $this->buildTcaWhereClause()
367
            . ' AND doktype = 7 AND no_search = 0'
368 6
        );
369
    }
370 6
371 6
    /**
372
     * Gets all the pages from a mounted page tree.
373 6
     *
374
     * @param array $mountPage
375 6
     * @return array An array of page IDs in the mounted page tree
376
     */
377 6
    protected function resolveMountPageTree($mountPage)
378
    {
379
        $mountPageSourceId = $mountPage['mountPageSource'];
380
        $mountPageIdentifier = $this->getMountPointIdentifier($mountPage);
381
382
        $siteRepository = GeneralUtility::makeInstance(SiteRepository::class);
383
        /* @var $siteRepository SiteRepository */
384
        $mountedSite = $siteRepository->getSiteByPageId($mountPageSourceId, $mountPageIdentifier);
385
386
        return $mountedSite->getPages($mountPageSourceId);
387
    }
388
}
389