Passed
Push — v3 ( 5a2177...2a9dc2 )
by Andrew
05:45
created

Seomatic::tableSchemaExists()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
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;
13
14
use craft\elements\User;
15
use nystudio107\seomatic\assetbundles\seomatic\SeomaticAsset;
16
use nystudio107\seomatic\helpers\MetaValue as MetaValueHelper;
17
use nystudio107\seomatic\helpers\PluginTemplate;
18
use nystudio107\seomatic\models\MetaScriptContainer;
19
use nystudio107\seomatic\models\Settings;
20
use nystudio107\seomatic\services\FrontendTemplates as FrontendTemplatesService;
21
use nystudio107\seomatic\services\Helper as HelperService;
22
use nystudio107\seomatic\services\JsonLd as JsonLdService;
23
use nystudio107\seomatic\services\Link as LinkService;
24
use nystudio107\seomatic\services\MetaBundles as MetaBundlesService;
25
use nystudio107\seomatic\services\MetaContainers as MetaContainersService;
26
use nystudio107\seomatic\services\Redirects as RedirectsService;
27
use nystudio107\seomatic\services\Script as ScriptService;
28
use nystudio107\seomatic\services\Sitemaps as SitemapsService;
29
use nystudio107\seomatic\services\Tag as TagService;
30
use nystudio107\seomatic\services\Title as TitleService;
31
use nystudio107\seomatic\twigextensions\SeomaticTwigExtension;
32
use nystudio107\seomatic\variables\SeomaticVariable;
33
34
use Craft;
35
use craft\base\Element;
36
use craft\base\ElementInterface;
37
use craft\base\Plugin;
38
use craft\elements\Entry;
39
use craft\elements\Category;
40
use craft\errors\SiteNotFoundException;
41
use craft\events\CategoryGroupEvent;
42
use craft\events\ElementEvent;
43
use craft\events\ExceptionEvent;
44
use craft\events\PluginEvent;
45
use craft\events\RegisterCacheOptionsEvent;
46
use craft\events\RegisterUrlRulesEvent;
47
use craft\events\RegisterUserPermissionsEvent;
48
use craft\events\SectionEvent;
49
use craft\services\Categories;
50
use craft\services\Elements;
51
use craft\services\Plugins;
52
use craft\services\Sections;
53
use craft\services\UserPermissions;
54
use craft\helpers\UrlHelper;
55
use craft\utilities\ClearCaches;
56
use craft\web\ErrorHandler;
57
use yii\web\HttpException;
58
use craft\web\UrlManager;
59
use craft\web\View;
60
61
use yii\base\Event;
62
63
/**
64
 * Class Seomatic
65
 *
66
 * @author    nystudio107
67
 * @package   Seomatic
68
 * @since     3.0.0
69
 *
70
 * @property  FrontendTemplatesService frontendTemplates
71
 * @property  HelperService            helper
72
 * @property  JsonLdService            jsonLd
73
 * @property  LinkService              link
74
 * @property  MetaBundlesService       metaBundles
75
 * @property  MetaContainersService    metaContainers
76
 * @property  RedirectsService         redirects
77
 * @property  ScriptService            script
78
 * @property  SitemapsService          sitemaps
79
 * @property  TagService               tag
80
 * @property  TitleService             title
81
 */
82
class Seomatic extends Plugin
83
{
84
    // Constants
85
    // =========================================================================
86
87
    const SEOMATIC_HANDLE = 'Seomatic';
88
89
    // Static Properties
90
    // =========================================================================
91
92
    /**
93
     * @var Seomatic
94
     */
95
    public static $plugin;
96
97
    /**
98
     * @var SeomaticVariable
99
     */
100
    public static $seomaticVariable;
101
102
    /**
103
     * @var Settings
104
     */
105
    public static $settings;
106
107
    /**
108
     * @var ElementInterface
109
     */
110
    public static $matchedElement;
111
112
    /**
113
     * @var bool
114
     */
115
    public static $devMode;
116
117
    /**
118
     * @var View
119
     */
120
    public static $view;
121
122
    /**
123
     * @var
124
     */
125
    public static $language;
126
127
    /**
128
     * @var bool
129
     */
130
    public static $previewingMetaContainers = false;
131
132
    // Static Methods
133
    // =========================================================================
134
135
    /**
136
     * Set the matched element
137
     *
138
     * @param $element null|ElementInterface
139
     */
140
    public static function setMatchedElement($element)
141
    {
142
        self::$matchedElement = $element;
143
        /** @var  $element Element */
144
        if ($element) {
145
            self::$language = MetaValueHelper::getSiteLanguage($element->siteId);
0 ignored issues
show
Bug introduced by
It seems like $element->siteId can also be of type null; however, parameter $siteId of nystudio107\seomatic\hel...alue::getSiteLanguage() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

145
            self::$language = MetaValueHelper::getSiteLanguage(/** @scrutinizer ignore-type */ $element->siteId);
Loading history...
146
        } else {
147
            self::$language = MetaValueHelper::getSiteLanguage(null);
148
        }
149
        MetaValueHelper::cache();
150
    }
151
152
    // Public Properties
153
    // =========================================================================
154
155
    /**
156
     * @var string
157
     */
158
    public $schemaVersion = '3.0.4';
159
160
    // Public Methods
161
    // =========================================================================
162
163
    /**
164
     * @inheritdoc
165
     */
166
    public function init()
167
    {
168
        parent::init();
169
        self::$plugin = $this;
170
        // Initialize properties
171
        self::$settings = Seomatic::$plugin->getSettings();
172
        self::$devMode = Craft::$app->getConfig()->getGeneral()->devMode;
173
        self::$view = Craft::$app->getView();
174
        MetaValueHelper::cache();
175
        // If devMode is on, always force the environment to be "local"
176
        if (self::$devMode) {
177
            self::$settings->environment = "local";
178
        }
179
        $this->name = Seomatic::$settings->pluginName;
180
        // Install our event listeners
181
        if ($this->tableSchemaExists()) {
182
            $this->installEventListeners();
183
        }
184
        // We're loaded
185
        Craft::info(
186
            Craft::t(
187
                'seomatic',
188
                '{name} plugin loaded',
189
                ['name' => $this->name]
190
            ),
191
            __METHOD__
192
        );
193
    }
194
195
    /**
196
     * @inheritdoc
197
     */
198
    public function getSettingsResponse()
199
    {
200
        return Craft::$app->runAction('seomatic/settings/plugin');
201
    }
202
203
    /**
204
     * @inheritdoc
205
     */
206
    public function getCpNavItem()
207
    {
208
        $subNavs = [];
209
        $navItem = parent::getCpNavItem();
210
        /** @var User $currentUser */
211
        $currentUser = Craft::$app->getUser()->getIdentity();
212
        // Only show sub-navs the user has permission to view
213
        if ($currentUser->can('seomatic:dashboard')) {
214
            $subNavs['dashboard'] = [
215
                'label' => 'Dashboard',
216
                'url'   => 'seomatic/dashboard',
217
            ];
218
        }
219
        if ($currentUser->can('seomatic:global-meta')) {
220
            $subNavs['global'] = [
221
                'label' => 'Global SEO',
222
                'url'   => 'seomatic/global',
223
            ];
224
        }
225
        if ($currentUser->can('seomatic:content-meta')) {
226
            $subNavs['content'] = [
227
                'label' => 'Content SEO',
228
                'url'   => 'seomatic/content',
229
            ];
230
        }
231
        if ($currentUser->can('seomatic:site-settings')) {
232
            $subNavs['site'] = [
233
                'label' => 'Site Settings',
234
                'url'   => 'seomatic/site',
235
            ];
236
        }
237
        if ($currentUser->can('seomatic:tracking-scripts')) {
238
            $subNavs['tracking'] = [
239
                'label' => 'Tracking Scripts',
240
                'url'   => 'seomatic/tracking',
241
            ];
242
        }
243
        if ($currentUser->can('seomatic:plugin-settings')) {
244
            $subNavs['plugin'] = [
245
                'label' => 'Plugin Settings',
246
                'url'   => 'seomatic/plugin',
247
            ];
248
        }
249
        $navItem = array_merge($navItem, [
250
            'subnav' => $subNavs,
251
        ]);
252
253
        return $navItem;
254
    }
255
256
    /**
257
     * Clear all the caches!
258
     */
259
    public function clearAllCaches()
260
    {
261
        Seomatic::$plugin->frontendTemplates->invalidateCaches();
262
        Seomatic::$plugin->metaContainers->invalidateCaches();
263
        Seomatic::$plugin->sitemaps->invalidateCaches();
264
    }
265
266
    // Protected Methods
267
    // =========================================================================
268
269
    /**
270
     * Determine whether our table schema exists or not; this is needed because
271
     * migrations such as the install migration and base_install migration may
272
     * not have been run by the time our init() method has been called
273
     *
274
     * @return bool
275
     */
276
    protected function tableSchemaExists(): bool
277
    {
278
        return (Craft::$app->db->schema->getTableSchema('{{%seomatic_metabundles}}') !== null);
279
    }
280
281
    /**
282
     * Install our event listeners. We do it only after we receive the event
283
     * EVENT_AFTER_LOAD_PLUGINS so that any pending db migrations can be run
284
     * before our event listeners kick in
285
     */
286
    protected function installEventListeners()
287
    {
288
        // Handler: EVENT_AFTER_LOAD_PLUGINS
289
        Event::on(
290
            Plugins::class,
291
            Plugins::EVENT_AFTER_LOAD_PLUGINS,
292
            function () {
293
                // Add in our Twig extensions
294
                Seomatic::$view->registerTwigExtension(new SeomaticTwigExtension);
295
                // Add in our event listeners that are needed for every request
296
                $this->installGlobalEventListeners();
297
                // Only respond to non-console site requests
298
                $request = Craft::$app->getRequest();
299
                if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()) {
300
                    $this->handleSiteRequest();
301
                }
302
                // AdminCP magic
303
                if ($request->getIsCpRequest() && !$request->getIsConsoleRequest()) {
304
                    $this->handleAdminCpRequest();
305
                }
306
            }
307
        );
308
    }
309
310
    /**
311
     * Install global event listeners
312
     */
313
    protected function installGlobalEventListeners()
314
    {
315
        // Handler: EVENT_AFTER_INSTALL_PLUGIN
316
        Event::on(
317
            Plugins::class,
318
            Plugins::EVENT_AFTER_INSTALL_PLUGIN,
319
            function (PluginEvent $event) {
320
                if ($event->plugin === $this) {
321
                    // Invalidate our caches after we've been installed
322
                    $this->clearAllCaches();
323
                    // Send them to our welcome screen
324
                    $request = Craft::$app->getRequest();
325
                    if ($request->isCpRequest) {
326
                        Craft::$app->getResponse()->redirect(UrlHelper::cpUrl(
327
                            'seomatic/dashboard',
328
                            [
329
                                'showWelcome' => true
330
                            ]
331
                        ))->send();
332
                    }
333
                }
334
            }
335
        );
336
        // Handler: Sections::EVENT_AFTER_SAVE_SECTION
337
        Event::on(
338
            Sections::class,
339
            Sections::EVENT_AFTER_SAVE_SECTION,
340
            function (SectionEvent $event) {
341
                Craft::debug(
342
                    'Sections::EVENT_AFTER_SAVE_SECTION',
343
                    __METHOD__
344
                );
345
                if (!empty($event->section) && $event->section->id !== null) {
346
                    Seomatic::$plugin->metaBundles->invalidateMetaBundleById(
347
                        MetaBundlesService::SECTION_META_BUNDLE,
348
                        $event->section->id,
349
                        $event->isNew
350
                    );
351
                    // Create the meta bundles for this section if it's new
352
                    if ($event->isNew) {
353
                        Seomatic::$plugin->metaBundles->createContentMetaBundleForSection($event->section);
354
                        Seomatic::$plugin->sitemaps->submitSitemapIndex();
355
                    }
356
                }
357
            }
358
        );
359
        // Handler: Sections::EVENT_AFTER_DELETE_SECTION
360
        Event::on(
361
            Sections::class,
362
            Sections::EVENT_AFTER_DELETE_SECTION,
363
            function (SectionEvent $event) {
364
                Craft::debug(
365
                    'Sections::EVENT_AFTER_DELETE_SECTION',
366
                    __METHOD__
367
                );
368
                if (!empty($event->section) && $event->section->id !== null) {
369
                    Seomatic::$plugin->metaBundles->invalidateMetaBundleById(
370
                        MetaBundlesService::SECTION_META_BUNDLE,
371
                        $event->section->id,
372
                        false
373
                    );
374
                    // Delete the meta bundles for this section
375
                    Seomatic::$plugin->metaBundles->deleteMetaBundleBySourceId(
376
                        MetaBundlesService::SECTION_META_BUNDLE,
377
                        $event->section->id
378
                    );
379
                }
380
            }
381
        );
382
        // Handler: Categories::EVENT_AFTER_SAVE_GROUP
383
        Event::on(
384
            Categories::class,
385
            Categories::EVENT_AFTER_SAVE_GROUP,
386
            function (CategoryGroupEvent $event) {
387
                Craft::debug(
388
                    'Categories::EVENT_AFTER_SAVE_GROUP',
389
                    __METHOD__
390
                );
391
                if (!empty($event->categoryGroup) && $event->categoryGroup->id !== null) {
392
                    Seomatic::$plugin->metaBundles->invalidateMetaBundleById(
393
                        MetaBundlesService::CATEGORYGROUP_META_BUNDLE,
394
                        $event->categoryGroup->id,
395
                        $event->isNew
396
                    );
397
                    // Create the meta bundles for this category if it's new
398
                    if ($event->isNew) {
399
                        Seomatic::$plugin->metaBundles->createContentMetaBundleForCategoryGroup($event->categoryGroup);
400
                        Seomatic::$plugin->sitemaps->submitSitemapIndex();
401
                    }
402
                }
403
            }
404
        );
405
        // Handler: Categories::EVENT_AFTER_DELETE_GROUP
406
        Event::on(
407
            Categories::class,
408
            Categories::EVENT_AFTER_DELETE_GROUP,
409
            function (CategoryGroupEvent $event) {
410
                Craft::debug(
411
                    'Categories::EVENT_AFTER_DELETE_GROUP',
412
                    __METHOD__
413
                );
414
                if (!empty($event->categoryGroup) && $event->categoryGroup->id !== null) {
415
                    Seomatic::$plugin->metaBundles->invalidateMetaBundleById(
416
                        MetaBundlesService::CATEGORYGROUP_META_BUNDLE,
417
                        $event->categoryGroup->id,
418
                        false
419
                    );
420
                    // Delete the meta bundles for this category
421
                    Seomatic::$plugin->metaBundles->deleteMetaBundleBySourceId(
422
                        MetaBundlesService::CATEGORYGROUP_META_BUNDLE,
423
                        $event->categoryGroup->id
424
                    );
425
                }
426
            }
427
        );
428
        // Handler: Elements::EVENT_AFTER_SAVE_ELEMENT
429
        Event::on(
430
            Elements::class,
431
            Elements::EVENT_AFTER_SAVE_ELEMENT,
432
            function (ElementEvent $event) {
433
                Craft::debug(
434
                    'Elements::EVENT_AFTER_SAVE_ELEMENT',
435
                    __METHOD__
436
                );
437
                /** @var  $element Element */
438
                $element = $event->element;
439
                Seomatic::$plugin->metaBundles->invalidateMetaBundleByElement(
440
                    $element,
441
                    $event->isNew
442
                );
443
                if ($event->isNew) {
444
                    Seomatic::$plugin->sitemaps->submitSitemapForElement($element);
445
                }
446
            }
447
        );
448
        // Handler: Elements::EVENT_AFTER_DELETE_ELEMENT
449
        Event::on(
450
            Elements::class,
451
            Elements::EVENT_AFTER_DELETE_ELEMENT,
452
            function (ElementEvent $event) {
453
                Craft::debug(
454
                    'Elements::EVENT_AFTER_DELETE_ELEMENT',
455
                    __METHOD__
456
                );
457
                /** @var  $element Element */
458
                $element = $event->element;
459
                Seomatic::$plugin->metaBundles->invalidateMetaBundleByElement(
460
                    $element,
461
                    false
462
                );
463
            }
464
        );
465
    }
466
467
    /**
468
     * Handle site requests
469
     */
470
    protected function handleSiteRequest()
471
    {
472
        // Load the sitemap containers
473
        Seomatic::$plugin->sitemaps->loadSitemapContainers();
474
        // Load the frontend template containers
475
        Seomatic::$plugin->frontendTemplates->loadFrontendTemplateContainers();
476
        // Handler: ErrorHandler::EVENT_BEFORE_HANDLE_EXCEPTION
477
        Event::on(
478
            ErrorHandler::class,
479
            ErrorHandler::EVENT_BEFORE_HANDLE_EXCEPTION,
480
            function (ExceptionEvent $event) {
481
                Craft::debug(
482
                    'ErrorHandler::EVENT_BEFORE_HANDLE_EXCEPTION',
483
                    __METHOD__
484
                );
485
                $exception = $event->exception;
486
                // If this is a Twig Runtime exception, use the previous one instead
487
                if ($exception instanceof \Twig_Error_Runtime &&
488
                    ($previousException = $exception->getPrevious()) !== null) {
489
                    $exception = $previousException;
490
                }
491
                // If this is a 404 error, see if we can handle it
492
                if ($exception instanceof HttpException && $exception->statusCode === 404) {
493
                    Seomatic::$plugin->redirects->handle404();
494
                }
495
            }
496
        );
497
        // Handler: View::EVENT_BEGIN_BODY
498
        Event::on(
499
            View::class,
500
            View::EVENT_BEGIN_BODY,
501
            function () {
502
                Craft::debug(
503
                    'View::EVENT_BEGIN_BODY',
504
                    __METHOD__
505
                );
506
                // The <body> placeholder tag has just rendered, include any script HTML
507
                if (Seomatic::$settings->renderEnabled && Seomatic::$seomaticVariable) {
508
                    Seomatic::$plugin->metaContainers->includeScriptBodyHtml(View::POS_BEGIN);
509
                }
510
            }
511
        );
512
        // Handler: View::EVENT_END_BODY
513
        Event::on(
514
            View::class,
515
            View::EVENT_END_BODY,
516
            function () {
517
                Craft::debug(
518
                    'View::EVENT_END_BODY',
519
                    __METHOD__
520
                );
521
                // The </body> placeholder tag is about to be rendered, include any script HTML
522
                if (Seomatic::$settings->renderEnabled && Seomatic::$seomaticVariable) {
523
                    Seomatic::$plugin->metaContainers->includeScriptBodyHtml(View::POS_END);
524
                }
525
            }
526
        );
527
        // Handler: View::EVENT_END_PAGE
528
        Event::on(
529
            View::class,
530
            View::EVENT_END_PAGE,
531
            function () {
532
                Craft::debug(
533
                    'View::EVENT_END_PAGE',
534
                    __METHOD__
535
                );
536
                // The page is done rendering, include our meta containers
537
                if (Seomatic::$settings->renderEnabled && Seomatic::$seomaticVariable) {
538
                    Seomatic::$plugin->metaContainers->includeMetaContainers();
539
                }
540
            }
541
        );
542
    }
543
544
    /**
545
     * Handle AdminCP requests
546
     */
547
    protected function handleAdminCpRequest()
548
    {
549
        // Handler: UrlManager::EVENT_REGISTER_CP_URL_RULES
550
        Event::on(
551
            UrlManager::class,
552
            UrlManager::EVENT_REGISTER_CP_URL_RULES,
553
            function (RegisterUrlRulesEvent $event) {
554
                Craft::debug(
555
                    'UrlManager::EVENT_REGISTER_CP_URL_RULES',
556
                    __METHOD__
557
                );
558
                // Register our AdminCP routes
559
                $event->rules = array_merge(
560
                    $event->rules,
561
                    $this->customAdminCpRoutes()
562
                );
563
            }
564
        );
565
        // Handler: UserPermissions::EVENT_REGISTER_PERMISSIONS
566
        Event::on(
567
            UserPermissions::class,
568
            UserPermissions::EVENT_REGISTER_PERMISSIONS,
569
            function (RegisterUserPermissionsEvent $event) {
570
                Craft::debug(
571
                    'UserPermissions::EVENT_REGISTER_PERMISSIONS',
572
                    __METHOD__
573
                );
574
                // Register our custom permissions
575
                $event->permissions[Craft::t('seomatic', 'SEOmatic')] = $this->customAdminCpPermissions();
576
            }
577
        );
578
        // Handler: ClearCaches::EVENT_REGISTER_CACHE_OPTIONS
579
        Event::on(
580
            ClearCaches::class,
581
            ClearCaches::EVENT_REGISTER_CACHE_OPTIONS,
582
            function (RegisterCacheOptionsEvent $event) {
583
                Craft::debug(
584
                    'ClearCaches::EVENT_REGISTER_CACHE_OPTIONS',
585
                    __METHOD__
586
                );
587
                // Register our AdminCP routes
588
                $event->options = array_merge(
589
                    $event->options,
590
                    $this->customAdminCpCacheOptions()
591
                );
592
            }
593
        );
594
        // Entries sidebar
595
        self::$view->hook('cp.entries.edit.details', function (&$context) {
596
            $html = '';
597
            self::$view->registerAssetBundle(SeomaticAsset::class);
598
            /** @var  $entry Entry */
599
            $entry = $context['entry'];
600
            if (!empty($entry) && !empty($entry->uri)) {
601
                Seomatic::$plugin->metaContainers->previewMetaContainers($entry->uri, $entry->siteId, true);
602
                // Render our preview sidebar template
603
                if (self::$settings->displayPreviewSidebar && self::$matchedElement) {
604
                    $html .= PluginTemplate::renderPluginTemplate('_sidebars/entry-preview.twig');
605
                }
606
                // Render our analysis sidebar template
607
                if (self::$settings->displayAnalysisSidebar && self::$matchedElement) {
608
                    $html .= PluginTemplate::renderPluginTemplate('_sidebars/entry-analysis.twig');
609
                }
610
            }
611
612
            return $html;
613
        });
614
        // Category Groups sidebar
615
        self::$view->hook('cp.categories.edit.details', function (&$context) {
616
            $html = '';
617
            self::$view->registerAssetBundle(SeomaticAsset::class);
618
            /** @var  $category Category */
619
            $category = $context['category'];
620
            if (!empty($category) && !empty($category->uri)) {
621
                Seomatic::$plugin->metaContainers->previewMetaContainers($category->uri, $category->siteId, true);
622
                // Render our preview sidebar template
623
                if (self::$settings->displayPreviewSidebar) {
624
                    $html .= PluginTemplate::renderPluginTemplate('_sidebars/category-preview.twig');
625
                }
626
                // Render our analysis sidebar template
627
                if (self::$settings->displayAnalysisSidebar) {
628
                    $html .= PluginTemplate::renderPluginTemplate('_sidebars/category-analysis.twig');
629
                }
630
            }
631
632
            return $html;
633
        });
634
    }
635
636
    /**
637
     * @inheritdoc
638
     */
639
    protected function createSettingsModel()
640
    {
641
        return new Settings();
642
    }
643
644
    /**
645
     * Return the custom AdminCP routes
646
     *
647
     * @return array
648
     */
649
    protected function customAdminCpRoutes(): array
650
    {
651
        return [
652
            'seomatic' =>
653
                'seomatic/settings/dashboard',
654
            'seomatic/dashboard' =>
655
                'seomatic/settings/dashboard',
656
            'seomatic/dashboard/<siteHandle:{handle}>' =>
657
                'seomatic/settings/dashboard',
658
659
            'seomatic/global' => [
660
                'route' => 'seomatic/settings/global',
661
                'defaults' => ['subSection' => 'general'],
662
            ],
663
            'seomatic/global/<subSection:{handle}>' =>
664
                'seomatic/settings/global',
665
            'seomatic/global/<subSection:{handle}>/<siteHandle:{handle}>' =>
666
                'seomatic/settings/global',
667
668
            'seomatic/content' =>
669
                'seomatic/settings/content',
670
            'seomatic/content/<siteHandle:{handle}>' =>
671
                'seomatic/settings/content',
672
673
            'seomatic/edit-content/<subSection:{handle}>/<sourceBundleType:{handle}>/<sourceHandle:{handle}>' =>
674
                'seomatic/settings/edit-content',
675
            'seomatic/edit-content/<subSection:{handle}>/<sourceBundleType:{handle}>/<sourceHandle:{handle}>/<siteHandle:{handle}>' =>
676
                'seomatic/settings/edit-content',
677
678
            'seomatic/site' => [
679
                'route' => 'seomatic/settings/site',
680
                'defaults' => ['subSection' => 'identity'],
681
            ],
682
            'seomatic/site/<subSection:{handle}>' =>
683
                'seomatic/settings/site',
684
            'seomatic/site/<subSection:{handle}>/<siteHandle:{handle}>' =>
685
                'seomatic/settings/site',
686
687
            'seomatic/tracking' => [
688
                'route' => 'seomatic/settings/tracking',
689
                'defaults' => ['subSection' => 'googleAnalytics'],
690
            ],
691
            'seomatic/tracking/<subSection:{handle}>' =>
692
                'seomatic/settings/tracking',
693
            'seomatic/tracking/<subSection:{handle}>/<siteHandle:{handle}>' =>
694
                'seomatic/settings/tracking',
695
696
            'seomatic/plugin' =>
697
                'seomatic/settings/plugin',
698
        ];
699
    }
700
701
    /**
702
     * Returns the custom AdminCP cache options.
703
     *
704
     * @return array
705
     */
706
    protected function customAdminCpCacheOptions(): array
707
    {
708
        return [
709
            // Frontend template caches
710
            [
711
                'key'    => 'seomatic-frontendtemplate-caches',
712
                'label'  => Craft::t('seomatic', 'SEOmatic frontend template caches'),
713
                'action' => [Seomatic::$plugin->frontendTemplates, 'invalidateCaches'],
714
            ],
715
            // Meta bundle caches
716
            [
717
                'key'    => 'seomatic-metabundle-caches',
718
                'label'  => Craft::t('seomatic', 'SEOmatic metadata caches'),
719
                'action' => [Seomatic::$plugin->metaContainers, 'invalidateCaches'],
720
            ],
721
            // Sitemap caches
722
            [
723
                'key'    => 'seomatic-sitemap-caches',
724
                'label'  => Craft::t('seomatic', 'SEOmatic sitemap caches'),
725
                'action' => [Seomatic::$plugin->sitemaps, 'invalidateCaches'],
726
            ],
727
        ];
728
    }
729
730
    /**
731
     * Returns the custom AdminCP user permissions.
732
     *
733
     * @return array
734
     */
735
    protected function customAdminCpPermissions(): array
736
    {
737
        // The script meta containers for the global meta bundle
738
        try {
739
            $currentSiteId = Craft::$app->getSites()->getCurrentSite()->id ?? 1;
740
        } catch (SiteNotFoundException $e) {
741
            $currentSiteId = 1;
742
        }
743
        // Dynamic permissions for the scripts
744
        $metaBundle = Seomatic::$plugin->metaBundles->getGlobalMetaBundle($currentSiteId);
745
        $scripts = Seomatic::$plugin->metaBundles->getContainerDataFromBundle(
746
            $metaBundle,
747
            MetaScriptContainer::CONTAINER_TYPE
748
        );
749
        $scriptsPerms = [];
750
        foreach ($scripts as $scriptHandle => $scriptData) {
751
            $scriptsPerms["seomatic:tracking-scripts:${scriptHandle}"] = [
752
                'label' => Craft::t('seomatic', $scriptData->name),
753
            ];
754
        }
755
756
        return [
757
            "seomatic:dashboard"  => [
758
                'label' => Craft::t('seomatic', 'Dashboard'),
759
            ],
760
            "seomatic:global-meta"      => [
761
                'label'  => Craft::t('seomatic', 'Edit Global Meta'),
762
                'nested' => [
763
                    "seomatic:global-meta:general"  => [
764
                        'label' => Craft::t('seomatic', 'General'),
765
                    ],
766
                    "seomatic:global-meta:twitter"  => [
767
                        'label' => Craft::t('seomatic', 'Twitter'),
768
                    ],
769
                    "seomatic:global-meta:facebook" => [
770
                        'label' => Craft::t('seomatic', 'Facebook'),
771
                    ],
772
                    "seomatic:global-meta:robots"   => [
773
                        'label' => Craft::t('seomatic', 'Robots'),
774
                    ],
775
                    "seomatic:global-meta:humans"   => [
776
                        'label' => Craft::t('seomatic', 'Humans'),
777
                    ],
778
                ],
779
            ],
780
            "seomatic:content-meta"     => [
781
                'label'  => Craft::t('seomatic', 'Edit Content SEO'),
782
                'nested' => [
783
                    "seomatic:content-meta:general"  => [
784
                        'label' => Craft::t('seomatic', 'General'),
785
                    ],
786
                    "seomatic:content-meta:twitter"  => [
787
                        'label' => Craft::t('seomatic', 'Twitter'),
788
                    ],
789
                    "seomatic:content-meta:facebook" => [
790
                        'label' => Craft::t('seomatic', 'Facebook'),
791
                    ],
792
                    "seomatic:content-meta:sitemap"  => [
793
                        'label' => Craft::t('seomatic', 'Sitemap'),
794
                    ],
795
                ],
796
            ],
797
            "seomatic:site-settings"    => [
798
                'label'  => Craft::t('seomatic', 'Edit Site Settings'),
799
                'nested' => [
800
                    "seomatic:site-settings:identity"     => [
801
                        'label' => Craft::t('seomatic', 'Identity'),
802
                    ],
803
                    "seomatic:site-settings:creator"      => [
804
                        'label' => Craft::t('seomatic', 'Creator'),
805
                    ],
806
                    "seomatic:site-settings:social-media" => [
807
                        'label' => Craft::t('seomatic', 'Social Media'),
808
                    ],
809
                ],
810
            ],
811
            "seomatic:tracking-scripts" => [
812
                'label'  => Craft::t('seomatic', 'Edit Tracking Scripts'),
813
                'nested' => $scriptsPerms,
814
            ],
815
            "seomatic:plugin-settings"  => [
816
                'label' => Craft::t('seomatic', 'Edit Plugin Settings'),
817
            ],
818
        ];
819
    }
820
}
821