Seomatic::customAdminCpCacheOptions()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 26
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 26
ccs 0
cts 12
cp 0
rs 9.8333
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
1
<?php
2
/**
3
 * SEOmatic plugin for Craft CMS
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) 2022 nystudio107
10
 */
11
12
namespace nystudio107\seomatic;
13
14
use Craft;
15
use craft\base\Element;
16
use craft\base\ElementInterface;
17
use craft\base\Plugin;
18
use craft\elements\Entry;
19
use craft\elements\User;
20
use craft\errors\SiteNotFoundException;
21
use craft\events\DefineGqlTypeFieldsEvent;
22
use craft\events\ElementEvent;
23
use craft\events\ModelEvent;
24
use craft\events\PluginEvent;
25
use craft\events\RegisterCacheOptionsEvent;
26
use craft\events\RegisterComponentTypesEvent;
27
use craft\events\RegisterGqlQueriesEvent;
28
use craft\events\RegisterGqlSchemaComponentsEvent;
29
use craft\events\RegisterGqlTypesEvent;
30
use craft\events\RegisterPreviewTargetsEvent;
31
use craft\events\RegisterUrlRulesEvent;
32
use craft\events\RegisterUserPermissionsEvent;
33
use craft\feedme\events\RegisterFeedMeFieldsEvent;
34
use craft\feedme\Plugin as FeedMe;
35
use craft\feedme\services\Fields as FeedMeFields;
36
use craft\gql\TypeManager;
37
use craft\helpers\StringHelper;
38
use craft\services\Elements;
39
use craft\services\Fields;
40
use craft\services\Gql;
41
use craft\services\Plugins;
42
use craft\services\Sites as SitesService;
43
use craft\services\UserPermissions;
44
use craft\utilities\ClearCaches;
45
use craft\web\Application;
46
use craft\web\UrlManager;
47
use craft\web\View;
48
use markhuot\CraftQL\Builders\Schema;
49
use markhuot\CraftQL\CraftQL;
50
use markhuot\CraftQL\Events\AlterSchemaFields;
51
use nystudio107\codeeditor\autocompletes\EnvironmentVariableAutocomplete;
52
use nystudio107\codeeditor\events\RegisterCodeEditorAutocompletesEvent;
53
use nystudio107\codeeditor\events\RegisterTwigValidatorVariablesEvent;
54
use nystudio107\codeeditor\services\AutocompleteService;
55
use nystudio107\codeeditor\validators\TwigTemplateValidator;
56
use nystudio107\crafttwigsandbox\helpers\SecurityPolicy;
57
use nystudio107\crafttwigsandbox\web\SandboxView;
58
use nystudio107\fastcgicachebust\FastcgiCacheBust;
59
use nystudio107\seomatic\autocompletes\TrackingVarsAutocomplete;
60
use nystudio107\seomatic\debug\panels\SeomaticPanel;
61
use nystudio107\seomatic\fields\Seomatic_Meta as Seomatic_MetaField;
62
use nystudio107\seomatic\fields\SeoSettings as SeoSettingsField;
63
use nystudio107\seomatic\gql\arguments\SeomaticArguments;
64
use nystudio107\seomatic\gql\interfaces\SeomaticInterface;
65
use nystudio107\seomatic\gql\queries\SeomaticQuery;
66
use nystudio107\seomatic\gql\resolvers\SeomaticResolver;
67
use nystudio107\seomatic\gql\types\SeomaticEnvironmentType;
68
use nystudio107\seomatic\helpers\Environment as EnvironmentHelper;
69
use nystudio107\seomatic\helpers\Gql as GqlHelper;
70
use nystudio107\seomatic\helpers\MetaValue as MetaValueHelper;
71
use nystudio107\seomatic\helpers\Schema as SchemaHelper;
72
use nystudio107\seomatic\helpers\UrlHelper;
73
use nystudio107\seomatic\integrations\feedme\SeoSettings as SeoSettingsFeedMe;
74
use nystudio107\seomatic\listeners\GetCraftQLSchema;
75
use nystudio107\seomatic\models\MetaScriptContainer;
76
use nystudio107\seomatic\models\Settings;
77
use nystudio107\seomatic\services\ServicesTrait;
78
use nystudio107\seomatic\twigextensions\SeomaticTwigExtension;
79
use nystudio107\seomatic\variables\SeomaticVariable;
80
use yii\base\Application as BaseApplication;
81
use yii\base\Event;
82
use yii\debug\Module;
83
84
/** @noinspection MissingPropertyAnnotationsInspection */
85
86
/**
87
 * Class Seomatic
88
 *
89
 * @author    nystudio107
90
 * @package   Seomatic
91
 * @since     3.0.0
92
 */
93
class Seomatic extends Plugin
94
{
95
    // Traits
96
    // =========================================================================
97
98
    use ServicesTrait;
99
100
    // Constants
101
    // =========================================================================
102
103
    const SEOMATIC_HANDLE = 'Seomatic';
104
105
    const DEVMODE_CACHE_DURATION = 30;
106
107
    const FRONTEND_SEO_FILE_LINK = 'seomatic/seo-file-link/<url:[^\/]+>/<robots:[^\/]+>/<canonical:[^\/]+>/<inline:\d+>/<fileName:[-\w\.*]+>';
108
109
    const FRONTEND_PREVIEW_PATH = 'seomatic/preview-social-media';
110
111
    const SEOMATIC_PREVIEW_AUTHORIZATION_KEY = 'seomaticPreviewAuthorizationKey';
112
113
    const GQL_ELEMENT_INTERFACES = [
114
        'EntryInterface',
115
        'CategoryInterface',
116
        'ProductInterface',
117
    ];
118
119
    const SEOMATIC_EXPRESSION_FIELD_TYPE = 'SeomaticExpressionField';
120
    const SEOMATIC_TRACKING_FIELD_TYPE = 'SeomaticTrackingField';
121
122
    // Static Properties
123
    // =========================================================================
124
125
    /**
126
     * @var Seomatic
127
     */
128
    public static $plugin;
129
130
    /**
131
     * @var SeomaticVariable|null
132
     */
133
    public static $seomaticVariable;
134
135
    /**
136
     * @var Settings
137
     */
138
    public static $settings;
139
140
    /**
141
     * @var ElementInterface|null
142
     */
143
    public static $matchedElement;
144
145
    /**
146
     * @var bool
147
     */
148
    public static $devMode;
149
150
    /**
151
     * @var View
152
     */
153
    public static $view;
154
155
    /**
156
     * @var null|View
157
     */
158
    public static $sandboxView;
159
160
    /**
161
     * @var string
162
     */
163
    public static $language;
164
165
    /**
166
     * @var string
167
     */
168
    public static $environment;
169
170
    /**
171
     * @var int
172
     */
173
    public static $cacheDuration;
174
175
    /**
176
     * @var bool
177
     */
178
    public static $previewingMetaContainers = false;
179
180
    /**
181
     * @var bool
182
     */
183
    public static $loadingMetaContainers = false;
184
185
    /**
186
     * @var bool
187
     */
188
    public static $savingSettings = false;
189
190
    /**
191
     * @var bool
192
     */
193
    public static $headlessRequest = false;
194
195
    /**
196
     * @var bool
197
     */
198
    public static $craft31 = false;
199
200
    /**
201
     * @var bool
202
     */
203
    public static $craft32 = false;
204
205
    /**
206
     * @var bool
207
     */
208
    public static $craft33 = false;
209
210
    /**
211
     * @var bool
212
     */
213
    public static $craft34 = false;
214
215
    /**
216
     * @var bool
217
     */
218
    public static $craft35 = false;
219
220
    /**
221
     * @var bool
222
     */
223
    public static $craft37 = false;
224
225
    /**
226
     * @var string
227
     */
228
    public $schemaVersion = '3.0.12';
229
    /**
230
     * @var bool
231
     */
232
    public $hasCpSection = true;
233
234
    // Public Properties
235
    // =========================================================================
236
237
    /**
238
     * @var bool
239
     */
240
    public $hasCpSettings = true;
241
242
    // Static Methods
243
    // =========================================================================
244
245
    /**
246
     * Set the matched element
247
     *
248
     * @param $element null|ElementInterface
249
     */
250
    public static function setMatchedElement($element)
251
    {
252
        self::$matchedElement = $element;
253
        /** @var  $element Element */
254
        if ($element) {
0 ignored issues
show
introduced by
$element is of type craft\base\Element, thus it always evaluated to true.
Loading history...
255
            self::$language = MetaValueHelper::getSiteLanguage($element->siteId);
256
        } else {
257
            self::$language = MetaValueHelper::getSiteLanguage(null);
258
        }
259
        MetaValueHelper::cache();
260
    }
261
262
    // Public Methods
263
    // =========================================================================
264
265
    /**
266
     * @inheritdoc
267
     */
268
    public function init()
269
    {
270
        parent::init();
271
        self::$plugin = $this;
272
        // Handle any console commands
273
        $request = Craft::$app->getRequest();
274
        if ($request->getIsConsoleRequest()) {
275
            $this->controllerNamespace = 'nystudio107\seomatic\console\controllers';
276
        }
277
        // Initialize properties
278
        /** @var Settings $settings */
279
        $settings = self::$plugin->getSettings();
280
        self::$settings = $settings;
281
        self::$devMode = Craft::$app->getConfig()->getGeneral()->devMode;
282
        self::$view = Craft::$app->getView();
283
        // Use a Twig sandbox for SEOmatic rendering
284
        $securityPolicy = SecurityPolicy::createFromFile('seomatic-sandbox', '@nystudio107/seomatic');
285
        self::$sandboxView = new SandboxView(['securityPolicy' => $securityPolicy]);
286
        self::$cacheDuration = self::$devMode
0 ignored issues
show
Documentation Bug introduced by
It seems like self::devMode ? self::DE...taCacheDuration ?? null can also be of type string. However, the property $cacheDuration is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
287
            ? self::DEVMODE_CACHE_DURATION
288
            : self::$settings->metaCacheDuration ?? null;
289
        self::$cacheDuration = self::$cacheDuration === null ? null : (int)self::$cacheDuration;
290
        self::$environment = EnvironmentHelper::determineEnvironment();
291
        MetaValueHelper::cache();
292
        // Version helpers
293
        self::$craft31 = version_compare(Craft::$app->getVersion(), '3.1', '>=');
0 ignored issues
show
Documentation Bug introduced by
It seems like version_compare(Craft::a...Version(), '3.1', '>=') can also be of type integer. However, the property $craft31 is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
294
        self::$craft32 = version_compare(Craft::$app->getVersion(), '3.2', '>=');
0 ignored issues
show
Documentation Bug introduced by
It seems like version_compare(Craft::a...Version(), '3.2', '>=') can also be of type integer. However, the property $craft32 is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
295
        self::$craft33 = version_compare(Craft::$app->getVersion(), '3.3', '>=');
0 ignored issues
show
Documentation Bug introduced by
It seems like version_compare(Craft::a...Version(), '3.3', '>=') can also be of type integer. However, the property $craft33 is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
296
        self::$craft34 = version_compare(Craft::$app->getVersion(), '3.4', '>=');
0 ignored issues
show
Documentation Bug introduced by
It seems like version_compare(Craft::a...Version(), '3.4', '>=') can also be of type integer. However, the property $craft34 is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
297
        self::$craft35 = version_compare(Craft::$app->getVersion(), '3.5', '>=');
0 ignored issues
show
Documentation Bug introduced by
It seems like version_compare(Craft::a...Version(), '3.5', '>=') can also be of type integer. However, the property $craft35 is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
298
        self::$craft37 = version_compare(Craft::$app->getVersion(), '3.7', '>=');
0 ignored issues
show
Documentation Bug introduced by
It seems like version_compare(Craft::a...Version(), '3.7', '>=') can also be of type integer. However, the property $craft37 is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
299
        $this->name = self::$settings->pluginName;
300
        // Install our event listeners
301
        $this->installEventListeners();
302
        // We're loaded
303
        Craft::info(
304
            Craft::t(
305
                'seomatic',
306
                '{name} plugin loaded',
307
                ['name' => $this->name]
308
            ),
309
            __METHOD__
310
        );
311
    }
312
313
    /**
314
     * @inheritdoc
315
     */
316
    public function getSettings()
317
    {
318
        // For all the emojis
319
        $settingsModel = parent::getSettings();
320
        if ($settingsModel !== null && !self::$savingSettings) {
321
            $attributes = $settingsModel->attributes();
322
            if ($attributes !== null) {
0 ignored issues
show
introduced by
The condition $attributes !== null is always true.
Loading history...
323
                foreach ($attributes as $attribute) {
324
                    if (is_string($settingsModel->$attribute)) {
325
                        $settingsModel->$attribute = html_entity_decode(
326
                            $settingsModel->$attribute,
327
                            ENT_NOQUOTES,
328
                            'UTF-8'
329
                        );
330
                    }
331
                }
332
            }
333
            self::$savingSettings = false;
334
        }
335
336
        /* @var Settings $settingsModel */
337
        return $settingsModel;
338
    }
339
340
    /**
341
     * Determine whether our table schema exists or not; this is needed because
342
     * migrations such as the install migration and base_install migration may
343
     * not have been run by the time our init() method has been called
344
     *
345
     * @return bool
346
     */
347
    public function migrationsAndSchemaReady(): bool
348
    {
349
        $pluginsService = Craft::$app->getPlugins();
350
        if ($pluginsService->doesPluginRequireDatabaseUpdate(self::$plugin)) {
351
            return false;
352
        }
353
        if (Craft::$app->db->schema->getTableSchema('{{%seomatic_metabundles}}') === null) {
354
            return false;
355
        }
356
357
        return true;
358
    }
359
360
    /**
361
     * Clear all the caches!
362
     */
363
    public function clearAllCaches()
364
    {
365
        // Clear all of SEOmatic's caches
366
        self::$plugin->frontendTemplates->invalidateCaches();
367
        self::$plugin->metaContainers->invalidateCaches();
368
        self::$plugin->sitemaps->invalidateCaches();
369
        SchemaHelper::invalidateCaches();
370
        // If they are using Craft 3.3 or later, clear the GraphQL caches too
371
        if (self::$craft33) {
372
            $gql = Craft::$app->getGql();
373
            if (method_exists($gql, 'invalidateCaches')) {
374
                $gql->invalidateCaches();
375
            }
376
        }
377
        // If the FastCGI Cache Bust plugin is installed, clear its caches too
378
        /** @var ?FastcgiCacheBust $plugin */
379
        $plugin = Craft::$app->getPlugins()->getPlugin('fastcgi-cache-bust');
380
        if ($plugin !== null) {
381
            $plugin->cache->clearAll();
382
        }
383
    }
384
385
    // Protected Methods
386
    // =========================================================================
387
388
    /**
389
     * @inheritdoc
390
     */
391
    public function getSettingsResponse()
392
    {
393
        // Just redirect to the plugin settings page
394
        Craft::$app->getResponse()->redirect(UrlHelper::cpUrl('seomatic/plugin'));
395
    }
396
397
    /**
398
     * @inheritdoc
399
     */
400
    public function getCpNavItem()
401
    {
402
        $subNavs = [];
403
        $navItem = parent::getCpNavItem();
404
        $request = Craft::$app->getRequest();
405
        $siteSuffix = '';
406
        if ($request->getSegment(1) === 'seomatic') {
407
            $segments = $request->getSegments();
408
            $lastSegment = end($segments);
409
            $site = Craft::$app->getSites()->getSiteByHandle($lastSegment);
410
            if ($site !== null) {
411
                $siteSuffix = '/' . $lastSegment;
412
            }
413
        }
414
        /** @var User $currentUser */
415
        $currentUser = Craft::$app->getUser()->getIdentity();
416
        // Only show sub-navs the user has permission to view
417
        if ($currentUser->can('seomatic:dashboard')) {
418
            $subNavs['dashboard'] = [
419
                'label' => Craft::t('seomatic', 'Dashboard'),
420
                'url' => 'seomatic/dashboard' . $siteSuffix,
421
            ];
422
        }
423
        if ($currentUser->can('seomatic:global-meta')) {
424
            $subNavs['global'] = [
425
                'label' => Craft::t('seomatic', 'Global SEO'),
426
                'url' => 'seomatic/global/general' . $siteSuffix,
427
            ];
428
        }
429
        if ($currentUser->can('seomatic:content-meta')) {
430
            $subNavs['content'] = [
431
                'label' => Craft::t('seomatic', 'Content SEO'),
432
                'url' => 'seomatic/content' . $siteSuffix,
433
            ];
434
        }
435
        if ($currentUser->can('seomatic:site-settings')) {
436
            $subNavs['site'] = [
437
                'label' => Craft::t('seomatic', 'Site Settings'),
438
                'url' => 'seomatic/site/identity' . $siteSuffix,
439
            ];
440
        }
441
        if ($currentUser->can('seomatic:tracking-scripts')) {
442
            $subNavs['tracking'] = [
443
                'label' => Craft::t('seomatic', 'Tracking Scripts'),
444
                'url' => 'seomatic/tracking/gtag' . $siteSuffix,
445
            ];
446
        }
447
        $editableSettings = true;
448
        $general = Craft::$app->getConfig()->getGeneral();
449
        if (self::$craft31 && !$general->allowAdminChanges) {
450
            $editableSettings = false;
451
        }
452
        if ($currentUser->can('seomatic:plugin-settings') && $editableSettings) {
453
            $subNavs['plugin'] = [
454
                'label' => Craft::t('seomatic', 'Plugin Settings'),
455
                'url' => 'seomatic/plugin',
456
            ];
457
        }
458
        // SEOmatic doesn't really have an index page, so if the user can't access any sub nav items, we probably shouldn't show the main sub nav item either
459
        if (empty($subNavs)) {
460
            return null;
461
        }
462
        // A single sub nav item is redundant
463
        if (count($subNavs) === 1) {
464
            $subNavs = [];
465
        }
466
        $navItem = array_merge($navItem, [
467
            'subnav' => $subNavs,
468
        ]);
469
470
        return $navItem;
471
    }
472
473
    /**
474
     * Install our event listeners.
475
     */
476
    protected function installEventListeners()
477
    {
478
        // Install our event listeners only if our table schema exists
479
        if ($this->migrationsAndSchemaReady()) {
480
            // Add in our Twig extensions
481
            $seomaticTwigExtension = new SeomaticTwigExtension();
482
            self::$view->registerTwigExtension($seomaticTwigExtension);
483
            self::$sandboxView->registerTwigExtension($seomaticTwigExtension);
0 ignored issues
show
Bug introduced by
The method registerTwigExtension() does not exist on null. ( Ignorable by Annotation )

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

483
            self::$sandboxView->/** @scrutinizer ignore-call */ 
484
                                registerTwigExtension($seomaticTwigExtension);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
484
            $request = Craft::$app->getRequest();
485
            // Add in our event listeners that are needed for every request
486
            $this->installGlobalEventListeners();
487
            // Install only for non-console site requests
488
            if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()) {
489
                $this->installSiteEventListeners();
490
            }
491
            // Install only for non-console Control Panel requests
492
            if ($request->getIsCpRequest() && !$request->getIsConsoleRequest()) {
493
                $this->installCpEventListeners();
494
            }
495
        }
496
        // Handler: EVENT_AFTER_INSTALL_PLUGIN
497
        Event::on(
498
            Plugins::class,
499
            Plugins::EVENT_AFTER_INSTALL_PLUGIN,
500
            function(PluginEvent $event) {
501
                if ($event->plugin === $this) {
502
                    // Invalidate our caches after we've been installed
503
                    $this->clearAllCaches();
504
                    // Send them to our welcome screen
505
                    $request = Craft::$app->getRequest();
506
                    if ($request->isCpRequest) {
507
                        Craft::$app->getResponse()->redirect(UrlHelper::cpUrl(
508
                            'seomatic/dashboard',
509
                            [
510
                                'showWelcome' => true,
511
                            ]
512
                        ))->send();
513
                    }
514
                }
515
            }
516
        );
517
        // Handler: ClearCaches::EVENT_REGISTER_CACHE_OPTIONS
518
        Event::on(
519
            ClearCaches::class,
520
            ClearCaches::EVENT_REGISTER_CACHE_OPTIONS,
521
            function(RegisterCacheOptionsEvent $event) {
522
                Craft::debug(
523
                    'ClearCaches::EVENT_REGISTER_CACHE_OPTIONS',
524
                    __METHOD__
525
                );
526
                // Register our Cache Options
527
                $event->options = array_merge(
528
                    $event->options,
529
                    $this->customAdminCpCacheOptions()
530
                );
531
            }
532
        );
533
        // Handler: EVENT_BEFORE_SAVE_PLUGIN_SETTINGS
534
        Event::on(
535
            Plugins::class,
536
            Plugins::EVENT_BEFORE_SAVE_PLUGIN_SETTINGS,
537
            function(PluginEvent $event) {
538
                if ($event->plugin === $this && !Craft::$app->getDb()->getSupportsMb4()) {
539
                    // For all the emojis
540
                    $settingsModel = $this->getSettings();
541
                    self::$savingSettings = true;
542
                    if ($settingsModel !== null) {
543
                        $attributes = $settingsModel->attributes();
544
                        if ($attributes !== null) {
545
                            foreach ($attributes as $attribute) {
546
                                if (is_string($settingsModel->$attribute)) {
547
                                    $settingsModel->$attribute =
548
                                        StringHelper::encodeMb4($settingsModel->$attribute);
549
                                }
550
                            }
551
                        }
552
                    }
553
                }
554
            }
555
        );
556
    }
557
558
    /**
559
     * Install global event listeners for all request types
560
     */
561
    protected function installGlobalEventListeners()
562
    {
563
        // Handler: Plugins::EVENT_AFTER_LOAD_PLUGINS
564
        Event::on(
565
            Plugins::class,
566
            Plugins::EVENT_AFTER_LOAD_PLUGINS,
567
            function() {
568
                // Delay registering SEO Elements to give other plugins a chance to load first
569
                $this->seoElements->getAllSeoElementTypes(false);
570
                // Delay installing GQL handlers to give other plugins a chance to register their own first
571
                $this->installGqlHandlers();
572
                // Install these only after all other plugins have loaded
573
                $request = Craft::$app->getRequest();
574
                // Only respond to non-console site requests
575
                if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()) {
576
                    $this->handleSiteRequest();
577
                }
578
                // Respond to Control Panel requests
579
                if ($request->getIsCpRequest() && !$request->getIsConsoleRequest()) {
580
                    $this->handleAdminCpRequest();
581
                }
582
            }
583
        );
584
        // Handler: Fields::EVENT_REGISTER_FIELD_TYPES
585
        Event::on(
586
            Fields::class,
587
            Fields::EVENT_REGISTER_FIELD_TYPES,
588
            function(RegisterComponentTypesEvent $event) {
589
                $event->types[] = SeoSettingsField::class;
590
                $event->types[] = Seomatic_MetaField::class;
591
            }
592
        );
593
        // Handler: Element::EVENT_AFTER_PROPAGATE
594
        Event::on(
595
            Element::class,
596
            Element::EVENT_AFTER_PROPAGATE,
597
            static function(ModelEvent $event) {
598
                Craft::debug(
599
                    'Element::EVENT_AFTER_PROPAGATE',
600
                    __METHOD__
601
                );
602
                /** @var Element $element */
603
                $element = $event->sender;
604
                self::$plugin->metaBundles->invalidateMetaBundleByElement(
605
                    $element,
606
                    $event->isNew
607
                );
608
                if ($event->isNew) {
609
                    self::$plugin->sitemaps->submitSitemapForElement($element);
610
                }
611
            }
612
        );
613
        // Handler: Elements::EVENT_AFTER_DELETE_ELEMENT
614
        Event::on(
615
            Elements::class,
616
            Elements::EVENT_AFTER_DELETE_ELEMENT,
617
            function(ElementEvent $event) {
618
                Craft::debug(
619
                    'Elements::EVENT_AFTER_DELETE_ELEMENT',
620
                    __METHOD__
621
                );
622
                /** @var Element $element */
623
                $element = $event->element;
624
                self::$plugin->metaBundles->invalidateMetaBundleByElement(
625
                    $element,
626
                    false
627
                );
628
            }
629
        );
630
        // Add social media preview targets on Craft 3.2 or later
631
        if (self::$craft32 && Seomatic::$settings->socialMediaPreviewTarget) {
632
            // Handler: Entry::EVENT_REGISTER_PREVIEW_TARGETS
633
            Event::on(
634
                Entry::class,
635
                Entry::EVENT_REGISTER_PREVIEW_TARGETS,
636
                function(RegisterPreviewTargetsEvent $e) {
637
                    /** @var Element $element */
638
                    $element = $e->sender;
639
                    if ($element->uri !== null) {
640
                        $e->previewTargets[] = [
641
                            'label' => '📣 ' . Craft::t('seomatic', 'Social Media Preview'),
642
                            'url' => UrlHelper::siteUrl(self::FRONTEND_PREVIEW_PATH, [
643
                                'elementId' => $element->id,
644
                                'siteId' => $element->siteId,
645
                            ]),
646
                        ];
647
                        // Don't allow the preview to be accessed publicly
648
                        Craft::$app->getSession()->authorize(self::SEOMATIC_PREVIEW_AUTHORIZATION_KEY . $element->id);
649
                    }
650
                }
651
            );
652
        }
653
        // Yii2 Debug Toolbar support
654
        Event::on(
655
            Application::class,
656
            BaseApplication::EVENT_BEFORE_REQUEST,
657
            static function() {
658
                /** @var Module|null $debugModule */
659
                $debugModule = Seomatic::$settings->enableDebugToolbarPanel ? Craft::$app->getModule('debug') : null;
660
661
                if ($debugModule) {
0 ignored issues
show
introduced by
$debugModule is of type yii\debug\Module, thus it always evaluated to true.
Loading history...
662
                    $debugModule->panels['seomatic'] = new SeomaticPanel([
663
                        'id' => 'seomatic',
664
                        'module' => $debugModule,
665
                    ]);
666
                }
667
            }
668
        );
669
        // FeedMe Support
670
        if (class_exists(FeedMe::class)) {
671
            Event::on(
672
                FeedMeFields::class,
673
                FeedMeFields::EVENT_REGISTER_FEED_ME_FIELDS,
674
                function(RegisterFeedMeFieldsEvent $e) {
675
                    Craft::debug(
676
                        'FeedMeFields::EVENT_REGISTER_FEED_ME_FIELDS',
677
                        __METHOD__
678
                    );
679
                    $e->fields[] = SeoSettingsFeedMe::class;
680
                }
681
            );
682
        }
683
        $updateMetaBundles = function($message) {
684
            Craft::debug(
685
                $message,
686
                __METHOD__
687
            );
688
            $seoElementTypes = Seomatic::$plugin->seoElements->getAllSeoElementTypes();
689
            foreach ($seoElementTypes as $seoElementType) {
690
                $metaBundleType = $seoElementType::META_BUNDLE_TYPE ?? '';
691
692
                if ($metaBundleType) {
693
                    Seomatic::$plugin->metaBundles->resaveMetaBundles($metaBundleType);
694
                }
695
            }
696
        };
697
698
        // Handler: Elements::EVENT_AFTER_SAVE_SITE
699
        Event::on(
700
            SitesService::class,
701
            SitesService::EVENT_AFTER_SAVE_SITE,
702
            function() use ($updateMetaBundles) {
703
                $updateMetaBundles('SitesService::EVENT_AFTER_SAVE_SITE');
704
            }
705
        );
706
707
        // Handler: Elements::EVENT_AFTER_DELETE_SITE
708
        Event::on(
709
            SitesService::class,
710
            SitesService::EVENT_AFTER_DELETE_SITE,
711
            function() use ($updateMetaBundles) {
712
                $updateMetaBundles('SitesService::EVENT_AFTER_DELETE_SITE');
713
            }
714
        );
715
    }
716
717
    /**
718
     * Register our GraphQL handlers
719
     *
720
     * @return void
721
     */
722
    protected function installGqlHandlers()
723
    {
724
        // Add native GraphQL support on Craft 3.3 or later
725
        if (self::$craft33) {
726
            // Handler: Gql::EVENT_REGISTER_GQL_TYPES
727
            Event::on(
728
                Gql::class,
729
                Gql::EVENT_REGISTER_GQL_TYPES,
730
                function(RegisterGqlTypesEvent $event) {
731
                    Craft::debug(
732
                        'Gql::EVENT_REGISTER_GQL_TYPES',
733
                        __METHOD__
734
                    );
735
                    $event->types[] = SeomaticInterface::class;
736
                    $event->types[] = SeomaticEnvironmentType::class;
737
                }
738
            );
739
            // Handler: Gql::EVENT_REGISTER_GQL_QUERIES
740
            Event::on(
741
                Gql::class,
742
                Gql::EVENT_REGISTER_GQL_QUERIES,
743
                function(RegisterGqlQueriesEvent $event) {
744
                    Craft::debug(
745
                        'Gql::EVENT_REGISTER_GQL_QUERIES',
746
                        __METHOD__
747
                    );
748
                    $queries = SeomaticQuery::getQueries();
749
                    foreach ($queries as $key => $value) {
750
                        $event->queries[$key] = $value;
751
                    }
752
                }
753
            );
754
            if (self::$craft35) {
755
                // Handler: Gql::EVENT_REGISTER_GQL_SCHEMA_COMPONENTS
756
                Event::on(
757
                    Gql::class,
758
                    Gql::EVENT_REGISTER_GQL_SCHEMA_COMPONENTS,
759
                    function(RegisterGqlSchemaComponentsEvent $event) {
760
                        Craft::debug(
761
                            'Gql::EVENT_REGISTER_GQL_SCHEMA_COMPONENTS',
762
                            __METHOD__
763
                        );
764
                        $label = Craft::t('seomatic', 'Seomatic');
765
                        $event->queries[$label]['seomatic.all:read'] = ['label' => Craft::t('seomatic', 'Query Seomatic data')];
766
                    }
767
                );
768
            }
769
        }
770
        // Add support for querying for SEOmatic metadata inside of element queries
771
        if (self::$craft34) {
772
            // Handler: TypeManager::EVENT_DEFINE_GQL_TYPE_FIELDS
773
            $knownInterfaceNames = self::$plugin->seoElements->getAllSeoElementGqlInterfaceNames();
774
            Event::on(
775
                TypeManager::class,
776
                TypeManager::EVENT_DEFINE_GQL_TYPE_FIELDS,
777
                function(DefineGqlTypeFieldsEvent $event) use ($knownInterfaceNames) {
778
                    if (in_array($event->typeName, $knownInterfaceNames, true)) {
779
                        Craft::debug(
780
                            'TypeManager::EVENT_DEFINE_GQL_TYPE_FIELDS',
781
                            __METHOD__
782
                        );
783
784
                        if (GqlHelper::canQuerySeo()) {
785
                            // Make Seomatic tags available to all entries.
786
                            $event->fields['seomatic'] = [
787
                                'name' => 'seomatic',
788
                                'type' => SeomaticInterface::getType(),
789
                                'args' => SeomaticArguments::getArguments(),
790
                                'resolve' => SeomaticResolver::class . '::resolve',
791
                                'description' => Craft::t('seomatic', 'This query is used to query for SEOmatic meta data.'),
792
                            ];
793
                        }
794
                    }
795
                });
796
        }
797
        // CraftQL Support
798
        if (class_exists(CraftQL::class)) {
799
            Event::on(
800
                Schema::class,
801
                AlterSchemaFields::EVENT,
802
                [GetCraftQLSchema::class, 'handle']
803
            );
804
        }
805
    }
806
807
    /**
808
     * Handle site requests.  We do it only after we receive the event
809
     * EVENT_AFTER_LOAD_PLUGINS so that any pending db migrations can be run
810
     * before our event listeners kick in
811
     */
812
    protected function handleSiteRequest()
813
    {
814
        // Handler: View::EVENT_END_PAGE
815
        Event::on(
816
            View::class,
817
            View::EVENT_END_PAGE,
818
            function() {
819
                Craft::debug(
820
                    'View::EVENT_END_PAGE',
821
                    __METHOD__
822
                );
823
                // The page is done rendering, include our meta containers
824
                if (self::$settings->renderEnabled && self::$seomaticVariable) {
825
                    self::$plugin->metaContainers->includeMetaContainers();
826
                }
827
            }
828
        );
829
    }
830
831
    /**
832
     * Handle Control Panel requests. We do it only after we receive the event
833
     * EVENT_AFTER_LOAD_PLUGINS so that any pending db migrations can be run
834
     * before our event listeners kick in
835
     */
836
    protected function handleAdminCpRequest()
837
    {
838
        // Don't cache Control Panel requests
839
        self::$cacheDuration = 1;
840
        // Prefix the Control Panel title
841
        self::$view->hook('cp.layouts.base', function(&$context) {
842
            if (self::$devMode) {
843
                $context['docTitle'] = self::$settings->devModeCpTitlePrefix . $context['docTitle'];
844
            } else {
845
                $context['docTitle'] = self::$settings->cpTitlePrefix . $context['docTitle'];
846
            }
847
        });
848
    }
849
850
    /**
851
     * Install site event listeners for site requests only
852
     */
853
    protected function installSiteEventListeners()
854
    {
855
        // Load the sitemap containers
856
        self::$plugin->sitemaps->loadSitemapContainers();
857
        // Load the frontend template containers
858
        self::$plugin->frontendTemplates->loadFrontendTemplateContainers();
859
        // Handler: UrlManager::EVENT_REGISTER_SITE_URL_RULES
860
        Event::on(
861
            UrlManager::class,
862
            UrlManager::EVENT_REGISTER_SITE_URL_RULES,
863
            function(RegisterUrlRulesEvent $event) {
864
                Craft::debug(
865
                    'UrlManager::EVENT_REGISTER_SITE_URL_RULES',
866
                    __METHOD__
867
                );
868
                // FileController
869
                $route = self::$plugin->handle . '/file/seo-file-link';
870
                $event->rules[self::FRONTEND_SEO_FILE_LINK] = ['route' => $route];
871
                // PreviewController
872
                $route = self::$plugin->handle . '/preview/social-media';
873
                $event->rules[self::FRONTEND_PREVIEW_PATH] = ['route' => $route];
874
                // Register our Control Panel routes
875
                $event->rules = array_merge(
876
                    $event->rules,
877
                    $this->customFrontendRoutes()
878
                );
879
            }
880
        );
881
    }
882
883
    /**
884
     * Return the custom frontend routes
885
     *
886
     * @return array
887
     */
888
    protected function customFrontendRoutes(): array
889
    {
890
        return [
891
        ];
892
    }
893
894
    /**
895
     * Install site event listeners for Control Panel requests only
896
     */
897
    protected function installCpEventListeners()
898
    {
899
        // Load the frontend template containers
900
        self::$plugin->frontendTemplates->loadFrontendTemplateContainers();
901
        // Handler: UrlManager::EVENT_REGISTER_CP_URL_RULES
902
        Event::on(
903
            UrlManager::class,
904
            UrlManager::EVENT_REGISTER_CP_URL_RULES,
905
            function(RegisterUrlRulesEvent $event) {
906
                Craft::debug(
907
                    'UrlManager::EVENT_REGISTER_CP_URL_RULES',
908
                    __METHOD__
909
                );
910
                // Register our Control Panel routes
911
                $event->rules = array_merge(
912
                    $event->rules,
913
                    $this->customAdminCpRoutes()
914
                );
915
            }
916
        );
917
        // Handler: UserPermissions::EVENT_REGISTER_PERMISSIONS
918
        Event::on(
919
            UserPermissions::class,
920
            UserPermissions::EVENT_REGISTER_PERMISSIONS,
921
            function(RegisterUserPermissionsEvent $event) {
922
                Craft::debug(
923
                    'UserPermissions::EVENT_REGISTER_PERMISSIONS',
924
                    __METHOD__
925
                );
926
                // Register our custom permissions
927
                $event->permissions[Craft::t('seomatic', 'SEOmatic')] = $this->customAdminCpPermissions();
928
            }
929
        );
930
        // Handler: AutocompleteService::EVENT_REGISTER_CODEEDITOR_AUTOCOMPLETES
931
        Event::on(AutocompleteService::class, AutocompleteService::EVENT_REGISTER_CODEEDITOR_AUTOCOMPLETES,
932
            function(RegisterCodeEditorAutocompletesEvent $event) {
933
                if ($event->fieldType === self::SEOMATIC_EXPRESSION_FIELD_TYPE) {
934
                    $event->types[] = EnvironmentVariableAutocomplete::class;
935
                }
936
                if ($event->fieldType === self::SEOMATIC_TRACKING_FIELD_TYPE) {
937
                    $event->types[] = TrackingVarsAutocomplete::class;
938
                }
939
            }
940
        );
941
        // Handler: TwigTemplateValidator::EVENT_REGISTER_TWIG_VALIDATOR_VARIABLES
942
        Event::on(TwigTemplateValidator::class,
943
            TwigTemplateValidator::EVENT_REGISTER_TWIG_VALIDATOR_VARIABLES,
944
            function(RegisterTwigValidatorVariablesEvent $event) {
945
                if (Seomatic::$seomaticVariable === null) {
946
                    Seomatic::$seomaticVariable = new SeomaticVariable();
947
                    Seomatic::$plugin->metaContainers->loadGlobalMetaContainers();
948
                    Seomatic::$seomaticVariable->init();
949
                }
950
                $event->variables['seomatic'] = Seomatic::$seomaticVariable;
951
            }
952
        );
953
    }
954
955
    /**
956
     * Return the custom Control Panel routes
957
     *
958
     * @return array
959
     */
960
    protected function customAdminCpRoutes(): array
961
    {
962
        return [
963
            'seomatic' =>
964
                '',
965
            'seomatic/dashboard' =>
966
                'seomatic/settings/dashboard',
967
            'seomatic/dashboard/<siteHandle:{handle}>' =>
968
                'seomatic/settings/dashboard',
969
970
            'seomatic/global' => [
971
                'route' => 'seomatic/settings/global',
972
                'defaults' => ['subSection' => 'general'],
973
            ],
974
            'seomatic/global/<subSection:{handle}>' =>
975
                'seomatic/settings/global',
976
            'seomatic/global/<subSection:{handle}>/<siteHandle:{handle}>' =>
977
                'seomatic/settings/global',
978
979
            'seomatic/content' =>
980
                'seomatic/settings/content',
981
            'seomatic/content/<siteHandle:{handle}>' =>
982
                'seomatic/settings/content',
983
984
            'seomatic/edit-content/<subSection:{handle}>/<sourceBundleType:{handle}>/<sourceHandle:{handle}>' =>
985
                'seomatic/settings/edit-content',
986
            'seomatic/edit-content/<subSection:{handle}>/<sourceBundleType:{handle}>/<sourceHandle:{handle}>/<siteHandle:{handle}>' =>
987
                'seomatic/settings/edit-content',
988
989
            // Seemingly duplicate route needed to handle Solspace Calendar, which allows characters like -'s
990
            // in their handles
991
            'seomatic/edit-content/<subSection:{handle}>/<sourceBundleType:{handle}>/<sourceHandle:{slug}>' =>
992
                'seomatic/settings/edit-content',
993
            'seomatic/edit-content/<subSection:{handle}>/<sourceBundleType:{handle}>/<sourceHandle:{slug}>/<siteHandle:{handle}>' =>
994
                'seomatic/settings/edit-content',
995
996
            'seomatic/site' => [
997
                'route' => 'seomatic/settings/site',
998
                'defaults' => ['subSection' => 'identity'],
999
            ],
1000
            'seomatic/site/<subSection:{handle}>' =>
1001
                'seomatic/settings/site',
1002
            'seomatic/site/<subSection:{handle}>/<siteHandle:{handle}>' =>
1003
                'seomatic/settings/site',
1004
1005
            'seomatic/tracking' => [
1006
                'route' => 'seomatic/settings/tracking',
1007
                'defaults' => ['subSection' => 'googleAnalytics'],
1008
            ],
1009
            'seomatic/tracking/<subSection:{handle}>' =>
1010
                'seomatic/settings/tracking',
1011
            'seomatic/tracking/<subSection:{handle}>/<siteHandle:{handle}>' =>
1012
                'seomatic/settings/tracking',
1013
1014
            'seomatic/plugin' =>
1015
                'seomatic/settings/plugin',
1016
        ];
1017
    }
1018
1019
    /**
1020
     * Returns the custom Control Panel user permissions.
1021
     *
1022
     * @return array
1023
     */
1024
    protected function customAdminCpPermissions(): array
1025
    {
1026
        // The script meta containers for the global meta bundle
1027
        try {
1028
            $currentSiteId = Craft::$app->getSites()->getCurrentSite()->id ?? 1;
1029
        } catch (SiteNotFoundException $e) {
1030
            $currentSiteId = 1;
1031
        }
1032
        // Dynamic permissions for the scripts
1033
        $metaBundle = self::$plugin->metaBundles->getGlobalMetaBundle($currentSiteId);
1034
        $scriptsPerms = [];
1035
        if ($metaBundle !== null) {
1036
            $scripts = self::$plugin->metaBundles->getContainerDataFromBundle(
1037
                $metaBundle,
1038
                MetaScriptContainer::CONTAINER_TYPE
1039
            );
1040
            foreach ($scripts as $scriptHandle => $scriptData) {
1041
                $scriptsPerms["seomatic:tracking-scripts:${scriptHandle}"] = [
1042
                    'label' => Craft::t('seomatic', $scriptData->name),
1043
                ];
1044
            }
1045
        }
1046
1047
        return [
1048
            'seomatic:dashboard' => [
1049
                'label' => Craft::t('seomatic', 'Dashboard'),
1050
            ],
1051
            'seomatic:global-meta' => [
1052
                'label' => Craft::t('seomatic', 'Edit Global Meta'),
1053
                'nested' => [
1054
                    'seomatic:global-meta:general' => [
1055
                        'label' => Craft::t('seomatic', 'General'),
1056
                    ],
1057
                    'seomatic:global-meta:twitter' => [
1058
                        'label' => Craft::t('seomatic', 'Twitter'),
1059
                    ],
1060
                    'seomatic:global-meta:facebook' => [
1061
                        'label' => Craft::t('seomatic', 'Facebook'),
1062
                    ],
1063
                    'seomatic:global-meta:robots' => [
1064
                        'label' => Craft::t('seomatic', 'Robots'),
1065
                    ],
1066
                    'seomatic:global-meta:humans' => [
1067
                        'label' => Craft::t('seomatic', 'Humans'),
1068
                    ],
1069
                    'seomatic:global-meta:ads' => [
1070
                        'label' => Craft::t('seomatic', 'Ads'),
1071
                    ],
1072
                    'seomatic:global-meta:security' => [
1073
                        'label' => Craft::t('seomatic', 'Security'),
1074
                    ],
1075
                ],
1076
            ],
1077
            'seomatic:content-meta' => [
1078
                'label' => Craft::t('seomatic', 'Edit Content SEO'),
1079
                'nested' => [
1080
                    'seomatic:content-meta:general' => [
1081
                        'label' => Craft::t('seomatic', 'General'),
1082
                    ],
1083
                    'seomatic:content-meta:twitter' => [
1084
                        'label' => Craft::t('seomatic', 'Twitter'),
1085
                    ],
1086
                    'seomatic:content-meta:facebook' => [
1087
                        'label' => Craft::t('seomatic', 'Facebook'),
1088
                    ],
1089
                    'seomatic:content-meta:sitemap' => [
1090
                        'label' => Craft::t('seomatic', 'Sitemap'),
1091
                    ],
1092
                ],
1093
            ],
1094
            'seomatic:site-settings' => [
1095
                'label' => Craft::t('seomatic', 'Edit Site Settings'),
1096
                'nested' => [
1097
                    'seomatic:site-settings:identity' => [
1098
                        'label' => Craft::t('seomatic', 'Identity'),
1099
                    ],
1100
                    'seomatic:site-settings:creator' => [
1101
                        'label' => Craft::t('seomatic', 'Creator'),
1102
                    ],
1103
                    'seomatic:site-settings:social' => [
1104
                        'label' => Craft::t('seomatic', 'Social Media'),
1105
                    ],
1106
                    'seomatic:site-settings:sitemap' => [
1107
                        'label' => Craft::t('seomatic', 'Sitemap'),
1108
                    ],
1109
                    'seomatic:site-settings:miscellaneous' => [
1110
                        'label' => Craft::t('seomatic', 'Miscellaneous'),
1111
                    ],
1112
                ],
1113
            ],
1114
            'seomatic:tracking-scripts' => [
1115
                'label' => Craft::t('seomatic', 'Edit Tracking Scripts'),
1116
                'nested' => $scriptsPerms,
1117
            ],
1118
            'seomatic:plugin-settings' => [
1119
                'label' => Craft::t('seomatic', 'Edit Plugin Settings'),
1120
            ],
1121
        ];
1122
    }
1123
1124
    /**
1125
     * Returns the custom Control Panel cache options.
1126
     *
1127
     * @return array
1128
     */
1129
    protected function customAdminCpCacheOptions(): array
1130
    {
1131
        return [
1132
            // Frontend template caches
1133
            [
1134
                'key' => 'seomatic-frontendtemplate-caches',
1135
                'label' => Craft::t('seomatic', 'SEOmatic frontend template caches'),
1136
                'action' => [self::$plugin->frontendTemplates, 'invalidateCaches'],
1137
            ],
1138
            // Meta bundle caches
1139
            [
1140
                'key' => 'seomatic-metabundle-caches',
1141
                'label' => Craft::t('seomatic', 'SEOmatic metadata caches'),
1142
                'action' => [self::$plugin->metaContainers, 'invalidateCaches'],
1143
            ],
1144
            // Sitemap caches
1145
            [
1146
                'key' => 'seomatic-sitemap-caches',
1147
                'label' => Craft::t('seomatic', 'SEOmatic sitemap caches'),
1148
                'action' => [self::$plugin->sitemaps, 'invalidateCaches'],
1149
            ],
1150
            // Schema caches
1151
            [
1152
                'key' => 'seomatic-schema-caches',
1153
                'label' => Craft::t('seomatic', 'SEOmatic schema caches'),
1154
                'action' => [SchemaHelper::class, 'invalidateCaches'],
1155
            ],
1156
        ];
1157
    }
1158
1159
    /**
1160
     * @inheritdoc
1161
     */
1162
    protected function createSettingsModel()
1163
    {
1164
        return new Settings();
1165
    }
1166
}
1167