Test Failed
Push — develop ( bcf63f...70e2cc )
by Andrew
09:44
created

Sitemaps::init()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * SEOmatic plugin for Craft CMS 3.x
4
 *
5
 * A turnkey SEO implementation for Craft CMS that is comprehensive, powerful,
6
 * and flexible
7
 *
8
 * @link      https://nystudio107.com
9
 * @copyright Copyright (c) 2017 nystudio107
10
 */
11
12
namespace nystudio107\seomatic\services;
13
14
use nystudio107\seomatic\jobs\GenerateSitemap;
15
use nystudio107\seomatic\Seomatic;
16
use nystudio107\seomatic\base\FrontendTemplate;
17
use nystudio107\seomatic\base\SitemapInterface;
18
use nystudio107\seomatic\helpers\UrlHelper;
19
use nystudio107\seomatic\models\FrontendTemplateContainer;
20
use nystudio107\seomatic\models\SitemapIndexTemplate;
21
use nystudio107\seomatic\models\SitemapTemplate;
22
use nystudio107\seomatic\models\SitemapCustomTemplate;
23
24
use Craft;
25
use craft\base\Component;
26
use craft\base\Element;
27
use craft\base\ElementInterface;
28
use craft\errors\SiteNotFoundException;
29
use craft\events\RegisterUrlRulesEvent;
30
use craft\web\UrlManager;
31
32
use yii\base\Event;
33
use yii\base\Exception;
34
use yii\base\InvalidConfigException;
35
use yii\caching\TagDependency;
36
37
/**
38
 * @author    nystudio107
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @package tag
Loading history...
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
39
 * @package   Seomatic
40
 * @since     3.0.0
41
 */
42
class Sitemaps extends Component implements SitemapInterface
43
{
44
    // Constants
45
    // =========================================================================
46
47
    const SEOMATIC_SITEMAPINDEX_CONTAINER = Seomatic::SEOMATIC_HANDLE.SitemapIndexTemplate::TEMPLATE_TYPE;
48
49
    const SEOMATIC_SITEMAP_CONTAINER = Seomatic::SEOMATIC_HANDLE.SitemapTemplate::TEMPLATE_TYPE;
50
51
    const SEOMATIC_SITEMAPCUSTOM_CONTAINER = Seomatic::SEOMATIC_HANDLE.SitemapCustomTemplate::TEMPLATE_TYPE;
52
53
    const SEARCH_ENGINE_SUBMISSION_URLS = [
54
        'google' => 'http://www.google.com/webmasters/sitemaps/ping?sitemap=',
55
        'bing' => 'http://www.bing.com/ping?sitemap=',
56
    ];
57
58
    // Protected Properties
59
    // =========================================================================
60
61
    /**
62
     * @var FrontendTemplateContainer
63
     */
64
    protected $sitemapTemplateContainer;
65
66
    // Public Methods
67
    // =========================================================================
68
69
    /**
0 ignored issues
show
Coding Style introduced by
Doc comment is empty
Loading history...
70
     *
71
     */
72
    public function init()
73
    {
74
        parent::init();
75
    }
76
77
    /**
78
     * Load in the sitemap frontend template containers
79
     */
80
    public function loadSitemapContainers()
81
    {
82
        if (Seomatic::$settings->sitemapsEnabled) {
83
            $this->sitemapTemplateContainer = FrontendTemplateContainer::create();
84
            // The Sitemap Index
85
            $sitemapIndexTemplate = SitemapIndexTemplate::create();
86
            $this->sitemapTemplateContainer->addData($sitemapIndexTemplate, self::SEOMATIC_SITEMAPINDEX_CONTAINER);
87
            // A custom sitemap
88
            $sitemapCustomTemplate = SitemapCustomTemplate::create();
89
            $this->sitemapTemplateContainer->addData($sitemapCustomTemplate, self::SEOMATIC_SITEMAPCUSTOM_CONTAINER);
90
            // A generic sitemap
91
            $sitemapTemplate = SitemapTemplate::create();
92
            $this->sitemapTemplateContainer->addData($sitemapTemplate, self::SEOMATIC_SITEMAP_CONTAINER);
93
            // Handler: UrlManager::EVENT_REGISTER_SITE_URL_RULES
94
            Event::on(
95
                UrlManager::class,
96
                UrlManager::EVENT_REGISTER_SITE_URL_RULES,
97
                function (RegisterUrlRulesEvent $event) {
98
                    Craft::debug(
99
                        'UrlManager::EVENT_REGISTER_SITE_URL_RULES',
100
                        __METHOD__
101
                    );
102
                    // Register our sitemap routes
103
                    $event->rules = array_merge(
104
                        $event->rules,
105
                        $this->sitemapRouteRules()
106
                    );
107
                }
108
            );
109
        }
110
    }
111
112
    /**
113
     * @return array
114
     */
115
    public function sitemapRouteRules(): array
116
    {
117
        $rules = [];
118
        $groups = Craft::$app->getSites()->getAllGroups();
119
        $groupId = $groups[0]->id;
0 ignored issues
show
Unused Code introduced by
The assignment to $groupId is dead and can be removed.
Loading history...
120
        $currentSite = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $currentSite is dead and can be removed.
Loading history...
121
        try {
122
            $currentSite = Craft::$app->getSites()->getCurrentSite();
123
        } catch (SiteNotFoundException $e) {
124
            Craft::error($e->getMessage(), __METHOD__);
125
        }
126
        if ($currentSite) {
127
            try {
128
                $groupId = $currentSite->getGroup()->id;
129
            } catch (InvalidConfigException $e) {
130
                Craft::error($e->getMessage(), __METHOD__);
131
            }
132
        }
133
        // Add the route to redirect sitemap.xml to the actual sitemap
134
        $route =
0 ignored issues
show
Coding Style introduced by
Multi-line assignments must have the equal sign on the second line
Loading history...
135
            Seomatic::$plugin->handle
136
            .'/'
137
            .'sitemap'
138
            .'/'
139
            .'sitemap-index-redirect';
140
        $rules['sitemap.xml'] = [
141
            'route' => $route,
142
        ];
143
        // Add the route for the sitemap.xsl styles
144
        $route =
0 ignored issues
show
Coding Style introduced by
Multi-line assignments must have the equal sign on the second line
Loading history...
145
            Seomatic::$plugin->handle
146
            .'/'
147
            .'sitemap'
148
            .'/'
149
            .'sitemap-styles';
150
        $rules['sitemap.xsl'] = [
151
            'route' => $route,
152
        ];
153
        // Add the route for the sitemap-empty.xsl styles
154
        $route =
0 ignored issues
show
Coding Style introduced by
Multi-line assignments must have the equal sign on the second line
Loading history...
155
            Seomatic::$plugin->handle
156
            .'/'
157
            .'sitemap'
158
            .'/'
159
            .'sitemap-empty-styles';
160
        $rules['sitemap-empty.xsl'] = [
161
            'route' => $route,
162
        ];
163
        // Add all of the frontend container routes
164
        foreach ($this->sitemapTemplateContainer->data as $sitemapTemplate) {
165
            /** @var $sitemapTemplate FrontendTemplate */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
166
            $rules = array_merge(
167
                $rules,
168
                $sitemapTemplate->routeRules()
169
            );
170
        }
171
172
        return $rules;
173
    }
174
175
    /**
176
     * @param string $template
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
177
     * @param array  $params
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
178
     *
179
     * @return string
180
     */
181
    public function renderTemplate(string $template, array $params = []): string
182
    {
183
        $html = '';
184
        if (!empty($this->sitemapTemplateContainer->data[$template])) {
185
            /** @var FrontendTemplate $sitemapTemplate */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
186
            $sitemapTemplate = $this->sitemapTemplateContainer->data[$template];
187
            $html = $sitemapTemplate->render($params);
188
        }
189
190
        return $html;
191
    }
192
193
    /**
194
     * Submit the sitemap index to the search engine services
195
     */
196
    public function submitSitemapIndex()
197
    {
198
        if (Seomatic::$settings->sitemapsEnabled && Seomatic::$environment === 'live' && Seomatic::$settings->submitSitemaps) {
199
            // Submit the sitemap to each search engine
200
            $searchEngineUrls = self::SEARCH_ENGINE_SUBMISSION_URLS;
201
            foreach ($searchEngineUrls as &$url) {
202
                $groups = Craft::$app->getSites()->getAllGroups();
203
                foreach ($groups as $group) {
204
                    $groupSiteIds = $group->getSiteIds();
205
                    if (!empty($groupSiteIds)) {
206
                        $siteId = $groupSiteIds[0];
207
                        $sitemapIndexUrl = $this->sitemapIndexUrlForSiteId($siteId);
208
                        if (!empty($sitemapIndexUrl)) {
209
                            $submissionUrl = $url.urlencode($sitemapIndexUrl);
210
                            // create new guzzle client
211
                            $guzzleClient = Craft::createGuzzleClient(['timeout' => 120, 'connect_timeout' => 120]);
212
                            // Submit the sitemap index to each search engine
213
                            try {
214
                                $guzzleClient->post($submissionUrl);
215
                                Craft::info(
216
                                    'Sitemap index submitted to: '.$submissionUrl,
217
                                    __METHOD__
218
                                );
219
                            } catch (\Exception $e) {
220
                                Craft::error(
221
                                    'Error submitting sitemap index to: '.$submissionUrl.' - '.$e->getMessage(),
222
                                    __METHOD__
223
                                );
224
                            }
225
                        }
226
                    }
227
                }
228
            }
229
        }
230
    }
231
232
    /**
233
     * Submit the bundle sitemap to the search engine services
234
     *
235
     * @param ElementInterface $element
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
236
     */
237
    public function submitSitemapForElement(ElementInterface $element)
238
    {
239
        if (Seomatic::$settings->sitemapsEnabled && Seomatic::$environment === 'live' && Seomatic::$settings->submitSitemaps) {
240
            /** @var Element $element */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
241
            list($sourceId, $sourceBundleType, $sourceHandle, $sourceSiteId, $typeId)
242
                = Seomatic::$plugin->metaBundles->getMetaSourceFromElement($element);
243
            // Submit the sitemap to each search engine
244
            $searchEngineUrls = self::SEARCH_ENGINE_SUBMISSION_URLS;
245
            foreach ($searchEngineUrls as &$url) {
246
                $sitemapUrl = $this->sitemapUrlForBundle($sourceBundleType, $sourceHandle, $sourceSiteId);
247
                if (!empty($sitemapUrl)) {
248
                    $submissionUrl = $url.urlencode($sitemapUrl);
249
                    // create new guzzle client
250
                    $guzzleClient = Craft::createGuzzleClient(['timeout' => 120, 'connect_timeout' => 120]);
251
                    // Submit the sitemap index to each search engine
252
                    try {
253
                        $guzzleClient->post($submissionUrl);
254
                        Craft::info(
255
                            'Sitemap index submitted to: '.$submissionUrl,
256
                            __METHOD__
257
                        );
258
                    } catch (\Exception $e) {
259
                        Craft::error(
260
                            'Error submitting sitemap index to: '.$submissionUrl.' - '.$e->getMessage(),
261
                            __METHOD__
262
                        );
263
                    }
264
                }
265
            }
266
        }
267
    }
268
269
    /**
270
     * Submit the bundle sitemap to the search engine services
271
     *
272
     * @param int $siteId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
273
     */
274
    public function submitCustomSitemap(int $siteId)
275
    {
276
        if (Seomatic::$settings->sitemapsEnabled && Seomatic::$environment === 'live' && Seomatic::$settings->submitSitemaps) {
277
            // Submit the sitemap to each search engine
278
            $searchEngineUrls = self::SEARCH_ENGINE_SUBMISSION_URLS;
279
            foreach ($searchEngineUrls as &$url) {
280
                $sitemapUrl = $this->sitemapCustomUrlForSiteId($siteId);
281
                if (!empty($sitemapUrl)) {
282
                    $submissionUrl = $url.urlencode($sitemapUrl);
283
                    // create new guzzle client
284
                    $guzzleClient = Craft::createGuzzleClient(['timeout' => 120, 'connect_timeout' => 120]);
285
                    // Submit the sitemap index to each search engine
286
                    try {
287
                        $guzzleClient->post($submissionUrl);
288
                        Craft::info(
289
                            'Sitemap Custom submitted to: '.$submissionUrl,
290
                            __METHOD__
291
                        );
292
                    } catch (\Exception $e) {
293
                        Craft::error(
294
                            'Error submitting sitemap index to: '.$submissionUrl.' - '.$e->getMessage(),
295
                            __METHOD__
296
                        );
297
                    }
298
                }
299
            }
300
        }
301
    }
302
303
    /**
304
     * Get the URL to the $siteId's sitemap index
305
     *
306
     * @param int|null $siteId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
307
     *
308
     * @return string
309
     */
310
    public function sitemapIndexUrlForSiteId(int $siteId = null): string
311
    {
312
        $url = '';
313
        $sites = Craft::$app->getSites();
314
        if ($siteId === null) {
315
            $siteId = $sites->currentSite->id ?? 1;
316
        }
317
        $site = $sites->getSiteById($siteId);
318
        if ($site !== null) {
319
            try {
320
                $url = UrlHelper::siteUrl(
321
                    '/sitemaps-'
322
                    .$site->groupId
323
                    .'-sitemap.xml',
324
                    null,
325
                    null,
326
                    $siteId
327
                );
328
            } catch (Exception $e) {
329
                Craft::error($e->getMessage(), __METHOD__);
330
            }
331
        }
332
333
        return $url;
334
    }
335
336
    /**
337
     * @return string
338
     */
339
    public function siteGroupSitemaps(): string
340
    {
341
        $result = '';
342
        $sites = [];
343
        if (Seomatic::$settings->siteGroupsSeparate) {
344
            // Get only the sites that are in the current site's group
345
            try {
346
                $siteGroup = Craft::$app->getSites()->getCurrentSite()->getGroup();
347
            } catch (InvalidConfigException $e) {
348
                $siteGroup = null;
349
                Craft::error($e->getMessage(), __METHOD__);
350
            }
351
            // If we can't get a group, just use the current site
352
            if ($siteGroup === null) {
353
                $sites = [Craft::$app->getSites()->getCurrentSite()];
354
            } else  {
0 ignored issues
show
Coding Style introduced by
Expected "} else \n"; found " else {\n"
Loading history...
355
                $sites = $siteGroup->getSites();
356
            }
357
        } else {
358
            $sites = Craft::$app->getSites()->getAllSites();
359
        }
360
361
        foreach($sites as $site) {
0 ignored issues
show
Coding Style introduced by
Expected "foreach (...) {\n"; found "foreach(...) {\n"
Loading history...
362
            $result .= 'sitemap: ' . $site->getBaseUrl(true) . PHP_EOL;
363
        }
364
365
        return $result;
366
    }
367
368
    /**
369
     * @param int|null $siteId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
370
     *
371
     * @return string
372
     */
373
    public function sitemapCustomUrlForSiteId(int $siteId = null)
374
    {
375
        $url = '';
376
        $sites = Craft::$app->getSites();
377
        if ($siteId === null) {
378
            $siteId = $sites->currentSite->id ?? 1;
379
        }
380
        $site = $sites->getSiteById($siteId);
381
        if ($site) {
382
            try {
383
                $url = UrlHelper::siteUrl(
384
                    '/sitemaps-'
385
                    .$site->groupId
386
                    .'-'
387
                    .SitemapCustomTemplate::CUSTOM_SCOPE
388
                    .'-'
389
                    .SitemapCustomTemplate::CUSTOM_HANDLE
390
                    .'-'
391
                    .$siteId
392
                    .'-sitemap.xml',
393
                    null,
394
                    null,
395
                    $siteId
396
                );
397
            } catch (Exception $e) {
398
                Craft::error($e->getMessage(), __METHOD__);
399
            }
400
        }
401
402
        return $url;
403
    }
404
405
    /**
406
     * @param string   $sourceBundleType
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
407
     * @param string   $sourceHandle
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
408
     * @param int|null $siteId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
409
     *
410
     * @return string
411
     */
412
    public function sitemapUrlForBundle(string $sourceBundleType, string $sourceHandle, int $siteId = null): string
413
    {
414
        $url = '';
415
        $sites = Craft::$app->getSites();
416
        if ($siteId === null) {
417
            $siteId = $sites->currentSite->id ?? 1;
418
        }
419
        $site = $sites->getSiteById($siteId);
420
        $metaBundle = Seomatic::$plugin->metaBundles->getMetaBundleBySourceHandle(
421
            $sourceBundleType,
422
            $sourceHandle,
423
            $siteId
424
        );
425
        if ($site && $metaBundle) {
426
            try {
427
                $url = UrlHelper::siteUrl(
428
                    '/sitemaps-'
429
                    .$site->groupId
430
                    .'-'
431
                    .$metaBundle->sourceBundleType
432
                    .'-'
433
                    .$metaBundle->sourceHandle
434
                    .'-'
435
                    .$metaBundle->sourceSiteId
436
                    .'-sitemap.xml',
437
                    null,
438
                    null,
439
                    $siteId
440
                );
441
            } catch (Exception $e) {
442
                Craft::error($e->getMessage(), __METHOD__);
443
            }
444
        }
445
446
        return $url;
447
    }
448
449
    /**
450
     * Invalidate all of the sitemap caches
451
     */
452
    public function invalidateCaches()
453
    {
454
        $cache = Craft::$app->getCache();
455
        TagDependency::invalidate($cache, self::GLOBAL_SITEMAP_CACHE_TAG);
456
        Craft::info(
457
            'All sitemap caches cleared',
458
            __METHOD__
459
        );
460
    }
461
462
    /**
463
     * Invalidate the sitemap cache passed in $handle
464
     *
465
     * @param string $handle
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
466
     * @param int    $siteId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
467
     * @param string $type
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
468
     */
469
    public function invalidateSitemapCache(string $handle, int $siteId, string $type)
470
    {
471
        $cache = Craft::$app->getCache();
472
        // If the queue should be run automatically, do it now
473
        TagDependency::invalidate($cache, SitemapTemplate::SITEMAP_CACHE_TAG.$handle.$siteId);
474
        Craft::info(
475
            'Sitemap cache cleared: '.$handle,
476
            __METHOD__
477
        );
478
        $sites = Craft::$app->getSites();
479
        if ($siteId === null) {
0 ignored issues
show
introduced by
The condition $siteId === null is always false.
Loading history...
480
            $siteId = $sites->currentSite->id ?? 1;
481
        }
482
        $site = $sites->getSiteById($siteId);
483
        // Start up a job to generate the sitemap
484
        $queue = Craft::$app->getQueue();
485
        $jobId = $queue->push(new GenerateSitemap([
486
            'groupId' => $site->groupId,
487
            'type' => $type,
488
            'handle' => $handle,
489
            'siteId' => $siteId,
490
        ]));
491
        Craft::debug(
492
            Craft::t(
493
                'seomatic',
494
                'Started GenerateSitemap queue job id: {jobId}',
495
                [
496
                    'jobId' => $jobId,
497
                ]
498
            ),
499
            __METHOD__
500
        );
501
    }
502
503
    /**
504
     * Invalidate the sitemap index cache
505
     */
506
    public function invalidateSitemapIndexCache()
507
    {
508
        $cache = Craft::$app->getCache();
509
        TagDependency::invalidate($cache, SitemapIndexTemplate::SITEMAP_INDEX_CACHE_TAG);
510
        Craft::info(
511
            'Sitemap index cache cleared',
512
            __METHOD__
513
        );
514
    }
515
}
516