Passed
Pull Request — release-11.0.x (#2934)
by Rafael
34:51 queued 29:14
created

AbstractInitializer::getPages()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2.0625

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 9
c 1
b 0
f 0
dl 0
loc 15
ccs 9
cts 12
cp 0.75
rs 9.9666
cc 2
nc 2
nop 0
crap 2.0625
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\Site;
32
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
33
use ApacheSolrForTypo3\Solr\Util;
34
use Doctrine\DBAL\DBALException;
35
use TYPO3\CMS\Backend\Utility\BackendUtility;
36
use TYPO3\CMS\Core\Database\ConnectionPool;
37
use TYPO3\CMS\Core\Messaging\FlashMessageService;
38
use TYPO3\CMS\Core\Utility\GeneralUtility;
39
40
/**
41
 * Abstract Index Queue initializer with implementation  of methods for common
42
 * needs during Index Queue initialization.
43
 *
44
 * @author Ingo Renner <[email protected]>
45
 */
46
abstract class AbstractInitializer implements IndexQueueInitializer
47
{
48
49
    /**
50
     * Site to initialize
51
     *
52
     * @var Site
53
     */
54
    protected $site;
55
56
    /**
57
     * The type of items this initializer is handling.
58
     *
59
     * @var string
60
     */
61
    protected $type;
62
63
    /**
64
     * Index Queue configuration.
65
     *
66
     * @var array
67
     */
68
    protected $indexingConfiguration;
69
70
    /**
71
     * Indexing configuration name.
72
     *
73
     * @var string
74
     */
75
    protected $indexingConfigurationName;
76
77
    /**
78
     * Flash message queue
79
     *
80
     * @var \TYPO3\CMS\Core\Messaging\FlashMessageQueue
81
     */
82
    protected $flashMessageQueue;
83
84
    /**
85
     * @var \ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager
86
     */
87
    protected $logger = null;
88
89
    /**
90
     * @var QueueItemRepository
91
     */
92
    protected $queueItemRepository;
93
94
    /**
95
     * Constructor, prepares the flash message queue
96
     * @param QueueItemRepository|null $queueItemRepository
97
     */
98 12
    public function __construct(QueueItemRepository $queueItemRepository = null)
99
    {
100 12
        $this->logger = GeneralUtility::makeInstance(SolrLogManager::class, /** @scrutinizer ignore-type */ __CLASS__);
101 12
        $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
102 12
        $this->flashMessageQueue = $flashMessageService->getMessageQueueByIdentifier('solr.queue.initializer');
103 12
        $this->queueItemRepository = $queueItemRepository ?? GeneralUtility::makeInstance(QueueItemRepository::class);
104 12
    }
105
106
    /**
107
     * Sets the site for the initializer.
108
     *
109
     * @param Site $site The site to initialize Index Queue items for.
110
     */
111 12
    public function setSite(Site $site)
112
    {
113 12
        $this->site = $site;
114 12
    }
115
116
    /**
117
     * Set the type (usually a Db table name) of items to initialize.
118
     *
119
     * @param string $type Type to initialize.
120
     */
121 1
    public function setType($type)
122
    {
123 1
        $this->type = $type;
124 1
    }
125
126
    /**
127
     * Sets the configuration for how to index a type of items.
128
     *
129
     * @param array $indexingConfiguration Indexing configuration from TypoScript
130
     */
131 6
    public function setIndexingConfiguration(array $indexingConfiguration)
132
    {
133 6
        $this->indexingConfiguration = $indexingConfiguration;
134 6
    }
135
136
    /**
137
     * Sets the name of the indexing configuration to initialize.
138
     *
139
     * @param string $indexingConfigurationName Indexing configuration name
140
     */
141 11
    public function setIndexingConfigurationName($indexingConfigurationName)
142
    {
143 11
        $this->indexingConfigurationName = (string)$indexingConfigurationName;
144 11
    }
145
146
    /**
147
     * Initializes Index Queue items for a certain site and indexing
148
     * configuration.
149
     *
150
     * @return bool TRUE if initialization was successful, FALSE on error.
151
     */
152 11
    public function initialize()
153
    {
154
        /** @var ConnectionPool $connectionPool */
155 11
        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
156
157 11
        $fetchItemsQuery = $this->buildSelectStatement() . ', "" as errors '
158 11
            . 'FROM ' . $this->type . ' '
159 11
            . 'WHERE '
160 11
            . $this->buildPagesClause()
161 11
            . $this->buildTcaWhereClause()
162 11
            . $this->buildUserWhereClause();
163
164
        try {
165 11
            if ($connectionPool->getConnectionForTable($this->type)->getParams() === $connectionPool->getConnectionForTable('tx_solr_indexqueue_item')->getParams()) {
166
                // If both tables are in the same DB, send only one query to copy all datas from one table to the other
167 11
                $initializationQuery = 'INSERT INTO tx_solr_indexqueue_item (root, item_type, item_uid, indexing_configuration, indexing_priority, changed, errors) ' . $fetchItemsQuery;
168 11
                $logData = ['query' => $initializationQuery];
169 11
                $logData['rows'] = $this->queueItemRepository->initializeByNativeSQLStatement($initializationQuery);
170
            } else {
171
                // If tables are using distinct connections, start by fetching items matching criteria
172
                $logData = ['query' => $fetchItemsQuery];
173
                $items = $connectionPool->getConnectionForTable($this->type)->fetchAll($fetchItemsQuery);
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\DBAL\Connection::fetchAll() has been deprecated: Use fetchAllAssociative() ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

173
                $items = /** @scrutinizer ignore-deprecated */ $connectionPool->getConnectionForTable($this->type)->fetchAll($fetchItemsQuery);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
174
                $logData['rows'] = count($items);
175
176
                if (count($items)) {
177
                    // Add items to the queue (if any)
178
                    $logData['rows'] = $connectionPool
179
                        ->getConnectionForTable('tx_solr_indexqueue_item')
180 11
                        ->bulkInsert('tx_solr_indexqueue_item', $items, array_keys($items[0]));
181
                }
182
            }
183
        } catch (DBALException $DBALException) {
184
            $logData['error'] = $DBALException->getCode() . ': ' . $DBALException->getMessage();
185
        }
186
187 11
        $this->logInitialization($logData);
188 11
        return true;
189
    }
190
191
    /**
192
     * Builds the SELECT part of the Index Queue initialization query.
193
     *
194
     */
195 12
    protected function buildSelectStatement()
196
    {
197 12
        $changedField = $GLOBALS['TCA'][$this->type]['ctrl']['tstamp'];
198 12
        if (!empty($GLOBALS['TCA'][$this->type]['ctrl']['enablecolumns']['starttime'])) {
199 12
            $changedField = 'GREATEST(' . $GLOBALS['TCA'][$this->type]['ctrl']['enablecolumns']['starttime'] . ',' . $GLOBALS['TCA'][$this->type]['ctrl']['tstamp'] . ')';
200
        }
201
        $select = 'SELECT '
202 12
            . '\'' . $this->site->getRootPageId() . '\' as root, '
203 12
            . '\'' . $this->type . '\' AS item_type, '
204 12
            . 'uid AS item_uid, '
205 12
            . '\'' . $this->indexingConfigurationName . '\' as indexing_configuration, '
206 12
            . $this->getIndexingPriority() . ' AS indexing_priority, '
207 12
            . $changedField . ' AS changed';
208
209 12
        return $select;
210
    }
211
212
    // initialization query building
213
214
    /**
215
     * Reads the indexing priority for an indexing configuration.
216
     *
217
     * @return int Indexing priority
218
     */
219 12
    protected function getIndexingPriority()
220
    {
221 12
        $priority = 0;
222
223 12
        if (!empty($this->indexingConfiguration['indexingPriority'])) {
224
            $priority = (int)$this->indexingConfiguration['indexingPriority'];
225
        }
226
227 12
        return $priority;
228
    }
229
230
    /**
231
     * Builds a part of the WHERE clause of the Index Queue initialization
232
     * query. This part selects the limits items to be selected from the pages
233
     * in a site only, plus additional pages that may have been configured.
234
     *
235
     */
236 11
    protected function buildPagesClause()
237
    {
238 11
        $pages = $this->getPages();
239 11
        $pageIdField = ($this->type === 'pages') ? 'uid' : 'pid';
240
241 11
        return $pageIdField . ' IN(' . implode(',', $pages) . ')';
242
    }
243
244
    /**
245
     * Gets the pages in a site plus additional pages that may have been
246
     * configured.
247
     *
248
     * @return array A (sorted) array of page IDs in a site
249
     */
250 11
    protected function getPages()
251
    {
252 11
        $pages = $this->site->getPages();
253 11
        $additionalPageIds = [];
254 11
        if (!empty($this->indexingConfiguration['additionalPageIds'])) {
255
            $additionalPageIds = GeneralUtility::intExplode(',', $this->indexingConfiguration['additionalPageIds']);
256
        }
257
258 11
        $pages = array_merge($pages, $additionalPageIds);
259 11
        sort($pages, SORT_NUMERIC);
260
261 11
        $pagesWithinNoSearchSubEntriesPages = $this->site->getPagesWithinNoSearchSubEntriesPages();
262
        // @todo: log properly if $additionalPageIds are within $pagesWithinNoSearchSubEntriesPages
263 11
        $pages = array_values(array_diff($pages, $pagesWithinNoSearchSubEntriesPages));
264 11
        return $pages;
265
    }
266
267
    /**
268
     * Builds the WHERE clauses of the Index Queue initialization query based
269
     * on TCA information for the type to be initialized.
270
     *
271
     * @return string Conditions to only add indexable items to the Index Queue
272
     */
273 12
    protected function buildTcaWhereClause()
274
    {
275 12
        $tcaWhereClause = '';
276 12
        $conditions = [];
277
278 12
        if (isset($GLOBALS['TCA'][$this->type]['ctrl']['delete'])) {
279 12
            $conditions['delete'] = $GLOBALS['TCA'][$this->type]['ctrl']['delete'] . ' = 0';
280
        }
281
282 12
        if (isset($GLOBALS['TCA'][$this->type]['ctrl']['enablecolumns']['disabled'])) {
283 12
            $conditions['disabled'] = $GLOBALS['TCA'][$this->type]['ctrl']['enablecolumns']['disabled'] . ' = 0';
284
        }
285
286 12
        if (isset($GLOBALS['TCA'][$this->type]['ctrl']['enablecolumns']['endtime'])) {
287
            // only include records with a future endtime or default value (0)
288 12
            $endTimeFieldName = $GLOBALS['TCA'][$this->type]['ctrl']['enablecolumns']['endtime'];
289 12
            $conditions['endtime'] = '(' . $endTimeFieldName . ' > ' . time() . ' OR ' . $endTimeFieldName . ' = 0)';
290
        }
291
292 12
        if (BackendUtility::isTableLocalizable($this->type)) {
293 12
            $conditions['languageField'] = [
294 12
                $GLOBALS['TCA'][$this->type]['ctrl']['languageField'] . ' = 0',
295
                // default language
296 12
                $GLOBALS['TCA'][$this->type]['ctrl']['languageField'] . ' = -1'
297
                // all languages
298
            ];
299 12
            if (isset($GLOBALS['TCA'][$this->type]['ctrl']['transOrigPointerField'])) {
300 12
                $conditions['languageField'][] = $GLOBALS['TCA'][$this->type]['ctrl']['transOrigPointerField'] . ' = 0'; // translations without original language source
301
            }
302 12
            $conditions['languageField'] = '(' . implode(' OR ',
303 12
                    $conditions['languageField']) . ')';
304
        }
305
306 12
        if (!empty($GLOBALS['TCA'][$this->type]['ctrl']['versioningWS'])) {
307
            // versioning is enabled for this table: exclude draft workspace records
308 12
            if (Util::getIsTYPO3VersionBelow10()) {
309 12
                $conditions['versioningWS'] = 'pid != -1';
310
            } else {
311
                /* @see \TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction::buildExpression */
312
                $conditions['versioningWS'] = 't3ver_wsid = 0';
313
            }
314
        }
315
316 12
        if (count($conditions)) {
317 12
            $tcaWhereClause = ' AND ' . implode(' AND ', $conditions);
318
        }
319
320 12
        return $tcaWhereClause;
321
    }
322
323
    /**
324
     * Builds the WHERE clauses of the Index Queue initialization query based
325
     * on TypoScript configuration for the type to be initialized.
326
     *
327
     * @return string Conditions to add items to the Index Queue based on TypoScript configuration
328
     */
329 12
    protected function buildUserWhereClause()
330
    {
331 12
        $condition = '';
332
333
        // FIXME replace this with the mechanism described below
334 12
        if (isset($this->indexingConfiguration['additionalWhereClause'])) {
335 5
            $condition = ' AND ' . $this->indexingConfiguration['additionalWhereClause'];
336
        }
337
338 12
        return $condition;
339
340
        // TODO add a query builder implementation based on TypoScript configuration
341
342
        /* example TypoScript
343
344
                @see http://docs.jboss.org/drools/release/5.4.0.Final/drools-expert-docs/html_single/index.html
345
                @see The Java Rule Engine API (JSR94)
346
347
                tt_news {
348
349
                        // RULES cObject provided by EXT:rules, simply evaluates to boolean TRUE or FALSE
350
                    conditions = RULES
351
                    conditions {
352
353
                        and {
354
355
                            10 {
356
                                field = pid
357
                                value = 2,3,5
358
                                condition = in / equals / notEquals / greaterThan / lessThan / greaterThanOrEqual / lessThanOrEqual
359
                            }
360
361
                            20 {
362
                                field = ...
363
                                value = ...
364
                                condition = ...
365
366
                                or {
367
                                    10 {
368
                                        field = ...
369
                                        value = ...
370
                                        condition =  ...
371
                                    }
372
373
                                    20 {
374
                                        field = ...
375
                                        value = ...
376
                                        condition = ...
377
                                    }
378
                                }
379
                            }
380
381
                        }
382
383
                    }
384
385
                    fields {
386
                        // field mapping
387
                    }
388
                }
389
        */
390
    }
391
392
    /**
393
     * Writes the passed log data to the log.
394
     *
395
     * @param array $logData
396
     */
397 12
    protected function logInitialization(array $logData)
398
    {
399 12
        if (!$this->site->getSolrConfiguration()->getLoggingIndexingIndexQueueInitialization()) {
400 12
            return;
401
        }
402
403
        $logSeverity = isset($logData['error']) ? SolrLogManager::ERROR : SolrLogManager::NOTICE;
404
        $logData = array_merge($logData, [
405
            'site' => $this->site->getLabel(),
406
            'indexing configuration name' => $this->indexingConfigurationName,
407
            'type' => $this->type,
408
        ]);
409
410
        $message = 'Index Queue initialized for indexing configuration ' . $this->indexingConfigurationName;
411
        $this->logger->log($logSeverity, $message, $logData);
412
    }
413
}
414