Passed
Push — develop ( 421797...7f10a1 )
by Andrew
09:07
created

Sitemaps::anyEntryTypeHasSitemapUrls()   B

Complexity

Conditions 10
Paths 4

Size

Total Lines 33
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 110

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 21
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 33
ccs 0
cts 22
cp 0
crap 110
rs 7.6666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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