Passed
Push — master ( 437b48...de73eb )
by Timo
04:54
created

firstDomainRecordFromLegacyDomainResolver()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 7
c 0
b 0
f 0
dl 0
loc 11
ccs 3
cts 3
cp 1
rs 10
cc 3
nc 3
nop 1
crap 3
1
<?php
2
3
namespace ApacheSolrForTypo3\Solr\Domain\Site;
4
5
/***************************************************************
6
 *  Copyright notice
7
 *
8
 *  (c) 2017 - Thomas Hohn <[email protected]>
9
 *  All rights reserved
10
 *
11
 *  This script is part of the TYPO3 project. The TYPO3 project is
12
 *  free software; you can redistribute it and/or modify
13
 *  it under the terms of the GNU General Public License as published by
14
 *  the Free Software Foundation; either version 3 of the License, or
15
 *  (at your option) any later version.
16
 *
17
 *  The GNU General Public License can be found at
18
 *  http://www.gnu.org/copyleft/gpl.html.
19
 *
20
 *  This script is distributed in the hope that it will be useful,
21
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 *  GNU General Public License for more details.
24
 *
25
 *  This copyright notice MUST APPEAR in all copies of the script!
26
 ***************************************************************/
27
28
use ApacheSolrForTypo3\Solr\Domain\Index\Queue\RecordMonitor\Helper\RootPageResolver;
29
use ApacheSolrForTypo3\Solr\System\Cache\TwoLevelCache;
30
use ApacheSolrForTypo3\Solr\System\Records\Pages\PagesRepository;
31
use ApacheSolrForTypo3\Solr\System\Records\SystemLanguage\SystemLanguageRepository;
32
use ApacheSolrForTypo3\Solr\System\Service\SiteService;
33
use ApacheSolrForTypo3\Solr\System\Util\SiteUtility;
34
use ApacheSolrForTypo3\Solr\Util;
35
use TYPO3\CMS\Backend\Utility\BackendUtility;
36
use TYPO3\CMS\Core\Registry;
37
use TYPO3\CMS\Core\Site\SiteFinder;
38
use TYPO3\CMS\Core\Utility\GeneralUtility;
39
use TYPO3\CMS\Core\Utility\RootlineUtility;
40
use TYPO3\CMS\Frontend\Compatibility\LegacyDomainResolver;
41
42
/**
43
 * SiteRepository
44
 *
45
 * Responsible to retrieve instances of Site objects
46
 *
47
 * @author Thomas Hohn <[email protected]>
48
 */
49
class SiteRepository
50
{
51
    /**
52
     * Rootpage resolver
53
     *
54
     * @var RootPageResolver
55
     */
56
    protected $rootPageResolver;
57
58
    /**
59
     * @var TwoLevelCache
60
     */
61
    protected $runtimeCache;
62
63
    /**
64
     * @var Registry
65
     */
66
    protected $registry;
67
68
    /**
69
     * @var SiteFinder
70
     */
71
    protected $siteFinder;
72
73
    /**
74
     * SiteRepository constructor.
75
     *
76
     * @param RootPageResolver|null $rootPageResolver
77
     * @param TwoLevelCache|null $twoLevelCache
78
     * @param Registry|null $registry
79
     * @param SiteFinder
80
     */
81 195
    public function __construct(RootPageResolver $rootPageResolver = null, TwoLevelCache $twoLevelCache = null, Registry $registry = null, SiteFinder $siteFinder = null)
82
    {
83 195
        $this->rootPageResolver = $rootPageResolver ?? GeneralUtility::makeInstance(RootPageResolver::class);
84 195
        $this->runtimeCache = $twoLevelCache ?? GeneralUtility::makeInstance(TwoLevelCache::class, /** @scrutinizer ignore-type */'cache_runtime');
85 195
        $this->registry = $registry ?? GeneralUtility::makeInstance(Registry::class);
86 195
        $this->siteFinder = $siteFinder ?? GeneralUtility::makeInstance(SiteFinder::class);
87 195
    }
88
89
    /**
90
     * Gets the Site for a specific page Id.
91
     *
92
     * @param int $pageId The page Id to get a Site object for.
93
     * @param string $mountPointIdentifier
94
     * @return SiteInterface Site for the given page Id.
95
     */
96 118
    public function getSiteByPageId($pageId, $mountPointIdentifier = '')
97
    {
98 118
        $rootPageId = $this->rootPageResolver->getRootPageId($pageId, false, $mountPointIdentifier);
99 118
        return $this->getSiteByRootPageId($rootPageId);
100
    }
101
102
    /**
103
     * Gets the Site for a specific root page Id.
104
     *
105
     * @param int $rootPageId Root page Id to get a Site object for.
106
     * @return SiteInterface Site for the given page Id.
107
     */
108 134
    public function getSiteByRootPageId($rootPageId)
109
    {
110 134
        $cacheId = 'SiteRepository' . '_' . 'getSiteByPageId' . '_' . $rootPageId;
111
112 134
        $methodResult = $this->runtimeCache->get($cacheId);
113 134
        if (!empty($methodResult)) {
114 97
            return $methodResult;
115
        }
116
117 134
        $methodResult = $this->buildSite($rootPageId);
118 127
        $this->runtimeCache->set($cacheId, $methodResult);
119
120 127
        return $methodResult;
121
    }
122
123
    /**
124
     * Returns the first available Site.
125
     *
126
     * @param bool $stopOnInvalidSite
127
     * @throws \Exception
128
     * @return Site
129
     */
130 24
    public function getFirstAvailableSite($stopOnInvalidSite = false)
131
    {
132 24
        $sites = $this->getAvailableSites($stopOnInvalidSite);
133 24
        return array_shift($sites);
134
    }
135
136
    /**
137
     * Gets all available TYPO3 sites with Solr configured.
138
     *
139
     * @param bool $stopOnInvalidSite
140
     * @throws \Exception
141
     * @return Site[] An array of availablesites
142
     */
143 79
    public function getAvailableSites($stopOnInvalidSite = false)
144
    {
145 79
        $cacheId = 'SiteRepository' . '_' . 'getAvailableSites';
146
147 79
        $methodResult = $this->runtimeCache->get($cacheId);
148 79
        if (!empty($methodResult)) {
149 16
            return $methodResult;
150
        }
151
152 79
        $legacySites = $this->getAvailableLegacySites($stopOnInvalidSite);
0 ignored issues
show
Deprecated Code introduced by
The function ApacheSolrForTypo3\Solr\...tAvailableLegacySites() has been deprecated: deprecated since EXT:solr 10 will be removed with EXT:solr 11 please use the site handling now ( Ignorable by Annotation )

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

152
        $legacySites = /** @scrutinizer ignore-deprecated */ $this->getAvailableLegacySites($stopOnInvalidSite);

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...
153 79
        $typo3ManagedSolrSites = $this->getAvailableTYPO3ManagedSites($stopOnInvalidSite);
154
155 79
        $methodResult = array_merge($legacySites, $typo3ManagedSolrSites);
156 79
        $this->runtimeCache->set($cacheId, $methodResult);
157
158 79
        return $methodResult;
159
    }
160
161
    /**
162
     * @deprecated deprecated since EXT:solr 10 will be removed with EXT:solr 11 please use the site handling now
163
     * @param bool $stopOnInvalidSite
164
     * @return array
165
     */
166 79
    protected function getAvailableLegacySites(bool $stopOnInvalidSite): array
167
    {
168 79
        $serversFromRegistry = $this->getSolrServersFromRegistry();
0 ignored issues
show
Deprecated Code introduced by
The function ApacheSolrForTypo3\Solr\...lrServersFromRegistry() has been deprecated: This method is only required for old solr based sites. ( Ignorable by Annotation )

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

168
        $serversFromRegistry = /** @scrutinizer ignore-deprecated */ $this->getSolrServersFromRegistry();

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...
169 79
        if(empty($serversFromRegistry)) {
170 6
            return [];
171
        }
172
173 76
        trigger_error('Method getAvailableLegacySites is deprecated since EXT:solr 10 and will be removed in v11, please use the site handling to configure EXT:solr', E_USER_DEPRECATED);
174 76
        $legacySites = [];
175 76
        foreach ($serversFromRegistry as $server) {
176 76
            if (isset($legacySites[$server['rootPageUid']])) {
177
                //get each site only once
178 6
                continue;
179
            }
180
181
            try {
182 76
                $legacySites[$server['rootPageUid']] = $this->buildSite($server['rootPageUid']);
183 4
            } catch (\InvalidArgumentException $e) {
184 4
                if ($stopOnInvalidSite) {
185
                    throw $e;
186
                }
187
            }
188
        }
189 76
        return $legacySites;
190
    }
191
192
193
    /**
194
     * @param bool $stopOnInvalidSite
195
     * @return array
196
     * @throws \Exception
197
     */
198 79
    protected function getAvailableTYPO3ManagedSites(bool $stopOnInvalidSite): array
199
    {
200 79
        $typo3ManagedSolrSites = [];
201 79
        $typo3Sites = $this->siteFinder->getAllSites();
202 79
        foreach ($typo3Sites as $typo3Site) {
203
            try {
204 3
                $rootPageId = $typo3Site->getRootPageId();
205 3
                if (isset($typo3ManagedSolrSites[$rootPageId])) {
206
                    //get each site only once
207
                    continue;
208
                }
209
210 3
                $typo3ManagedSolrSites[$rootPageId] = $this->buildSite($rootPageId);
211
212
            } catch (\Exception $e) {
213
                if ($stopOnInvalidSite) {
214
                    throw $e;
215
                }
216
            }
217
        }
218 79
        return $typo3ManagedSolrSites;
219
    }
220
221
    /**
222
     * Gets the system languages (IDs) for which Solr connections have been
223
     * configured.
224
     *
225
     * @param Site $site
226
     * @return array
227
     * @throws \ApacheSolrForTypo3\Solr\NoSolrConnectionFoundException
228
     * @deprecated use $site->getConnectionConfig
229
     * @todo check if method is still needed
230
     */
231
    public function getAllLanguages(Site $site)
232
    {
233
        trigger_error('Method getAllLanguages is deprecated since EXT:solr 10 and will be removed in v11, use  $site->getConnectionConfig instead', E_USER_DEPRECATED);
234
235
        $siteLanguages = [];
236
        foreach ($site->getAllSolrConnectionConfigurations() as $solrConnectionConfiguration) {
237
            $siteLanguages[] = $solrConnectionConfiguration['language'];
238
        }
239
240
        return $siteLanguages;
241
    }
242
243
    /**
244
     * Creates an instance of the Site object.
245
     *
246
     * @param integer $rootPageId
247
     * @throws \InvalidArgumentException
248
     * @return SiteInterface
249
     */
250 170
    protected function buildSite($rootPageId)
251
    {
252 170
        if (empty($rootPageId)) {
253
            throw new \InvalidArgumentException('Root page id can not be empty');
254
        }
255 170
        $rootPageRecord = (array)BackendUtility::getRecord('pages', $rootPageId);
256
257 170
        $this->validateRootPageRecord($rootPageId, $rootPageRecord);
258
259
        //@todo The handling of the legacy site can be removed in EXT:solr 11
260 159
        if (!SiteUtility::getIsSiteManagedSite($rootPageId)) {
261 159
            return $this->buildLegacySite($rootPageRecord);
0 ignored issues
show
Deprecated Code introduced by
The function ApacheSolrForTypo3\Solr\...tory::buildLegacySite() has been deprecated: buildLegacySite is deprecated and will be removed in EXT:solr 11. Please configure your system with the TYPO3 sitehandling ( Ignorable by Annotation )

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

261
            return /** @scrutinizer ignore-deprecated */ $this->buildLegacySite($rootPageRecord);

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...
262
        }
263
264
        return $this->buildTypo3ManagedSite($rootPageRecord);
265
    }
266
267
    /**
268
     * Retrieves the default language by the rootPageId of a site.
269
     *
270
     * @param int $rootPageId
271
     * @return int|mixed
272
     * @deprecated Use Site directly
273
     */
274 159
    protected function getDefaultLanguage($rootPageId)
275
    {
276 159
        trigger_error('Method getDefaultLanguage is deprecated since EXT:solr 10 and will be removed in v11, use  the site directly instead', E_USER_DEPRECATED);
277
278 159
        $siteDefaultLanguage = 0;
279
280 159
        $configuration = Util::getConfigurationFromPageId($rootPageId, 'config');
281
282 159
        $siteDefaultLanguage = $configuration->getValueByPathOrDefaultValue('sys_language_uid', $siteDefaultLanguage);
283
        // default language is set through default L GET parameter -> overruling config.sys_language_uid
284 159
        $siteDefaultLanguage = $configuration->getValueByPathOrDefaultValue('defaultGetVars.L', $siteDefaultLanguage);
285
286 159
        return $siteDefaultLanguage;
287
    }
288
289
    /**
290
     * Retrieves the configured solr servers from the registry.
291
     *
292
     * @deprecated This method is only required for old solr based sites.
293
     * @return array
294
     */
295 76
    protected function getSolrServersFromRegistry()
296
    {
297 76
        trigger_error('Method getSolrServersFromRegistry is deprecated since EXT:solr 10 and will be removed in v11, use sitehandling instead', E_USER_DEPRECATED);
298
299 76
        $servers = (array)$this->registry->get('tx_solr', 'servers', []);
300 76
        return $servers;
301
    }
302
303
    /**
304
     * @param $rootPageId
305
     * @deprecated This method is only required for old solr based sites.
306
     * @return NULL|string
307
     */
308 159
    protected function getDomainFromConfigurationOrFallbackToDomainRecord($rootPageId)
309
    {
310 159
        trigger_error('Method getDomainFromConfigurationOrFallbackToDomainRecord is deprecated since EXT:solr 10 and will be removed in v11, use sitehandling instead', E_USER_DEPRECATED);
311
312
        /** @var $siteService SiteService */
313 159
        $siteService = GeneralUtility::makeInstance(SiteService::class);
314 159
        $domain = $siteService->getFirstDomainForRootPage($rootPageId);
315 159
        if ($domain === '') {
316 158
            $rootlineUtility = GeneralUtility::makeInstance(RootlineUtility::class, $rootPageId);
317 158
            try {
318 158
                $rootLine = $rootlineUtility->get();
319 158
            } catch (\RuntimeException $e) {
320
                $rootLine = [];
321
            }
322 1
            $domain = $this->firstDomainRecordFromLegacyDomainResolver($rootLine);
323
            return (string)$domain;
324
        }
325
326
        return $domain;
327
    }
328
329 159
    /**
330
     * @param $rootLine
331
     * @return null|string
332 159
     */
333 159
    private function firstDomainRecordFromLegacyDomainResolver($rootLine)
334 159
    {
335
        trigger_error('BackendUtility::firstDomainRecord() will be removed in TYPO3 v10.0. Use sitehandling instead.', E_USER_DEPRECATED);
336
        $domainResolver = GeneralUtility::makeInstance(LegacyDomainResolver::class);
337
        foreach ($rootLine as $row) {
338
            $domain = $domainResolver->matchRootPageId($row['uid']);
339
            if (is_array($domain)) {
340
                return rtrim($domain['domainName'], '/');
341
            }
342 170
        }
343
        return null;
344 170
    }
345 10
346 10
    /**
347 10
     * @param string $domain
348
     * @return string
349
     */
350
    protected function getSiteHashForDomain($domain)
351 160
    {
352 2
        /** @var $siteHashService SiteHashService */
353 2
        $siteHashService = GeneralUtility::makeInstance(SiteHashService::class);
354 2
        $siteHash = $siteHashService->getSiteHashForDomain($domain);
355
        return $siteHash;
356
    }
357 159
358
    /**
359
     * @param int $rootPageId
360
     * @param array $rootPageRecord
361
     * @throws \InvalidArgumentException
362
     */
363
    protected function validateRootPageRecord($rootPageId, $rootPageRecord)
364
    {
365 159
        if (empty($rootPageRecord)) {
366
            throw new \InvalidArgumentException(
367 159
                'The rootPageRecord for the given rootPageRecord ID \'' . $rootPageId . '\' could not be found in the database and can therefore not be used as site root rootPageRecord.',
368
                1487326416
369 159
            );
370 159
        }
371 159
372 159
        if (!Site::isRootPage($rootPageRecord)) {
373 159
            throw new \InvalidArgumentException(
374 159
                'The rootPageRecord for the given rootPageRecord ID \'' . $rootPageId . '\' is not marked as root rootPageRecord and can therefore not be used as site root rootPageRecord.',
375
                1309272922
376 159
            );
377 159
        }
378
    }
379 159
380
    /**
381 159
     *
382
     * @deprecated buildLegacySite is deprecated and will be removed in EXT:solr 11. Please configure your system with the TYPO3 sitehandling
383 159
     * @param array $rootPageRecord
384
     * @return LegacySite
385 159
     */
386
    protected function buildLegacySite($rootPageRecord): LegacySite
387 159
    {
388
        trigger_error('You are using EXT:solr without sitehandling. This setup is deprecated and will be removed in EXT:solr 11', E_USER_DEPRECATED);
389 159
390
        $solrConfiguration = Util::getSolrConfigurationFromPageId($rootPageRecord['uid']);
391 159
        $domain = $this->getDomainFromConfigurationOrFallbackToDomainRecord($rootPageRecord['uid']);
0 ignored issues
show
Deprecated Code introduced by
The function ApacheSolrForTypo3\Solr\...allbackToDomainRecord() has been deprecated: This method is only required for old solr based sites. ( Ignorable by Annotation )

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

391
        $domain = /** @scrutinizer ignore-deprecated */ $this->getDomainFromConfigurationOrFallbackToDomainRecord($rootPageRecord['uid']);

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...
392
        $siteHash = $this->getSiteHashForDomain($domain);
393
        $defaultLanguage = $this->getDefaultLanguage($rootPageRecord['uid']);
0 ignored issues
show
Deprecated Code introduced by
The function ApacheSolrForTypo3\Solr\...y::getDefaultLanguage() has been deprecated: Use Site directly ( Ignorable by Annotation )

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

393
        $defaultLanguage = /** @scrutinizer ignore-deprecated */ $this->getDefaultLanguage($rootPageRecord['uid']);

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...
394
        $pageRepository = GeneralUtility::makeInstance(PagesRepository::class);
395
        $availableLanguageIds = GeneralUtility::makeInstance(SystemLanguageRepository::class)->findSystemLanguages();
396
397
        return GeneralUtility::makeInstance(
398
            LegacySite::class,
399
            /** @scrutinizer ignore-type */
400
            $solrConfiguration,
401
            /** @scrutinizer ignore-type */
402
            $rootPageRecord,
403
            /** @scrutinizer ignore-type */
404
            $domain,
405
            /** @scrutinizer ignore-type */
406
            $siteHash,
407
            /** @scrutinizer ignore-type */
408
            $pageRepository,
409
            /** @scrutinizer ignore-type */
410
            $defaultLanguage,
411
            /** @scrutinizer ignore-type */
412
            $availableLanguageIds
413
        );
414
    }
415
416
    /**
417
     * @param array $rootPageRecord
418
     * @return Typo3ManagedSite
419
     */
420
    protected function buildTypo3ManagedSite(array $rootPageRecord): Typo3ManagedSite
421
    {
422
        $solrConfiguration = Util::getSolrConfigurationFromPageId($rootPageRecord['uid']);
423
        /** @var \TYPO3\CMS\Core\Site\Entity\Site $typo3Site */
424
        $typo3Site = $this->siteFinder->getSiteByPageId($rootPageRecord['uid']);
425
        $domain = $typo3Site->getBase()->getHost();
426
427
        $siteHash = $this->getSiteHashForDomain($domain);
428
        $defaultLanguage = $typo3Site->getDefaultLanguage()->getLanguageId();
429
        $pageRepository = GeneralUtility::makeInstance(PagesRepository::class);
430
        $availableLanguageIds = array_map(function($language) {
431
            return $language->getLanguageId();
432
        }, $typo3Site->getAllLanguages());
433
434
        $solrConnectionConfigurations = [];
435
        foreach ($availableLanguageIds as $languageUid) {
436
            $solrConnectionConfigurations[$languageUid] = [
437
                'connectionKey' =>  $rootPageRecord['uid'] . '|' . $languageUid,
438
                'rootPageTitle' => $rootPageRecord['title'],
439
                'rootPageUid' => $rootPageRecord['uid'],
440
                'read' => [
441
                    'scheme' => SiteUtility::getConnectionProperty($typo3Site, 'scheme', $languageUid, 'read', 'http'),
442
                    'host' => SiteUtility::getConnectionProperty($typo3Site, 'host', $languageUid, 'read', 'localhost'),
443
                    'port' => (int)SiteUtility::getConnectionProperty($typo3Site, 'port', $languageUid, 'read', 8983),
444
                    // @todo: transform core to path
445
                    'path' =>
446
                        SiteUtility::getConnectionProperty($typo3Site, 'path', $languageUid, 'read', '/solr/') .
447
                        SiteUtility::getConnectionProperty($typo3Site, 'core', $languageUid, 'read', 'core_en') . '/' ,
448
                    'username' => SiteUtility::getConnectionProperty($typo3Site, 'username', $languageUid, 'read', ''),
449
                    'password' => SiteUtility::getConnectionProperty($typo3Site, 'password', $languageUid, 'read', ''),
450
                    'timeout' => SiteUtility::getConnectionProperty($typo3Site, 'timeout', $languageUid, 'read', 0)
451
                ],
452
                'write' => [
453
                    'scheme' => SiteUtility::getConnectionProperty($typo3Site, 'scheme', $languageUid, 'write', 'http'),
454
                    'host' => SiteUtility::getConnectionProperty($typo3Site, 'host', $languageUid, 'write', 'localhost'),
455
                    'port' => (int)SiteUtility::getConnectionProperty($typo3Site, 'port', $languageUid, 'write', 8983),
456
                    // @todo: transform core to path
457
                    'path' =>
458
                        SiteUtility::getConnectionProperty($typo3Site, 'path', $languageUid, 'read', '/solr/') .
459
                        SiteUtility::getConnectionProperty($typo3Site, 'core', $languageUid, 'read', 'core_en') . '/' ,
460
                    'username' => SiteUtility::getConnectionProperty($typo3Site, 'username', $languageUid, 'write', ''),
461
                    'password' => SiteUtility::getConnectionProperty($typo3Site, 'password', $languageUid, 'write', ''),
462
                    'timeout' => SiteUtility::getConnectionProperty($typo3Site, 'timeout', $languageUid, 'write', 0)
463
                ],
464
465
                'language' => $languageUid
466
            ];
467
        }
468
469
        return GeneralUtility::makeInstance(
470
            Typo3ManagedSite::class,
471
            /** @scrutinizer ignore-type */
472
            $solrConfiguration,
473
            /** @scrutinizer ignore-type */
474
            $rootPageRecord,
475
            /** @scrutinizer ignore-type */
476
            $domain,
477
            /** @scrutinizer ignore-type */
478
            $siteHash,
479
            /** @scrutinizer ignore-type */
480
            $pageRepository,
481
            /** @scrutinizer ignore-type */
482
            $defaultLanguage,
483
            /** @scrutinizer ignore-type */
484
            $availableLanguageIds,
485
            /** @scrutinizer ignore-type */
486
            $solrConnectionConfigurations,
487
            /** @scrutinizer ignore-type */
488
            $typo3Site
489
        );
490
    }
491
492
}
493