Completed
Push — master ( 4904bb...48f77b )
by Timo
11s
created

ConnectionManager::getLanguageName()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3.0067

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 10
cts 11
cp 0.9091
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 11
nc 3
nop 1
crap 3.0067
1
<?php
2
namespace ApacheSolrForTypo3\Solr;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2010-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\System\Page\Rootline;
28
use TYPO3\CMS\Backend\Routing\UriBuilder;
29
use TYPO3\CMS\Backend\Toolbar\ClearCacheActionsHookInterface;
30
use TYPO3\CMS\Core\Imaging\Icon;
31
use TYPO3\CMS\Core\Imaging\IconFactory;
32
use TYPO3\CMS\Core\Registry;
33
use TYPO3\CMS\Core\SingletonInterface;
34
use TYPO3\CMS\Core\TypoScript\ExtendedTemplateService;
35
use TYPO3\CMS\Core\Utility\GeneralUtility;
36
use TYPO3\CMS\Frontend\Page\PageRepository;
37
38
/**
39
 * A class to easily create a connection to a Solr server.
40
 *
41
 * Internally keeps track of already existing connections and makes sure that no
42
 * duplicate connections are created.
43
 *
44
 * @author Ingo Renner <[email protected]>
45
 */
46
class ConnectionManager implements SingletonInterface, ClearCacheActionsHookInterface
47
{
48
49
    /**
50
     * @var array
51
     */
52
    protected static $connections = [];
53
54
    /**
55
     * Gets a Solr connection.
56
     *
57
     * Instead of generating a new connection with each call, connections are
58
     * kept and checked whether the requested connection already exists. If a
59
     * connection already exists, it's reused.
60
     *
61
     * @param string $host Solr host (optional)
62
     * @param int $port Solr port (optional)
63
     * @param string $path Solr path (optional)
64
     * @param string $scheme Solr scheme, defaults to http, can be https (optional)
65
     * @param string $username Solr user name (optional)
66
     * @param string $password Solr password (optional)
67
     * @return SolrService A solr connection.
68
     */
69
70 68
    public function getConnection($host = '', $port = 8983, $path = '/solr/', $scheme = 'http', $username = '', $password = '')
71
    {
72 68
        if (empty($host)) {
73 1
            GeneralUtility::devLog(
74
                'ApacheSolrForTypo3\Solr\ConnectionManager::getConnection() called with empty
75
                host parameter. Using configuration from TSFE, might be
76
                inaccurate. Always provide a host or use the getConnectionBy*
77 1
                methods.',
78 1
                'solr',
79 1
                2
80
            );
81
82 1
            $configuration = Util::getSolrConfiguration();
83 1
            $host = $configuration->getSolrHost();
84 1
            $port = $configuration->getSolrPort();
85 1
            $path = $configuration->getSolrPath();
86 1
            $scheme = $configuration->getSolrScheme();
87 1
            $username = $configuration->getSolrUsername();
88 1
            $password = $configuration->getSolrPassword();
89
        }
90
91 68
        $connectionHash = md5($scheme . '://' . $host . $port . $path . $username . $password);
92
93 68
        if (!isset(self::$connections[$connectionHash])) {
94 68
            $connection = GeneralUtility::makeInstance(SolrService::class, $host, $port, $path, $scheme);
95 67
            if ($username !== '') {
96 62
                $connection->setAuthenticationCredentials($username, $password);
97
            }
98
99 67
            self::$connections[$connectionHash] = $connection;
100
        }
101
102 67
        return self::$connections[$connectionHash];
103
    }
104
105
    /**
106
     * Creates a solr configuration from the configuration array and returns it.
107
     *
108
     * @param array $config The solr configuration array
109
     * @return SolrService
110
     */
111 65
    protected function getConnectionFromConfiguration(array $config)
112
    {
113 65
        return $this->getConnection(
114 65
            $config['solrHost'],
115 65
            $config['solrPort'],
116 65
            $config['solrPath'],
117 65
            $config['solrScheme'],
118 65
            $config['solrUsername'],
119 65
            $config['solrPassword']
120
        );
121
    }
122
123
    /**
124
     * Gets a Solr configuration for a page ID.
125
     *
126
     * @param int $pageId A page ID.
127
     * @param int $language The language ID to get the connection for as the path may differ. Optional, defaults to 0.
128
     * @param string $mount Comma list of MountPoint parameters
129
     * @return array A solr configuration.
130
     * @throws NoSolrConnectionFoundException
131
     */
132 56
    public function getConfigurationByPageId($pageId, $language = 0, $mount = '')
133
    {
134
        // find the root page
135 56
        $pageSelect = GeneralUtility::makeInstance(PageRepository::class);
136
137
        /** @var Rootline $rootLine */
138 56
        $rootLine = GeneralUtility::makeInstance(Rootline::class, $pageSelect->getRootLine($pageId, $mount));
139 56
        $siteRootPageId = $rootLine->getRootPageId();
140
141
        try {
142 56
            $solrConfiguration = $this->getConfigurationByRootPageId($siteRootPageId, $language);
143 1
        } catch (NoSolrConnectionFoundException $nscfe) {
144
            /* @var $noSolrConnectionException NoSolrConnectionFoundException */
145 1
            $noSolrConnectionException = GeneralUtility::makeInstance(
146 1
                NoSolrConnectionFoundException::class,
147 1
                $nscfe->getMessage() . ' Initial page used was [' . $pageId . ']',
148 1
                1275399922
149
            );
150 1
            $noSolrConnectionException->setPageId($pageId);
151
152 1
            throw $noSolrConnectionException;
153
        }
154
155 55
        return $solrConfiguration;
156
    }
157
158
    /**
159
     * Gets a Solr connection for a page ID.
160
     *
161
     * @param int $pageId A page ID.
162
     * @param int $language The language ID to get the connection for as the path may differ. Optional, defaults to 0.
163
     * @param string $mount Comma list of MountPoint parameters
164
     * @return SolrService A solr connection.
165
     * @throws NoSolrConnectionFoundException
166
     */
167 56
    public function getConnectionByPageId($pageId, $language = 0, $mount = '')
168
    {
169 56
        $solrServer = $this->getConfigurationByPageId($pageId, $language, $mount);
170 55
        $solrConnection = $this->getConnectionFromConfiguration($solrServer);
171 55
        return $solrConnection;
172
    }
173
174
    /**
175
     * Gets a Solr configuration for a root page ID.
176
     *
177
     * @param int $pageId A root page ID.
178
     * @param int $language The language ID to get the configuration for as the path may differ. Optional, defaults to 0.
179
     * @return array A solr configuration.
180
     * @throws NoSolrConnectionFoundException
181
     */
182 57
    public function getConfigurationByRootPageId($pageId, $language = 0)
183
    {
184 57
        $connectionKey = $pageId . '|' . $language;
185 57
        $solrServers = $this->getAllConfigurations();
186
187 57
        if (isset($solrServers[$connectionKey])) {
188 55
            $solrConfiguration = $solrServers[$connectionKey];
189
        } else {
190
            /* @var $noSolrConnectionException NoSolrConnectionFoundException */
191 2
            $noSolrConnectionException = GeneralUtility::makeInstance(
192 2
                NoSolrConnectionFoundException::class,
193
                'Could not find a Solr connection for root page ['
194 2
                . $pageId . '] and language [' . $language . '].',
195 2
                1275396474
196
            );
197 2
            $noSolrConnectionException->setRootPageId($pageId);
198 2
            $noSolrConnectionException->setLanguageId($language);
199
200 2
            throw $noSolrConnectionException;
201
        }
202
203 55
        return $solrConfiguration;
204
    }
205
206
    /**
207
     * Gets a Solr connection for a root page ID.
208
     *
209
     * @param int $pageId A root page ID.
210
     * @param int $language The language ID to get the connection for as the path may differ. Optional, defaults to 0.
211
     * @return SolrService A solr connection.
212
     * @throws NoSolrConnectionFoundException
213
     */
214 6
    public function getConnectionByRootPageId($pageId, $language = 0)
215
    {
216 6
        $config = $this->getConfigurationByRootPageId($pageId, $language);
217 6
        $solrConnection = $this->getConnectionFromConfiguration($config);
218
219 6
        return $solrConnection;
220
    }
221
222
    /**
223
     * Gets all connection configurations found.
224
     *
225
     * @return array An array of connection configurations.
226
     */
227 67
    public function getAllConfigurations()
228
    {
229
        /** @var $registry Registry */
230 67
        $registry = GeneralUtility::makeInstance(Registry::class);
231 67
        $solrConfigurations = $registry->get('tx_solr', 'servers', []);
232
233 67
        return $solrConfigurations;
234
    }
235
236
    /**
237
     * Stores the connections in the registry.
238
     *
239
     * @param array $solrConfigurations
240
     */
241 3
    protected function setAllConfigurations(array $solrConfigurations)
242
    {
243
        /** @var $registry Registry */
244 3
        $registry = GeneralUtility::makeInstance(Registry::class);
245 3
        $registry->set('tx_solr', 'servers', $solrConfigurations);
246 3
    }
247
248
    /**
249
     * Gets all connections found.
250
     *
251
     * @return SolrService[] An array of initialized ApacheSolrForTypo3\Solr\SolrService connections
252
     */
253 6
    public function getAllConnections()
254
    {
255 6
        $connections = [];
256
257 6
        $solrConfigurations = $this->getAllConfigurations();
258 6
        foreach ($solrConfigurations as $solrConfiguration) {
259 6
            $connections[] = $this->getConnectionFromConfiguration($solrConfiguration);
260
        }
261
262 6
        return $connections;
263
    }
264
265
    /**
266
     * Gets all connection configurations for a given site.
267
     *
268
     * @param Site $site A TYPO3 site
269
     * @return array An array of Solr connection configurations for a site
270
     */
271 22
    public function getConfigurationsBySite(Site $site)
272
    {
273 22
        $solrConfigurations = [];
274
275 22
        $allConfigurations = $this->getAllConfigurations();
276 22
        foreach ($allConfigurations as $configuration) {
277 22
            if ($configuration['rootPageUid'] == $site->getRootPageId()) {
278 22
                $solrConfigurations[] = $configuration;
279
            }
280
        }
281
282 22
        return $solrConfigurations;
283
    }
284
285
    /**
286
     * Gets all connections configured for a given site.
287
     *
288
     * @param Site $site A TYPO3 site
289
     * @return SolrService[] An array of Solr connection objects (ApacheSolrForTypo3\Solr\SolrService)
290
     */
291 11
    public function getConnectionsBySite(Site $site)
292
    {
293 11
        $connections = [];
294
295 11
        $solrServers = $this->getConfigurationsBySite($site);
296 11
        foreach ($solrServers as $solrServer) {
297 11
            $connections[] = $this->getConnectionFromConfiguration($solrServer);
298
        }
299
300 11
        return $connections;
301
    }
302
303
    // updates
304
305
    /**
306
     * Adds a menu entry to the clear cache menu to detect Solr connections.
307
     *
308
     * @param array $cacheActions Array of CacheMenuItems
309
     * @param array $optionValues Array of AccessConfigurations-identifiers (typically  used by userTS with options.clearCache.identifier)
310
     */
311
    public function manipulateCacheActions(&$cacheActions, &$optionValues)
312
    {
313
        if ($GLOBALS['BE_USER']->isAdmin()) {
314
            $title = 'Initialize Solr connections';
315
            $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
316
317
            $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
318
            $cacheActions[] = [
319
                'id' => 'clearSolrConnectionCache',
320
                'title' => $title,
321
                'href' =>  $uriBuilder->buildUriFromRoute('ajax_solr_updateConnections'),
322
                'icon' => $iconFactory->getIcon('extensions-solr-module-initsolrconnections', Icon::SIZE_SMALL)
323
            ];
324
            $optionValues[] = 'clearSolrConnectionCache';
325
        }
326
    }
327
328
    /**
329
     * Updates the connections in the registry.
330
     *
331
     */
332 2
    public function updateConnections()
333
    {
334 2
        $solrConnections = $this->getConfiguredSolrConnections();
335 2
        $solrConnections = $this->filterDuplicateConnections($solrConnections);
336
337 2
        if (!empty($solrConnections)) {
338 2
            $this->setAllConfigurations($solrConnections);
339
        }
340 2
    }
341
342
    /**
343
     * Entrypoint for the ajax request
344
     */
345
    public function updateConnectionsInCacheMenu()
346
    {
347
        $this->updateConnections();
348
    }
349
350
    /**
351
     * Updates the Solr connections for a specific root page ID / site.
352
     *
353
     * @param int $rootPageId A site root page id
354
     */
355 1
    public function updateConnectionByRootPageId($rootPageId)
356
    {
357 1
        $systemLanguages = $this->getSystemLanguages();
358 1
        $rootPage = GeneralUtility::makeInstance(Site::class, $rootPageId)->getRootPage();
359
360 1
        $updatedSolrConnections = [];
361 1
        foreach ($systemLanguages as $languageId) {
362 1
            $connection = $this->getConfiguredSolrConnectionByRootPage($rootPage, $languageId);
363
364 1
            if (!empty($connection)) {
365 1
                $updatedSolrConnections[$connection['connectionKey']] = $connection;
366
            }
367
        }
368
369 1
        $solrConnections = $this->getAllConfigurations();
370 1
        $solrConnections = array_merge($solrConnections, $updatedSolrConnections);
371 1
        $solrConnections = $this->filterDuplicateConnections($solrConnections);
372 1
        $this->setAllConfigurations($solrConnections);
373 1
    }
374
375
    /**
376
     * Finds the configured Solr connections. Also respects multi-site
377
     * environments.
378
     *
379
     * @return array An array with connections, each connection with keys rootPageTitle, rootPageUid, solrHost, solrPort, solrPath
380
     */
381 2
    protected function getConfiguredSolrConnections()
382
    {
383 2
        $configuredSolrConnections = [];
384
385
        // find website roots and languages for this installation
386 2
        $rootPages = $this->getRootPages();
387 2
        $languages = $this->getSystemLanguages();
388
389
        // find solr configurations and add them as function menu entries
390 2
        foreach ($rootPages as $rootPage) {
391 2
            foreach ($languages as $languageId) {
392 2
                $connection = $this->getConfiguredSolrConnectionByRootPage($rootPage,
393
                    $languageId);
394
395 2
                if (!empty($connection)) {
396 2
                    $configuredSolrConnections[$connection['connectionKey']] = $connection;
397
                }
398
            }
399
        }
400
401 2
        return $configuredSolrConnections;
402
    }
403
404
    /**
405
     * Gets the configured Solr connection for a specific root page and language ID.
406
     *
407
     * @param array $rootPage A root page record with at least title and uid
408
     * @param int $languageId ID of a system language
409
     * @return array A solr connection configuration.
410
     */
411 3
    protected function getConfiguredSolrConnectionByRootPage(array $rootPage, $languageId)
412
    {
413 3
        $connection = [];
414
415 3
        $languageId = intval($languageId);
416 3
        GeneralUtility::_GETset($languageId, 'L');
417 3
        $connectionKey = $rootPage['uid'] . '|' . $languageId;
418
419 3
        $pageSelect = GeneralUtility::makeInstance(PageRepository::class);
420 3
        $rootLine = $pageSelect->getRootLine($rootPage['uid']);
421
422 3
        $tmpl = GeneralUtility::makeInstance(ExtendedTemplateService::class);
423 3
        $tmpl->tt_track = false; // Do not log time-performance information
424 3
        $tmpl->init();
425 3
        $tmpl->runThroughTemplates($rootLine); // This generates the constants/config + hierarchy info for the template.
426
427
        // fake micro TSFE to get correct condition parsing
428 3
        $GLOBALS['TSFE'] = new \stdClass();
429 3
        $GLOBALS['TSFE']->tmpl = new \stdClass();
430 3
        $GLOBALS['TSFE']->cObjectDepthCounter = 50;
431 3
        $GLOBALS['TSFE']->tmpl->rootLine = $rootLine;
432 3
        $GLOBALS['TSFE']->sys_page = $pageSelect;
433 3
        $GLOBALS['TSFE']->id = $rootPage['uid'];
434 3
        $GLOBALS['TSFE']->page = $rootPage;
435
436 3
        $tmpl->generateConfig();
437 3
        $GLOBALS['TSFE']->tmpl->setup = $tmpl->setup;
438
439 3
        $configuration = Util::getSolrConfigurationFromPageId($rootPage['uid'], false, $languageId);
440
441 3
        $solrIsEnabledAndConfigured = $configuration->getEnabled() && $configuration->getSolrHasConnectionConfiguration();
442 3
        if (!$solrIsEnabledAndConfigured) {
443
            return $connection;
444
        }
445
446
        $connection = [
447 3
            'connectionKey' => $connectionKey,
448 3
            'rootPageTitle' => $rootPage['title'],
449 3
            'rootPageUid' => $rootPage['uid'],
450 3
            'solrScheme' => $configuration->getSolrScheme(),
451 3
            'solrHost' => $configuration->getSolrHost(),
452 3
            'solrPort' => $configuration->getSolrPort(),
453 3
            'solrPath' => $configuration->getSolrPath(),
454 3
            'solrUsername' => $configuration->getSolrUsername(),
455 3
            'solrPassword' => $configuration->getSolrPassword(),
456
457 3
            'language' => $languageId
458
        ];
459
460 3
        $connection['label'] = $this->buildConnectionLabel($connection);
461 3
        return $connection;
462
    }
463
464
    /**
465
     * Gets the language name for a given language ID.
466
     *
467
     * @param int $languageId language ID
468
     * @return string Language name
469
     */
470 3
    protected function getLanguageName($languageId)
471
    {
472 3
        $languageName = '';
473
474 3
        $language = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
475 3
            'uid, title',
476 3
            'sys_language',
477 3
            'uid = ' . (integer)$languageId
478
        );
479
480 3
        if (count($language)) {
481
            $languageName = $language[0]['title'];
482 3
        } elseif ($languageId == 0) {
483 3
            $languageName = 'default';
484
        }
485
486 3
        return $languageName;
487
    }
488
489
    /**
490
     * Creates a human readable label from the connections' configuration.
491
     *
492
     * @param array $connection Connection configuration
493
     * @return string Connection label
494
     */
495 3
    protected function buildConnectionLabel(array $connection)
496
    {
497 3
        $connectionLabel = $connection['rootPageTitle']
498 3
            . ' (pid: ' . $connection['rootPageUid']
499 3
            . ', language: ' . $this->getLanguageName($connection['language'])
500 3
            . ') - '
501
#			. $connection['solrScheme'] . '://'
502 3
            . $connection['solrHost'] . ':'
503 3
            . $connection['solrPort']
504 3
            . $connection['solrPath'];
505
506 3
        return $connectionLabel;
507
    }
508
509
    /**
510
     * Filters duplicate connections. When detecting the configured connections
511
     * this is done with a little brute force by simply combining all root pages
512
     * with all languages, this method filters out the duplicates.
513
     *
514
     * @param array $connections An array of unfiltered connections, containing duplicates
515
     * @return array An array with connections, no duplicates.
516
     */
517 3
    protected function filterDuplicateConnections(array $connections)
518
    {
519 3
        $hashedConnections = [];
520 3
        $filteredConnections = [];
521
522
        // array_unique() doesn't work on multi dimensional arrays, so we need to flatten it first
523 3
        foreach ($connections as $key => $connection) {
524 3
            unset($connection['language']);
525 3
            $connectionHash = md5(implode('|', $connection));
526 3
            $hashedConnections[$key] = $connectionHash;
527
        }
528
529 3
        $hashedConnections = array_unique($hashedConnections);
530
531 3
        foreach ($hashedConnections as $key => $hash) {
532 3
            $filteredConnections[$key] = $connections[$key];
533
        }
534
535 3
        return $filteredConnections;
536
    }
537
538
    /**
539
     * Finds the system's configured languages.
540
     *
541
     * @return array An array of language IDs
542
     */
543 3
    protected function getSystemLanguages()
544
    {
545 3
        $languages = [0];
546
547 3
        $languageRecords = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
548 3
            'uid',
549 3
            'sys_language',
550 3
            'hidden = 0'
551
        );
552
553 3
        if (!is_array($languageRecords)) {
554
            return $languages;
555
        }
556
557 3
        foreach ($languageRecords as $languageRecord) {
558
            $languages[] = $languageRecord['uid'];
559
        }
560 3
        return $languages;
561
    }
562
563
    /**
564
     * Gets the site's root pages. The "Is root of website" flag must be set,
565
     * which usually is the case for pages with pid = 0.
566
     *
567
     * @return array An array of (partial) root page records, containing the uid and title fields
568
     */
569 2
    protected function getRootPages()
570
    {
571 2
        $rootPages = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
572 2
            'uid, title',
573 2
            'pages',
574 2
            'is_siteroot = 1 AND deleted = 0 AND hidden = 0 AND pid != -1'
575
        );
576
577 2
        return $rootPages;
578
    }
579
}
580