Completed
Push — master ( 1ccb29...3a83fd )
by Timo
47s
created

Page::mountedPageExists()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2.1481

Importance

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