Passed
Push — master ( f63a3e...201a83 )
by Timo
34:43
created

Page   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 329
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 67.88%

Importance

Changes 0
Metric Value
wmc 28
c 0
b 0
f 0
lcom 1
cbo 7
dl 0
loc 329
ccs 129
cts 190
cp 0.6788
rs 10

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A setType() 0 4 1
A initialize() 0 7 2
A initializeMountedPage() 0 7 1
B initializeMountPages() 0 57 7
B validateMountPage() 0 34 3
A mountedPageExists() 0 11 2
A addMountedPagesToIndexQueue() 0 23 2
A getPageIdsOfExistingMountPages() 0 21 3
B addIndexQueueItemIndexingProperties() 0 27 3
A getMountPointIdentifier() 0 6 1
A findMountPages() 0 14 1
A resolveMountPageTree() 0 7 1
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 8
    public function __construct()
57
    {
58 8
        parent::__construct();
59
60 8
        $this->type = 'pages';
61 8
        $this->logger = GeneralUtility::makeInstance(SolrLogManager::class, __CLASS__);
62 8
    }
63
64
    /**
65
     * Overrides the general setType() implementation, forcing type to "pages".
66
     *
67
     * @param string $type Type to initialize (ignored).
68
     */
69 7
    public function setType($type)
70
    {
71 7
        $this->type = 'pages';
72 7
    }
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 7
    public function initialize()
81
    {
82 7
        $pagesInitialized = parent::initialize();
83 7
        $mountPagesInitialized = $this->initializeMountPages();
84
85 7
        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 7
    protected function initializeMountPages()
111
    {
112 7
        $mountPagesInitialized = false;
113 7
        $mountPages = $this->findMountPages();
114
115 7
        if (empty($mountPages)) {
116 4
            $mountPagesInitialized = true;
117 4
            return $mountPagesInitialized;
118
        }
119
120 3
        foreach ($mountPages as $mountPage) {
121 3
            if (!$this->validateMountPage($mountPage)) {
122 3
                continue;
123
            }
124
125 3
            $mountedPages = $this->resolveMountPageTree($mountPage['mountPageSource']);
126
127
            // handling mount_pid_ol behavior
128 3
            if ($mountPage['mountPageOverlayed']) {
129
                // the page shows the mounted page's content
130
                $mountedPages[] = $mountPage['mountPageSource'];
131
            } else {
132
                // Add page like a regular page, as only the sub tree is
133
                // mounted. The page itself has its own content.
134 3
                $indexQueue = GeneralUtility::makeInstance(Queue::class);
135 3
                $indexQueue->updateItem($this->type, $mountPage['uid']);
136
            }
137
138
            // This can happen when the mount point does not show the content of the
139
            // mounted page and the mounted page does not have any subpages.
140 3
            if (empty($mountedPages)) {
141
                continue;
142
            }
143
144 3
            DatabaseUtility::transactionStart();
145
            try {
146 3
                $this->addMountedPagesToIndexQueue($mountedPages, $mountPage);
147 3
                $this->addIndexQueueItemIndexingProperties($mountPage, $mountedPages);
148
149 3
                DatabaseUtility::transactionCommit();
150 3
                $mountPagesInitialized = true;
151
            } catch (\Exception $e) {
152
                DatabaseUtility::transactionRollback();
153
154
                $this->logger->log(
155
                    SolrLogManager::ERROR,
156
                    'Index Queue initialization failed for mount pages',
157
                    [
158
                        $e->__toString()
159
                    ]
160
                );
161 3
                break;
162
            }
163
        }
164
165 3
        return $mountPagesInitialized;
166
    }
167
168
    /**
169
     * Checks whether a Mount Page is properly configured.
170
     *
171
     * @param array $mountPage A mount page
172
     * @return bool TRUE if the Mount Page is OK, FALSE otherwise
173
     */
174 3
    protected function validateMountPage(array $mountPage)
175
    {
176 3
        $isValidMountPage = true;
177
178 3
        if (empty($mountPage['mountPageSource'])) {
179 3
            $isValidMountPage = false;
180
181 3
            $flashMessage = GeneralUtility::makeInstance(
182 3
                FlashMessage::class,
183 3
                'Property "Mounted page" must not be empty. Invalid Mount Page configuration for page ID ' . $mountPage['uid'] . '.',
184 3
                'Failed to initialize Mount Page tree. ',
185 3
                FlashMessage::ERROR
186
            );
187 3
            $this->flashMessageQueue->addMessage($flashMessage);
188
        }
189
190 3
        if (!$this->mountedPageExists($mountPage['mountPageSource'])) {
191 3
            $isValidMountPage = false;
192
193 3
            $flashMessage = GeneralUtility::makeInstance(
194 3
                FlashMessage::class,
195
                'The mounted page must be accessible in the frontend. '
196
                . 'Invalid Mount Page configuration for page ID '
197 3
                . $mountPage['uid'] . ', the mounted page with ID '
198 3
                . $mountPage['mountPageSource']
199 3
                . ' is not accessible in the frontend.',
200 3
                'Failed to initialize Mount Page tree. ',
201 3
                FlashMessage::ERROR
202
            );
203 3
            $this->flashMessageQueue->addMessage($flashMessage);
204
        }
205
206 3
        return $isValidMountPage;
207
    }
208
209
    /**
210
     * Checks whether the mounted page (mount page source) exists. That is,
211
     * whether it accessible in the frontend. So the record must exist
212
     * (deleted = 0) and must not be hidden (hidden = 0).
213
     *
214
     * @param int $mountedPageId Mounted page ID
215
     * @return bool TRUE if the page is accessible in the frontend, FALSE otherwise.
216
     */
217 3
    protected function mountedPageExists($mountedPageId)
218
    {
219 3
        $mountedPageExists = false;
220
221 3
        $mountedPage = BackendUtility::getRecord('pages', $mountedPageId, 'uid', ' AND hidden = 0');
222 3
        if (!empty($mountedPage)) {
223 3
            $mountedPageExists = true;
224
        }
225
226 3
        return $mountedPageExists;
227
    }
228
229
    /**
230
     * Adds the virtual / mounted pages to the Index Queue as if they would
231
     * belong to the same site where they are mounted.
232
     *
233
     * @param array $mountedPages An array of mounted page IDs
234
     * @param array $mountProperties Array with mount point properties (mountPageSource, mountPageDestination, mountPageOverlayed)
235
     */
236 4
    protected function addMountedPagesToIndexQueue(array $mountedPages, array $mountProperties)
237
    {
238 4
        $mountPointPageIsWithExistingQueueEntry = $this->getPageIdsOfExistingMountPages($mountedPages);
239 4
        $mountedPagesThatNeedToBeAdded = array_diff($mountedPages, $mountPointPageIsWithExistingQueueEntry);
240
241 4
        if (count($mountedPagesThatNeedToBeAdded) === 0) {
242
            //nothing to do
243 1
            return;
244
        }
245
246 3
        $mountIdentifier = $this->getMountPointIdentifier($mountProperties);
247
        $initializationQuery = 'INSERT INTO tx_solr_indexqueue_item (root, item_type, item_uid, indexing_configuration, indexing_priority, changed, has_indexing_properties, pages_mountidentifier, errors) '
248 3
            . $this->buildSelectStatement() . ', 1, ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($mountIdentifier,
249 3
                'tx_solr_indexqueue_item') . ',""'
250 3
            . 'FROM pages '
251 3
            . 'WHERE '
252 3
            . 'uid IN(' . implode(',', $mountedPagesThatNeedToBeAdded) . ') '
253 3
            . $this->buildTcaWhereClause()
254 3
            . $this->buildUserWhereClause();
255
256 3
        $GLOBALS['TYPO3_DB']->sql_query($initializationQuery);
257 3
        $this->logInitialization($initializationQuery);
258 3
    }
259
260
    /**
261
     * Retrieves an array of pageIds from mountPoints that allready have a queue entry.
262
     *
263
     * @param array $mountedPages
264
     * @return array
265
     */
266 4
    protected function getPageIdsOfExistingMountPages($mountedPages)
267
    {
268 4
        $queueItemsOfExistingMountPoints = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
269 4
            'COUNT(*) AS queueItemCount,item_uid',
270 4
            'tx_solr_indexqueue_item',
271 4
            'item_type="pages" AND item_uid IN (' . implode(',', $mountedPages) . ')',
272 4
            'item_uid',
273 4
            '',
274 4
            '',
275 4
            'item_uid'
276
        );
277
278 4
        $mountedPagesIdsWithQueueItems = [];
279 4
        foreach ($queueItemsOfExistingMountPoints as $id => $queueItemsOfExistingMountPoint) {
280 1
            if (((int)$queueItemsOfExistingMountPoint['queueItemCount']) > 0) {
281 1
                $mountedPagesIdsWithQueueItems[] = (int)$id;
282
            }
283
        }
284
285 4
        return $mountedPagesIdsWithQueueItems;
286
    }
287
288
    /**
289
     * Adds Index Queue item indexing properties for mounted pages. The page
290
     * indexer later needs to know that he's dealing with a mounted page, the
291
     * indexing properties will let make it possible for the indexer to
292
     * distinguish the mounted pages.
293
     *
294
     * @param array $mountPage An array with information about the root/destination Mount Page
295
     * @param array $mountedPages An array of mounted page IDs
296
     */
297 4
    protected function addIndexQueueItemIndexingProperties(array $mountPage, array $mountedPages)
298
    {
299 4
        $mountIdentifier = $this->getMountPointIdentifier($mountPage);
300 4
        $mountPageItems = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
301 4
            '*',
302 4
            'tx_solr_indexqueue_item',
303 4
            'root = ' . intval($this->site->getRootPageId()) . ' '
304 4
            . 'AND item_type = \'pages\' '
305 4
            . 'AND item_uid IN(' . implode(',', $mountedPages) . ') '
306 4
            . 'AND has_indexing_properties = 1 '
307 4
            . 'AND pages_mountidentifier=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($mountIdentifier,
308 4
                'tx_solr_indexqueue_item')
309
        );
310
311 4
        if (!is_array($mountPageItems)) {
312
            return;
313
        }
314
315 4
        foreach ($mountPageItems as $mountPageItemRecord) {
316 3
            $mountPageItem = GeneralUtility::makeInstance(Item::class, $mountPageItemRecord);
317 3
            $mountPageItem->setIndexingProperty('mountPageSource', $mountPage['mountPageSource']);
318 3
            $mountPageItem->setIndexingProperty('mountPageDestination', $mountPage['mountPageDestination']);
319 3
            $mountPageItem->setIndexingProperty('isMountedPage', '1');
320
321 3
            $mountPageItem->storeIndexingProperties();
322
        }
323 4
    }
324
325
    /**
326
     * Builds an identifier of the given mount point properties.
327
     *
328
     * @param array $mountProperties Array with mount point properties (mountPageSource, mountPageDestination, mountPageOverlayed)
329
     * @return string String consisting of mountPageDestination-mountPageSource-mountPageOverlayed
330
     */
331 4
    protected function getMountPointIdentifier(array $mountProperties)
332
    {
333 4
        return $mountProperties['mountPageDestination']
334 4
        . '-' . $mountProperties['mountPageSource']
335 4
        . '-' . $mountProperties['mountPageOverlayed'];
336
    }
337
338
    // Mount Page resolution
339
340
    /**
341
     * Finds the mount pages in the current site.
342
     *
343
     * @return array An array of mount pages
344
     */
345 7
    protected function findMountPages()
346
    {
347 7
        return $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
348
            'uid,
349
				\'1\'        as isMountPage,
350
				mount_pid    as mountPageSource,
351
				uid          as mountPageDestination,
352 7
				mount_pid_ol as mountPageOverlayed',
353 7
            'pages',
354 7
            $this->buildPagesClause()
355 7
            . $this->buildTcaWhereClause()
356 7
            . ' AND doktype = 7 AND no_search = 0'
357
        );
358
    }
359
360
    /**
361
     * Gets all the pages from a mounted page tree.
362
     *
363
     * @param int $mountPageSourceId
364
     * @return array An array of page IDs in the mounted page tree
365
     */
366 3
    protected function resolveMountPageTree($mountPageSourceId)
367
    {
368 3
        $siteRepository = GeneralUtility::makeInstance(SiteRepository::class);
369 3
        $mountedSite = $siteRepository->getSiteByPageId($mountPageSourceId);
370
371 3
        return $mountedSite->getPages($mountPageSourceId);
372
    }
373
}
374