Completed
Push — master ( d2e609...7e8018 )
by Timo
18s
created

Page::addIndexQueueItemIndexingProperties()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

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