Passed
Pull Request — master (#1287)
by Sascha
21:29
created

Util::getConfigurationFromInitializedTSFE()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 8
ccs 5
cts 5
cp 1
rs 9.4285
cc 1
eloc 5
nc 1
nop 1
crap 1
1
<?php
2
namespace ApacheSolrForTypo3\Solr;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2009-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
 *
19
 *  This script is distributed in the hope that it will be useful,
20
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 *  GNU General Public License for more details.
23
 *
24
 *  This copyright notice MUST APPEAR in all copies of the script!
25
 ***************************************************************/
26
27
use ApacheSolrForTypo3\Solr\Domain\Index\Queue\RecordMonitor\Helper\RootPageResolver;
28
use ApacheSolrForTypo3\Solr\Domain\Site\SiteRepository;
29
use ApacheSolrForTypo3\Solr\Domain\Site\SiteHashService;
30
use ApacheSolrForTypo3\Solr\System\Cache\TwoLevelCache;
31
use ApacheSolrForTypo3\Solr\System\Configuration\ConfigurationManager;
32
use ApacheSolrForTypo3\Solr\System\Configuration\ConfigurationPageResolver;
33
use ApacheSolrForTypo3\Solr\System\Configuration\ExtensionConfiguration;
34
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration;
35
use ApacheSolrForTypo3\Solr\System\DateTime\FormatService;
36
use TYPO3\CMS\Backend\Utility\BackendUtility;
37
use TYPO3\CMS\Core\TimeTracker\NullTimeTracker;
38
use TYPO3\CMS\Core\TypoScript\ExtendedTemplateService;
39
use TYPO3\CMS\Core\Utility\GeneralUtility;
40
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
41
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
42
use TYPO3\CMS\Frontend\Page\PageRepository;
43
44
/**
45
 * Utility class for tx_solr
46
 *
47
 * @author Ingo Renner <[email protected]>
48
 */
49
class Util
50
{
51
52
    /**
53
     * Generates a document id for documents representing page records.
54
     *
55
     * @param int $uid The page's uid
56
     * @param int $typeNum The page's typeNum
57
     * @param int $language the language id, defaults to 0
58
     * @param string $accessGroups comma separated list of uids of groups that have access to that page
59
     * @param string $mountPointParameter The mount point parameter that is used to access the page.
60
     * @return string The document id for that page
61
     */
62 45
    public static function getPageDocumentId(
63
        $uid,
64
        $typeNum = 0,
65
        $language = 0,
66
        $accessGroups = '0,-1',
67
        $mountPointParameter = ''
68
    ) {
69 45
        $additionalParameters = $typeNum . '/' . $language . '/' . $accessGroups;
70
71 45
        if ((string)$mountPointParameter !== '') {
72
            $additionalParameters = $mountPointParameter . '/' . $additionalParameters;
73
        }
74
75 45
        $documentId = self::getDocumentId(
76 45
            'pages',
77 45
            $uid,
78 45
            $uid,
79 45
            $additionalParameters
80
        );
81
82 45
        return $documentId;
83
    }
84
85
    /**
86
     * Generates a document id in the form $siteHash/$type/$uid.
87
     *
88
     * @param string $table The records table name
89
     * @param int $pid The record's pid
90
     * @param int $uid The record's uid
91
     * @param string $additionalIdParameters Additional ID parameters
92
     * @return string A document id
93
     */
94 56
    public static function getDocumentId(
95
        $table,
96
        $pid,
97
        $uid,
98
        $additionalIdParameters = ''
99
    ) {
100 56
        $siteRepository = GeneralUtility::makeInstance(SiteRepository::class);
101 56
        $site = $siteRepository->getSiteByPageId($pid);
102 56
        $siteHash = $site->getSiteHash();
103
104 56
        $documentId = $siteHash . '/' . $table . '/' . $uid;
105 56
        if (!empty($additionalIdParameters)) {
106 45
            $documentId .= '/' . $additionalIdParameters;
107
        }
108
109 56
        return $documentId;
110
    }
111
112
    /**
113
     * Returns given word as CamelCased.
114
     *
115
     * Converts a word like "send_email" to "SendEmail". It
116
     * will remove non alphanumeric characters from the word, so
117
     * "who's online" will be converted to "WhoSOnline"
118
     *
119
     * @param string $word Word to convert to camel case
120
     * @return string UpperCamelCasedWord
121
     */
122
    public static function camelize($word)
123
    {
124
        return str_replace(' ', '',
125
            ucwords(preg_replace('![^A-Z^a-z^0-9]+!', ' ', $word)));
126
    }
127
128
    /**
129
     * Returns a given CamelCasedString as an lowercase string with underscores.
130
     * Example: Converts BlogExample to blog_example, and minimalValue to minimal_value
131
     *
132
     * @param string $string String to be converted to lowercase underscore
133
     * @return string     lowercase_and_underscored_string
134
     */
135
    public static function camelCaseToLowerCaseUnderscored($string)
136
    {
137
        return strtolower(preg_replace('/(?<=\w)([A-Z])/', '_\\1', $string));
138
    }
139
140
    /**
141
     * Returns a given string with underscores as UpperCamelCase.
142
     * Example: Converts blog_example to BlogExample
143
     *
144
     * @param string $string String to be converted to camel case
145
     * @return string     UpperCamelCasedWord
146
     */
147 26
    public static function underscoredToUpperCamelCase($string)
148
    {
149 26
        return str_replace(' ', '',
150 26
            ucwords(str_replace('_', ' ', strtolower($string))));
151
    }
152
153
    /**
154
     * Shortcut to retrieve the TypoScript configuration for EXT:solr
155
     * (plugin.tx_solr) from TSFE.
156
     *
157
     * @return TypoScriptConfiguration
158
     */
159 155
    public static function getSolrConfiguration()
160
    {
161 155
        $configurationManager = self::getConfigurationManager();
162
163 155
        return $configurationManager->getTypoScriptConfiguration();
164
    }
165
166
    /**
167
     * @return ConfigurationManager
168
     */
169 194
    private static function getConfigurationManager()
170
    {
171
        /** @var ConfigurationManager $configurationManager */
172 194
        $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
173 194
        return $configurationManager;
174
    }
175
176
    /**
177
     * Gets the Solr configuration for a specific root page id.
178
     * To be used from the backend.
179
     *
180
     * @param int $pageId Id of the (root) page to get the Solr configuration from.
181
     * @param bool $initializeTsfe Optionally initializes a full TSFE to get the configuration, defaults to FALSE
182
     * @param int $language System language uid, optional, defaults to 0
183
     * @return TypoScriptConfiguration The Solr configuration for the requested tree.
184
     */
185 63
    public static function getSolrConfigurationFromPageId(
186
        $pageId,
187
        $initializeTsfe = false,
188
        $language = 0
189
    ) {
190 63
        $rootPath = '';
191 63
        return self::getConfigurationFromPageId($pageId, $rootPath, $initializeTsfe, $language);
192
    }
193
194
    /**
195
     * Loads the TypoScript configuration for a given page id and language.
196
     * Language usage may be disabled to get the default TypoScript
197
     * configuration.
198
     *
199
     * @param int $pageId Id of the (root) page to get the Solr configuration from.
200
     * @param string $path The TypoScript configuration path to retrieve.
201
     * @param bool $initializeTsfe Optionally initializes a full TSFE to get the configuration, defaults to FALSE
202
     * @param int $language System language uid, optional, defaults to 0
203
     * @param bool $useTwoLevelCache Flag to enable the two level cache for the typoscript configuration array
204
     * @return TypoScriptConfiguration The Solr configuration for the requested tree.
205
     */
206 65
    public static function getConfigurationFromPageId(
207
        $pageId,
208
        $path,
209
        $initializeTsfe = false,
210
        $language = 0,
211
        $useTwoLevelCache = true
212
    ) {
213 65
        $pageId = self::getConfigurationPageIdToUse($pageId);
214
215 65
        static $configurationObjectCache = [];
216 65
        $cacheId = md5($pageId . '|' . $path . '|' . $language);
217 65
        if (isset($configurationObjectCache[$cacheId])) {
218 57
            return $configurationObjectCache[$cacheId];
219
        }
220
221
        // If we're on UID 0, we cannot retrieve a configuration currently.
222
        // getRootline() below throws an exception (since #typo3-60 )
223
        // as UID 0 cannot have any parent rootline by design.
224 65
        if ($pageId == 0) {
225 2
            return $configurationObjectCache[$cacheId] = self::buildTypoScriptConfigurationFromArray([], $pageId, $language, $path);
226
        }
227
228 64
        if ($useTwoLevelCache) {
229
            /** @var $cache TwoLevelCache */
230 64
            $cache = GeneralUtility::makeInstance(TwoLevelCache::class, 'tx_solr_configuration');
231 64
            $configurationArray = $cache->get($cacheId);
232
        }
233
234 64
        if (!empty($configurationArray)) {
235
            // we have a cache hit and can return it.
236
            return $configurationObjectCache[$cacheId] = self::buildTypoScriptConfigurationFromArray($configurationArray, $pageId, $language, $path);
237
        }
238
239
        // we have nothing in the cache. We need to build the configurationToUse
240 64
        $configurationArray = self::buildConfigurationArray($pageId, $path, $initializeTsfe, $language);
241
242 64
        if ($useTwoLevelCache && isset($cache)) {
243 64
            $cache->set($cacheId, $configurationArray);
244
        }
245
246 64
        return $configurationObjectCache[$cacheId] = self::buildTypoScriptConfigurationFromArray($configurationArray, $pageId, $language, $path);
247
    }
248
249
    /**
250
     * This method retrieves the closest pageId where a configuration is located, when this
251
     * feature is enabled.
252
     *
253
     * @param int $pageId
254
     * @return int
255
     */
256 65
    protected static function getConfigurationPageIdToUse($pageId)
257
    {
258 65
        $extensionConfiguration = GeneralUtility::makeInstance(ExtensionConfiguration::class);
259 65
        if ($extensionConfiguration->getIsUseConfigurationFromClosestTemplateEnabled()) {
260
            /** @var $configurationPageResolve ConfigurationPageResolver */
261
            $configurationPageResolver = GeneralUtility::makeInstance(ConfigurationPageResolver::class);
262
            $pageId = $configurationPageResolver->getClosestPageIdWithActiveTemplate($pageId);
263
            return $pageId;
264
        }
265 65
        return $pageId;
266
    }
267
268
    /**
269
     * Initializes a TSFE, if required and builds an configuration array, containing the solr configuration.
270
     *
271
     * @param integer $pageId
272
     * @param string $path
273
     * @param boolean $initializeTsfe
274
     * @param integer $language
275
     * @return array
276
     */
277 64
    protected static function buildConfigurationArray($pageId, $path, $initializeTsfe, $language)
278
    {
279 64
        if ($initializeTsfe) {
280 2
            self::initializeTsfe($pageId, $language);
281 2
            $configurationToUse = self::getConfigurationFromInitializedTSFE($path);
282
        } else {
283 64
            $configurationToUse = self::getConfigurationFromExistingTSFE($pageId, $path, $language);
284
        }
285
286 64
        return is_array($configurationToUse) ? $configurationToUse : [];
287
    }
288
289
    /**
290
     * Builds the configuration object from a config array and returns it.
291
     *
292
     * @param array $configurationToUse
293
     * @param int $pageId
294
     * @param int $languageId
295
     * @param string $typoScriptPath
296
     * @return TypoScriptConfiguration
297
     */
298 65
    protected static function buildTypoScriptConfigurationFromArray(array $configurationToUse, $pageId, $languageId, $typoScriptPath)
299
    {
300 65
        $configurationManager = self::getConfigurationManager();
301 65
        return $configurationManager->getTypoScriptConfiguration($configurationToUse, $pageId, $languageId, $typoScriptPath);
302
    }
303
304
    /**
305
     * This function is used to retrieve the configuration from a previous initialized TSFE
306
     * (see: getConfigurationFromPageId)
307
     *
308
     * @param string $path
309
     * @return mixed
310
     */
311 2
    private static function getConfigurationFromInitializedTSFE($path)
312
    {
313
        /** @var $tmpl ExtendedTemplateService */
314 2
        $tmpl = GeneralUtility::makeInstance(ExtendedTemplateService::class);
315 2
        $configuration = $tmpl->ext_getSetup($GLOBALS['TSFE']->tmpl->setup, $path);
316 2
        $configurationToUse = $configuration[0];
317 2
        return $configurationToUse;
318
    }
319
320
    /**
321
     * This function is used to retrieve the configuration from an existing TSFE instance
322
     * @param $pageId
323
     * @param $path
324
     * @param $language
325
     * @return mixed
326
     */
327 64
    private static function getConfigurationFromExistingTSFE($pageId, $path, $language)
328
    {
329 64
        if (is_int($language)) {
330 64
            GeneralUtility::_GETset($language, 'L');
331
        }
332
333
            /** @var $pageSelect PageRepository */
334 64
        $pageSelect = GeneralUtility::makeInstance(PageRepository::class);
335 64
        $rootLine = $pageSelect->getRootLine($pageId);
336
337 64
        $initializedTsfe = false;
338 64
        $initializedPageSelect = false;
339 64
        if (empty($GLOBALS['TSFE']->sys_page)) {
340 54
            if (empty($GLOBALS['TSFE'])) {
341 54
                $GLOBALS['TSFE'] = new \stdClass();
342 54
                $GLOBALS['TSFE']->tmpl = new \stdClass();
343 54
                $GLOBALS['TSFE']->tmpl->rootLine = $rootLine;
344 54
                $GLOBALS['TSFE']->sys_page = $pageSelect;
345 54
                $GLOBALS['TSFE']->id = $pageId;
346 54
                $GLOBALS['TSFE']->tx_solr_initTsfe = 1;
347 54
                $initializedTsfe = true;
348
            }
349
350 54
            $GLOBALS['TSFE']->sys_page = $pageSelect;
351 54
            $initializedPageSelect = true;
352
        }
353
            /** @var $tmpl ExtendedTemplateService */
354 64
        $tmpl = GeneralUtility::makeInstance(ExtendedTemplateService::class);
355 64
        $tmpl->tt_track = false; // Do not log time-performance information
356 64
        $tmpl->init();
357 64
        $tmpl->runThroughTemplates($rootLine); // This generates the constants/config + hierarchy info for the template.
358 64
        $tmpl->generateConfig();
359
360 64
        $getConfigurationFromInitializedTSFEAndWriteToCache = $tmpl->ext_getSetup($tmpl->setup, $path);
361 64
        $configurationToUse = $getConfigurationFromInitializedTSFEAndWriteToCache[0];
362
363 64
        if ($initializedPageSelect) {
364 54
            $GLOBALS['TSFE']->sys_page = null;
365
        }
366 64
        if ($initializedTsfe) {
367 54
            unset($GLOBALS['TSFE']);
368
        }
369 64
        return $configurationToUse;
370
    }
371
372
    /**
373
     * Initializes the TSFE for a given page ID and language.
374
     *
375
     * @param int $pageId The page id to initialize the TSFE for
376
     * @param int $language System language uid, optional, defaults to 0
377
     * @param bool $useCache Use cache to reuse TSFE
378
     * @return void
379
     */
380 15
    public static function initializeTsfe(
381
        $pageId,
382
        $language = 0,
383
        $useCache = true
384
    ) {
385 15
        static $tsfeCache = [];
386
387
        // resetting, a TSFE instance with data from a different page Id could be set already
388 15
        unset($GLOBALS['TSFE']);
389
390 15
        $cacheId = $pageId . '|' . $language;
391
392 15
        if (!is_object($GLOBALS['TT'])) {
393 15
            $GLOBALS['TT'] = GeneralUtility::makeInstance(NullTimeTracker::class);
394
        }
395
396 15
        if (!isset($tsfeCache[$cacheId]) || !$useCache) {
397 15
            GeneralUtility::_GETset($language, 'L');
398
399 15
            $GLOBALS['TSFE'] = GeneralUtility::makeInstance(TypoScriptFrontendController::class,
400 15
                $GLOBALS['TYPO3_CONF_VARS'], $pageId, 0);
401
402
            // for certain situations we need to trick TSFE into granting us
403
            // access to the page in any case to make getPageAndRootline() work
404
            // see http://forge.typo3.org/issues/42122
405 15
            $pageRecord = BackendUtility::getRecord('pages', $pageId, 'fe_group');
406 15
            $groupListBackup = $GLOBALS['TSFE']->gr_list;
407 15
            $GLOBALS['TSFE']->gr_list = $pageRecord['fe_group'];
408
409 15
            $GLOBALS['TSFE']->sys_page = GeneralUtility::makeInstance(PageRepository::class);
410 15
            $GLOBALS['TSFE']->getPageAndRootline();
411
412
            // restore gr_list
413 15
            $GLOBALS['TSFE']->gr_list = $groupListBackup;
414
415 15
            $GLOBALS['TSFE']->initTemplate();
416 15
            $GLOBALS['TSFE']->forceTemplateParsing = true;
417 15
            $GLOBALS['TSFE']->initFEuser();
418 15
            $GLOBALS['TSFE']->initUserGroups();
419
            //  $GLOBALS['TSFE']->getCompressedTCarray(); // seems to cause conflicts sometimes
420
421 15
            $GLOBALS['TSFE']->no_cache = true;
422 15
            $GLOBALS['TSFE']->tmpl->start($GLOBALS['TSFE']->rootLine);
423 15
            $GLOBALS['TSFE']->no_cache = false;
424 15
            $GLOBALS['TSFE']->getConfigArray();
425
426 15
            $GLOBALS['TSFE']->settingLanguage();
427 15
            if (!$useCache) {
428
                $GLOBALS['TSFE']->settingLocale();
429
            }
430
431 15
            $GLOBALS['TSFE']->newCObj();
432 15
            $GLOBALS['TSFE']->absRefPrefix = self::getAbsRefPrefixFromTSFE($GLOBALS['TSFE']);
433 15
            $GLOBALS['TSFE']->calculateLinkVars();
434
435 15
            if ($useCache) {
436 15
                $tsfeCache[$cacheId] = $GLOBALS['TSFE'];
437
            }
438
        }
439
440 15
        if ($useCache) {
441 15
            $GLOBALS['TSFE'] = $tsfeCache[$cacheId];
442 15
            $GLOBALS['TSFE']->settingLocale();
443
        }
444 15
    }
445
446
    /**
447
     * Check if record ($table, $uid) is a workspace record
448
     *
449
     * @param string $table The table the record belongs to
450
     * @param int $uid The record's uid
451
     * @return bool TRUE if the record is in a draft workspace, FALSE if it's a LIVE record
452
     */
453 38
    public static function isDraftRecord($table, $uid)
454
    {
455 38
        $isWorkspaceRecord = false;
456
457 38
        if ((ExtensionManagementUtility::isLoaded('workspaces')) && (BackendUtility::isTableWorkspaceEnabled($table))) {
458
            $record = BackendUtility::getRecord($table, $uid, 'pid, t3ver_state');
459
460
            if ($record['pid'] == '-1' || $record['t3ver_state'] > 0) {
461
                $isWorkspaceRecord = true;
462
            }
463
        }
464
465 38
        return $isWorkspaceRecord;
466
    }
467
468
    /**
469
     * Checks whether a record is a localization overlay.
470
     *
471
     * @param string $tableName The record's table name
472
     * @param array $record The record to check
473
     * @return bool TRUE if the record is a language overlay, FALSE otherwise
474
     */
475 31
    public static function isLocalizedRecord($tableName, array $record)
476
    {
477 31
        $isLocalizedRecord = false;
478
479 31
        if (isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])) {
480 6
            $translationOriginalPointerField = $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'];
481
482 6
            if ($record[$translationOriginalPointerField] > 0) {
483 3
                $isLocalizedRecord = true;
484
            }
485
        }
486
487 31
        return $isLocalizedRecord;
488
    }
489
490
    /**
491
     * Check if the page type of a page record is allowed
492
     *
493
     * @param array $pageRecord The pages database row
494
     * @param string $configurationName The name of the configuration to use.
495
     *
496
     * @return bool TRUE if the page type is allowed, otherwise FALSE
497
     */
498 30
    public static function isAllowedPageType(array $pageRecord, $configurationName = 'pages')
499
    {
500 30
        $isAllowedPageType = false;
501 30
        $configurationName = is_null($configurationName) ? 'pages' : $configurationName;
502 30
        $allowedPageTypes = self::getAllowedPageTypes($pageRecord['uid'], $configurationName);
503
504 30
        if (in_array($pageRecord['doktype'], $allowedPageTypes)) {
505 29
            $isAllowedPageType = true;
506
        }
507
508 30
        return $isAllowedPageType;
509
    }
510
511
    /**
512
     * Get allowed page types
513
     *
514
     * @param int $pageId Page ID
515
     * @param string $configurationName The name of the configuration to use.
516
     *
517
     * @return array Allowed page types to compare to a doktype of a page record
518
     */
519 30
    public static function getAllowedPageTypes($pageId, $configurationName = 'pages')
520
    {
521 30
        $rootPath = '';
522 30
        $configuration = self::getConfigurationFromPageId($pageId, $rootPath);
523 30
        return $configuration->getIndexQueueAllowedPageTypesArrayByConfigurationName($configurationName);
524
    }
525
526
    /**
527
     * Method to check if a page exists.
528
     *
529
     * @param int $pageId
530
     * @return bool
531
     */
532
    public static function pageExists($pageId)
533
    {
534
        $page = BackendUtility::getRecord('pages', (int)$pageId, 'uid');
535
536
        if (!is_array($page) || $page['uid'] != $pageId) {
537
            return false;
538
        }
539
540
        return true;
541
    }
542
543
    /**
544
     * Resolves the configured absRefPrefix to a valid value and resolved if absRefPrefix
545
     * is set to "auto".
546
     *
547
     * @param TypoScriptFrontendController $TSFE
548
     * @return string
549
     */
550 15
    public static function getAbsRefPrefixFromTSFE(TypoScriptFrontendController $TSFE)
551
    {
552 15
        $absRefPrefix = '';
553 15
        if (empty($TSFE->config['config']['absRefPrefix'])) {
554 12
            return $absRefPrefix;
555
        }
556
557 3
        $absRefPrefix = trim($TSFE->config['config']['absRefPrefix']);
558 3
        if ($absRefPrefix === 'auto') {
559 1
            $absRefPrefix = GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
560
        }
561
562 3
        return $absRefPrefix;
563
    }
564
565
    /**
566
     * This function can be used to check if one of the strings in needles is
567
     * contained in the haystack.
568
     *
569
     *
570
     * Example:
571
     *
572
     * haystack: the brown fox
573
     * needles: ['hello', 'world']
574
     * result: false
575
     *
576
     * haystack: the brown fox
577
     * needles: ['is', 'fox']
578
     * result: true
579
     *
580
     * @param string $haystack
581
     * @param array $needles
582
     * @return bool
583
     */
584 35
    public static function containsOneOfTheStrings($haystack, array $needles)
585
    {
586 35
        foreach ($needles as $needle) {
587 35
            $position = strpos($haystack, $needle);
588 35
            if ($position !== false) {
589 3
                return true;
590
            }
591
        }
592
593 32
        return false;
594
    }
595
}
596