Passed
Push — master ( f55a40...3301f5 )
by Timo
23:56
created

AbstractInitializer::initialize()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 19
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 2.1757

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 11
cts 17
cp 0.6471
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 14
nc 2
nop 0
crap 2.1757
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\Site;
32
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
33
use Doctrine\DBAL\DBALException;
34
use TYPO3\CMS\Backend\Utility\BackendUtility;
35
use TYPO3\CMS\Core\Messaging\FlashMessageService;
36
use TYPO3\CMS\Core\Utility\GeneralUtility;
37
38
/**
39
 * Abstract Index Queue initializer with implementation  of methods for common
40
 * needs during Index Queue initialization.
41
 *
42
 * @author Ingo Renner <[email protected]>
43
 */
44
abstract class AbstractInitializer implements IndexQueueInitializer
45
{
46
47
    /**
48
     * Site to initialize
49
     *
50
     * @var Site
51
     */
52
    protected $site;
53
54
    /**
55
     * The type of items this initializer is handling.
56
     *
57
     * @var string
58
     */
59
    protected $type;
60
61
    /**
62
     * Index Queue configuration.
63
     *
64
     * @var array
65
     */
66
    protected $indexingConfiguration;
67
68
    /**
69
     * Indexing configuration name.
70
     *
71
     * @var string
72
     */
73
    protected $indexingConfigurationName;
74
75
    /**
76
     * Flash message queue
77
     *
78
     * @var \TYPO3\CMS\Core\Messaging\FlashMessageQueue
79
     */
80
    protected $flashMessageQueue;
81
82
    /**
83
     * @var \ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager
84
     */
85
    protected $logger = null;
86
87
    /**
88
     * @var QueueItemRepository
89
     */
90
    protected $queueItemRepository;
91
92
    /**
93
     * Constructor, prepares the flash message queue
94
     * @param QueueItemRepository|null $queueItemRepository
95
     */
96 12
    public function __construct(QueueItemRepository $queueItemRepository = null)
97
    {
98 12
        $this->logger = GeneralUtility::makeInstance(SolrLogManager::class, /** @scrutinizer ignore-type */ __CLASS__);
99 12
        $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
100 12
        $this->flashMessageQueue = $flashMessageService->getMessageQueueByIdentifier('solr.queue.initializer');
101 12
        $this->queueItemRepository = $queueItemRepository ?? GeneralUtility::makeInstance(QueueItemRepository::class);
102 12
    }
103
104
    /**
105
     * Sets the site for the initializer.
106
     *
107
     * @param Site $site The site to initialize Index Queue items for.
108
     */
109 12
    public function setSite(Site $site)
110
    {
111 12
        $this->site = $site;
112 12
    }
113
114
    /**
115
     * Set the type (usually a Db table name) of items to initialize.
116
     *
117
     * @param string $type Type to initialize.
118
     */
119 1
    public function setType($type)
120
    {
121 1
        $this->type = $type;
122 1
    }
123
124
    /**
125
     * Sets the configuration for how to index a type of items.
126
     *
127
     * @param array $indexingConfiguration Indexing configuration from TypoScript
128
     */
129 6
    public function setIndexingConfiguration(array $indexingConfiguration)
130
    {
131 6
        $this->indexingConfiguration = $indexingConfiguration;
132 6
    }
133
134
    /**
135
     * Sets the name of the indexing configuration to initialize.
136
     *
137
     * @param string $indexingConfigurationName Indexing configuration name
138
     */
139 11
    public function setIndexingConfigurationName($indexingConfigurationName)
140
    {
141 11
        $this->indexingConfigurationName = (string)$indexingConfigurationName;
142 11
    }
143
144
    /**
145
     * Initializes Index Queue items for a certain site and indexing
146
     * configuration.
147
     *
148
     * @return bool TRUE if initialization was successful, FALSE on error.
149
     */
150 11
    public function initialize()
151
    {
152
        $initializationQuery = 'INSERT INTO tx_solr_indexqueue_item (root, item_type, item_uid, indexing_configuration, indexing_priority, changed, errors) '
153 11
            . $this->buildSelectStatement() . ',"" '
154 11
            . 'FROM ' . $this->type . ' '
155 11
            . 'WHERE '
156 11
            . $this->buildPagesClause()
157 11
            . $this->buildTcaWhereClause()
158 11
            . $this->buildUserWhereClause();
159 11
        $logData = ['query' => $initializationQuery];
160
161
        try {
162 11
            $logData['rows'] = $this->queueItemRepository->initializeByNativeSQLStatement($initializationQuery);
163
        } catch (DBALException $DBALException) {
164
            $logData['error'] = $DBALException->getCode() . ': ' . $DBALException->getMessage();
165
        }
166
167 11
        $this->logInitialization($logData);
168 11
        return true;
169
    }
170
171
    /**
172
     * Builds the SELECT part of the Index Queue initialization query.
173
     *
174
     */
175 12
    protected function buildSelectStatement()
176
    {
177 12
        $changedField = $GLOBALS['TCA'][$this->type]['ctrl']['tstamp'];
178 12
        if (!empty($GLOBALS['TCA'][$this->type]['ctrl']['enablecolumns']['starttime'])) {
179 12
            $changedField = 'GREATEST(' . $GLOBALS['TCA'][$this->type]['ctrl']['enablecolumns']['starttime'] . ',' . $GLOBALS['TCA'][$this->type]['ctrl']['tstamp'] . ')';
180
        }
181
        $select = 'SELECT '
182 12
            . '\'' . $this->site->getRootPageId() . '\' as root, '
183 12
            . '\'' . $this->type . '\' AS item_type, '
184 12
            . 'uid AS item_uid, '
185 12
            . '\'' . $this->indexingConfigurationName . '\' as indexing_configuration, '
186 12
            . $this->getIndexingPriority() . ' AS indexing_priority, '
187 12
            . $changedField . ' AS changed';
188
189 12
        return $select;
190
    }
191
192
    // initialization query building
193
194
    /**
195
     * Reads the indexing priority for an indexing configuration.
196
     *
197
     * @return int Indexing priority
198
     */
199 12
    protected function getIndexingPriority()
200
    {
201 12
        $priority = 0;
202
203 12
        if (!empty($this->indexingConfiguration['indexingPriority'])) {
204
            $priority = (int)$this->indexingConfiguration['indexingPriority'];
205
        }
206
207 12
        return $priority;
208
    }
209
210
    /**
211
     * Builds a part of the WHERE clause of the Index Queue initialization
212
     * query. This part selects the limits items to be selected from the pages
213
     * in a site only, plus additional pages that may have been configured.
214
     *
215
     */
216 11
    protected function buildPagesClause()
217
    {
218 11
        $pages = $this->getPages();
219 11
        $pageIdField = ($this->type === 'pages') ? 'uid' : 'pid';
220
221 11
        return $pageIdField . ' IN(' . implode(',', $pages) . ')';
222
    }
223
224
    /**
225
     * Gets the pages in a site plus additional pages that may have been
226
     * configured.
227
     *
228
     * @return array A (sorted) array of page IDs in a site
229
     */
230 11
    protected function getPages()
231
    {
232 11
        $pages = $this->site->getPages();
233 11
        $additionalPageIds = [];
234 11
        if (!empty($this->indexingConfiguration['additionalPageIds'])) {
235
            $additionalPageIds = GeneralUtility::intExplode(',', $this->indexingConfiguration['additionalPageIds']);
236
        }
237
238 11
        $pages = array_merge($pages, $additionalPageIds);
239 11
        sort($pages, SORT_NUMERIC);
240
241 11
        return $pages;
242
    }
243
244
    /**
245
     * Builds the WHERE clauses of the Index Queue initialization query based
246
     * on TCA information for the type to be initialized.
247
     *
248
     * @return string Conditions to only add indexable items to the Index Queue
249
     */
250 12
    protected function buildTcaWhereClause()
251
    {
252 12
        $tcaWhereClause = '';
253 12
        $conditions = [];
254
255 12
        if (isset($GLOBALS['TCA'][$this->type]['ctrl']['delete'])) {
256 12
            $conditions['delete'] = $GLOBALS['TCA'][$this->type]['ctrl']['delete'] . ' = 0';
257
        }
258
259 12
        if (isset($GLOBALS['TCA'][$this->type]['ctrl']['enablecolumns']['disabled'])) {
260 12
            $conditions['disabled'] = $GLOBALS['TCA'][$this->type]['ctrl']['enablecolumns']['disabled'] . ' = 0';
261
        }
262
263 12
        if (isset($GLOBALS['TCA'][$this->type]['ctrl']['enablecolumns']['endtime'])) {
264
            // only include records with a future endtime or default value (0)
265 12
            $endTimeFieldName = $GLOBALS['TCA'][$this->type]['ctrl']['enablecolumns']['endtime'];
266 12
            $conditions['endtime'] = '(' . $endTimeFieldName . ' > ' . time() . ' OR ' . $endTimeFieldName . ' = 0)';
267
        }
268
269 12
        if (BackendUtility::isTableLocalizable($this->type)) {
270
            $conditions['languageField'] = [
271
                $GLOBALS['TCA'][$this->type]['ctrl']['languageField'] . ' = 0',
272
                // default language
273
                $GLOBALS['TCA'][$this->type]['ctrl']['languageField'] . ' = -1'
274
                // all languages
275
            ];
276
            if (isset($GLOBALS['TCA'][$this->type]['ctrl']['transOrigPointerField'])) {
277
                $conditions['languageField'][] = $GLOBALS['TCA'][$this->type]['ctrl']['transOrigPointerField'] . ' = 0'; // translations without original language source
278
            }
279
            $conditions['languageField'] = '(' . implode(' OR ',
280
                    $conditions['languageField']) . ')';
281
        }
282
283 12
        if (!empty($GLOBALS['TCA'][$this->type]['ctrl']['versioningWS'])) {
284
            // versioning is enabled for this table: exclude draft workspace records
285 12
            $conditions['versioningWS'] = 'pid != -1';
286
        }
287
288 12
        if (count($conditions)) {
289 12
            $tcaWhereClause = ' AND ' . implode(' AND ', $conditions);
290
        }
291
292 12
        return $tcaWhereClause;
293
    }
294
295
    /**
296
     * Builds the WHERE clauses of the Index Queue initialization query based
297
     * on TypoScript configuration for the type to be initialized.
298
     *
299
     * @return string Conditions to add items to the Index Queue based on TypoScript configuration
300
     */
301 12
    protected function buildUserWhereClause()
302
    {
303 12
        $condition = '';
304
305
        // FIXME replace this with the mechanism described below
306 12
        if (isset($this->indexingConfiguration['additionalWhereClause'])) {
307 5
            $condition = ' AND ' . $this->indexingConfiguration['additionalWhereClause'];
308
        }
309
310 12
        return $condition;
311
312
        // TODO add a query builder implementation based on TypoScript configuration
313
314
        /* example TypoScript
315
316
                @see http://docs.jboss.org/drools/release/5.4.0.Final/drools-expert-docs/html_single/index.html
317
                @see The Java Rule Engine API (JSR94)
318
319
                tt_news {
320
321
                        // RULES cObject provided by EXT:rules, simply evaluates to boolean TRUE or FALSE
322
                    conditions = RULES
323
                    conditions {
324
325
                        and {
326
327
                            10 {
328
                                field = pid
329
                                value = 2,3,5
330
                                condition = in / equals / notEquals / greaterThan / lessThan / greaterThanOrEqual / lessThanOrEqual
331
                            }
332
333
                            20 {
334
                                field = ...
335
                                value = ...
336
                                condition = ...
337
338
                                or {
339
                                    10 {
340
                                        field = ...
341
                                        value = ...
342
                                        condition =  ...
343
                                    }
344
345
                                    20 {
346
                                        field = ...
347
                                        value = ...
348
                                        condition = ...
349
                                    }
350
                                }
351
                            }
352
353
                        }
354
355
                    }
356
357
                    fields {
358
                        // field mapping
359
                    }
360
                }
361
        */
362
    }
363
364
    /**
365
     * Writes the passed log data to the log.
366
     *
367
     * @param array $logData
368
     */
369 12
    protected function logInitialization(array $logData)
370
    {
371 12
        if (!$this->site->getSolrConfiguration()->getLoggingIndexingIndexQueueInitialization()) {
372 12
            return;
373
        }
374
375
        $logSeverity = isset($logData['error']) ? SolrLogManager::ERROR : SolrLogManager::NOTICE;
376
        $logData = array_merge($logData, [
377
            'site' => $this->site->getLabel(),
378
            'indexing configuration name' => $this->indexingConfigurationName,
379
            'type' => $this->type,
380
        ]);
381
382
        $message = 'Index Queue initialized for indexing configuration ' . $this->indexingConfigurationName;
383
        $this->logger->log($logSeverity, $message, $logData);
384
    }
385
}
386