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

AbstractInitializer::buildSelectStatement()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 2.0758

Importance

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