Test Failed
Push — develop ( 364a8f...bcf63f )
by Andrew
10:39
created

Seomatic::installGlobalEventListeners()   D

Complexity

Conditions 15
Paths 32

Size

Total Lines 162
Code Lines 101

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 240

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 15
eloc 101
c 1
b 0
f 0
nc 32
nop 0
dl 0
loc 162
ccs 0
cts 95
cp 0
crap 240
rs 4.7333

How to fix   Long Method    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;
13
14
use nystudio107\seomatic\assetbundles\seomatic\SeomaticAsset;
15
use nystudio107\seomatic\fields\SeoSettings as SeoSettingsField;
16
use nystudio107\seomatic\fields\Seomatic_Meta as Seomatic_MetaField;
17
use nystudio107\seomatic\gql\arguments\SeomaticArguments;
18
use nystudio107\seomatic\gql\interfaces\SeomaticInterface;
19
use nystudio107\seomatic\gql\resolvers\SeomaticResolver;
20
use nystudio107\seomatic\gql\queries\SeomaticQuery;
21
use nystudio107\seomatic\helpers\Environment as EnvironmentHelper;
22
use nystudio107\seomatic\helpers\MetaValue as MetaValueHelper;
23
use nystudio107\seomatic\integrations\feedme\SeoSettings as SeoSettingsFeedMe;
24
use nystudio107\seomatic\listeners\GetCraftQLSchema;
25
use nystudio107\seomatic\models\MetaScriptContainer;
26
use nystudio107\seomatic\models\Settings;
27
use nystudio107\seomatic\services\FrontendTemplates as FrontendTemplatesService;
28
use nystudio107\seomatic\services\Helper as HelperService;
29
use nystudio107\seomatic\services\JsonLd as JsonLdService;
30
use nystudio107\seomatic\services\Link as LinkService;
31
use nystudio107\seomatic\services\MetaBundles as MetaBundlesService;
32
use nystudio107\seomatic\services\MetaContainers as MetaContainersService;
33
use nystudio107\seomatic\services\Script as ScriptService;
34
use nystudio107\seomatic\services\SeoElements as SeoElementsService;
35
use nystudio107\seomatic\services\Sitemaps as SitemapsService;
36
use nystudio107\seomatic\services\Tag as TagService;
37
use nystudio107\seomatic\services\Title as TitleService;
38
use nystudio107\seomatic\twigextensions\SeomaticTwigExtension;
39
use nystudio107\seomatic\variables\SeomaticVariable;
40
41
use nystudio107\pluginmanifest\services\ManifestService;
42
43
use nystudio107\fastcgicachebust\FastcgiCacheBust;
0 ignored issues
show
Bug introduced by
The type nystudio107\fastcgicachebust\FastcgiCacheBust was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
44
45
use Craft;
46
use craft\base\Element;
47
use craft\base\ElementInterface;
48
use craft\base\Plugin;
49
use craft\elements\User;
50
use craft\elements\Entry;
51
use craft\errors\SiteNotFoundException;
52
use craft\events\ElementEvent;
53
use craft\events\PluginEvent;
54
use craft\events\DefineGqlTypeFieldsEvent;
55
use craft\events\RegisterCacheOptionsEvent;
56
use craft\events\RegisterComponentTypesEvent;
57
use craft\events\RegisterGqlQueriesEvent;
58
use craft\events\RegisterGqlTypesEvent;
59
use craft\events\RegisterPreviewTargetsEvent;
60
use craft\events\RegisterUrlRulesEvent;
61
use craft\events\RegisterUserPermissionsEvent;
62
use craft\gql\TypeManager;
63
use craft\helpers\StringHelper;
64
use craft\services\Elements;
65
use craft\services\Fields;
66
use craft\services\Gql;
67
use craft\services\Plugins;
68
use craft\services\UserPermissions;
69
use craft\helpers\UrlHelper;
70
use craft\utilities\ClearCaches;
71
use craft\web\UrlManager;
72
use craft\web\View;
73
74
use craft\feedme\Plugin as FeedMe;
0 ignored issues
show
Bug introduced by
The type craft\feedme\Plugin was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
75
use craft\feedme\events\RegisterFeedMeFieldsEvent;
0 ignored issues
show
Bug introduced by
The type craft\feedme\events\RegisterFeedMeFieldsEvent was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
76
use craft\feedme\services\Fields as FeedMeFields;
0 ignored issues
show
Bug introduced by
The type craft\feedme\services\Fields was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
77
78
use markhuot\CraftQL\Builders\Schema;
0 ignored issues
show
Bug introduced by
The type markhuot\CraftQL\Builders\Schema was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
79
use markhuot\CraftQL\CraftQL;
0 ignored issues
show
Bug introduced by
The type markhuot\CraftQL\CraftQL was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
80
use markhuot\CraftQL\Events\AlterSchemaFields;
0 ignored issues
show
Bug introduced by
The type markhuot\CraftQL\Events\AlterSchemaFields was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
81
82
use yii\base\Event;
83
84
/** @noinspection MissingPropertyAnnotationsInspection */
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...
85
86
/**
87
 * Class Seomatic
88
 *
89
 * @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...
90
 * @package   Seomatic
91
 * @since     3.0.0
92
 *
93
 * @property FrontendTemplatesService $frontendTemplates
94
 * @property HelperService            $helper
95
 * @property JsonLdService            $jsonLd
96
 * @property LinkService              $link
97
 * @property ManifestService           $manifest
98
 * @property MetaBundlesService       $metaBundles
99
 * @property MetaContainersService    $metaContainers
100
 * @property ScriptService            $script
101
 * @property SeoElementsService       $seoElements
102
 * @property SitemapsService          $sitemaps
103
 * @property TagService               $tag
104
 * @property TitleService             $title
105
 */
106
class Seomatic extends Plugin
107
{
108
    // Constants
109
    // =========================================================================
110
111
    const SEOMATIC_HANDLE = 'Seomatic';
112
113
    const DEVMODE_CACHE_DURATION = 30;
114
115
    const FRONTEND_SEO_FILE_LINK = 'seomatic/seo-file-link/<url:[^\/]+>/<robots:[^\/]+>/<canonical:[^\/]+>/<inline:\d+>/<fileName:[-\w\.*]+>';
116
117
    const FRONTEND_PREVIEW_PATH = 'seomatic/preview-social-media';
118
119
    const SEOMATIC_PREVIEW_AUTHORIZATION_KEY = 'seomaticPreviewAuthorizationKey';
120
121
    const GQL_ELEMENT_INTERFACES = [
122
        'EntryInterface',
123
        'CategoryInterface',
124
        'ProductInterface',
125
    ];
126
127
    // Static Properties
128
    // =========================================================================
129
130
    /**
131
     * @var Seomatic
132
     */
133
    public static $plugin;
134
135
    /**
136
     * @var SeomaticVariable
137
     */
138
    public static $seomaticVariable;
139
140
    /**
141
     * @var Settings
142
     */
143
    public static $settings;
144
145
    /**
146
     * @var ElementInterface
147
     */
148
    public static $matchedElement;
149
150
    /**
151
     * @var bool
152
     */
153
    public static $devMode;
154
155
    /**
156
     * @var View
157
     */
158
    public static $view;
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 $craft31 = false;
194
195
    /**
196
     * @var bool
197
     */
198
    public static $craft32 = false;
199
200
    /**
201
     * @var bool
202
     */
203
    public static $craft33 = false;
204
205
    /**
206
     * @var bool
207
     */
208
    public static $craft34 = false;
209
210
    // Static Methods
211
    // =========================================================================
212
213
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $config should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $id should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $parent should have a doc-comment as per coding-style.
Loading history...
214
     * @inheritdoc
215
     */
216
    public function __construct($id, $parent = null, array $config = [])
217
    {
218
        $config['components'] = [
219
            'frontendTemplates' => FrontendTemplatesService::class,
220
            'helper' => HelperService::class,
221
            'jsonLd' => JsonLdService::class,
222
            'link' => LinkService::class,
223
            'manifest' => ManifestService::class,
224
            'metaBundles' => MetaBundlesService::class,
225
            'metaContainers' => MetaContainersService::class,
226
            'script' => ScriptService::class,
227
            'seoElements' => SeoElementsService::class,
228
            'sitemaps' => SitemapsService::class,
229
            'tag' => TagService::class,
230
            'title' => TitleService::class,
231
        ];
232
233
        parent::__construct($id, $parent, $config);
234
    }
235
236
    /**
237
     * Set the matched element
238
     *
239
     * @param $element null|ElementInterface
240
     */
241
    public static function setMatchedElement($element)
242
    {
243
        self::$matchedElement = $element;
244
        /** @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...
245
        if ($element) {
0 ignored issues
show
introduced by
$element is of type craft\base\Element, thus it always evaluated to true.
Loading history...
246
            self::$language = MetaValueHelper::getSiteLanguage($element->siteId);
247
        } else {
248
            self::$language = MetaValueHelper::getSiteLanguage(null);
249
        }
250
        MetaValueHelper::cache();
251
    }
252
253
    // Public Properties
254
    // =========================================================================
255
256
    /**
257
     * @var string
258
     */
259
    public $schemaVersion = '3.0.9';
260
261
    /**
262
     * @var bool
263
     */
264
    public $hasCpSection = true;
265
266
    /**
267
     * @var bool
268
     */
269
    public $hasCpSettings = true;
270
271
    // Public Methods
272
    // =========================================================================
273
274
    /**
275
     * @inheritdoc
276
     */
277
    public function init()
278
    {
279
        parent::init();
280
        self::$plugin = $this;
281
        // Handle any console commands
282
        $request = Craft::$app->getRequest();
283
        if ($request->getIsConsoleRequest()) {
284
            $this->controllerNamespace = 'nystudio107\seomatic\console\controllers';
285
        }
286
        // Initialize properties
287
        self::$settings = self::$plugin->getSettings();
288
        self::$devMode = Craft::$app->getConfig()->getGeneral()->devMode;
289
        self::$view = Craft::$app->getView();
290
        self::$cacheDuration = self::$devMode
291
            ? self::DEVMODE_CACHE_DURATION
292
            : self::$settings->metaCacheDuration ?? null;
293
        self::$cacheDuration = self::$cacheDuration === null ? null : (int)self::$cacheDuration;
294
        self::$environment = EnvironmentHelper::determineEnvironment();
295
        MetaValueHelper::cache();
296
        // Version helpers
297
        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...
298
        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...
299
        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...
300
        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...
301
        $this->name = self::$settings->pluginName;
302
        // Install our event listeners
303
        $this->installEventListeners();
304
        // We're loaded
305
        Craft::info(
306
            Craft::t(
307
                'seomatic',
308
                '{name} plugin loaded',
309
                ['name' => $this->name]
310
            ),
311
            __METHOD__
312
        );
313
    }
314
315
    /**
316
     * @inheritdoc
317
     */
318
    public function getSettings()
319
    {
320
        // For all the emojis
321
        $settingsModel = parent::getSettings();
322
        if ($settingsModel !== null && !self::$savingSettings) {
323
            $attributes = $settingsModel->attributes();
324
            if ($attributes !== null) {
0 ignored issues
show
introduced by
The condition $attributes !== null is always true.
Loading history...
325
                foreach ($attributes as $attribute) {
326
                    if (is_string($settingsModel->$attribute)) {
327
                        $settingsModel->$attribute = html_entity_decode(
328
                            $settingsModel->$attribute,
329
                            ENT_NOQUOTES,
330
                            'UTF-8'
331
                        );
332
                    }
333
                }
334
            }
335
            self::$savingSettings = false;
336
        }
337
338
        return $settingsModel;
339
    }
340
341
    /**
342
     * @inheritdoc
343
     */
344
    public function getSettingsResponse()
345
    {
346
        // Just redirect to the plugin settings page
347
        Craft::$app->getResponse()->redirect(UrlHelper::cpUrl('seomatic/plugin'));
348
    }
349
350
    /**
351
     * @inheritdoc
352
     */
353
    public function getCpNavItem()
354
    {
355
        $subNavs = [];
356
        $navItem = parent::getCpNavItem();
357
        /** @var User $currentUser */
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...
358
        $request = Craft::$app->getRequest();
359
        $siteSuffix = '';
360
        if ($request->getSegment(1) === 'seomatic') {
361
            $segments = $request->getSegments();
362
            $lastSegment = end($segments);
363
            $site = Craft::$app->getSites()->getSiteByHandle($lastSegment);
364
            if ($site !== null) {
365
                $siteSuffix = '/'.$lastSegment;
366
            }
367
        }
368
        $currentUser = Craft::$app->getUser()->getIdentity();
369
        // Only show sub-navs the user has permission to view
370
        if ($currentUser->can('seomatic:dashboard')) {
0 ignored issues
show
Bug introduced by
The method can() does not exist on yii\web\IdentityInterface. It seems like you code against a sub-type of yii\web\IdentityInterface such as craft\elements\User. ( Ignorable by Annotation )

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

370
        if ($currentUser->/** @scrutinizer ignore-call */ can('seomatic:dashboard')) {
Loading history...
371
            $subNavs['dashboard'] = [
372
                'label' => Craft::t('seomatic', 'Dashboard'),
373
                'url' => 'seomatic/dashboard'.$siteSuffix,
374
            ];
375
        }
376
        if ($currentUser->can('seomatic:global-meta')) {
377
            $subNavs['global'] = [
378
                'label' => Craft::t('seomatic', 'Global SEO'),
379
                'url' => 'seomatic/global/general'.$siteSuffix,
380
            ];
381
        }
382
        if ($currentUser->can('seomatic:content-meta')) {
383
            $subNavs['content'] = [
384
                'label' => Craft::t('seomatic', 'Content SEO'),
385
                'url' => 'seomatic/content'.$siteSuffix,
386
            ];
387
        }
388
        if ($currentUser->can('seomatic:site-settings')) {
389
            $subNavs['site'] = [
390
                'label' => Craft::t('seomatic', 'Site Settings'),
391
                'url' => 'seomatic/site/identity'.$siteSuffix,
392
            ];
393
        }
394
        if ($currentUser->can('seomatic:tracking-scripts')) {
395
            $subNavs['tracking'] = [
396
                'label' => Craft::t('seomatic', 'Tracking Scripts'),
397
                'url' => 'seomatic/tracking/googleAnalytics'.$siteSuffix,
398
            ];
399
        }
400
        $editableSettings = true;
401
        $general = Craft::$app->getConfig()->getGeneral();
402
        if (self::$craft31 && !$general->allowAdminChanges) {
403
            $editableSettings = false;
404
        }
405
        if ($currentUser->can('seomatic:plugin-settings') && $editableSettings) {
406
            $subNavs['plugin'] = [
407
                'label' => Craft::t('seomatic', 'Plugin Settings'),
408
                'url' => 'seomatic/plugin',
409
            ];
410
        }
411
        $navItem = array_merge($navItem, [
412
            'subnav' => $subNavs,
413
        ]);
414
415
        return $navItem;
416
    }
417
418
    /**
419
     * Clear all the caches!
420
     */
421
    public function clearAllCaches()
422
    {
423
        // Clear all of SEOmatic's caches
424
        self::$plugin->frontendTemplates->invalidateCaches();
425
        self::$plugin->metaContainers->invalidateCaches();
426
        self::$plugin->sitemaps->invalidateCaches();
427
        // If they are using Craft 3.3 or later, clear the GraphQL caches too
428
        if (self::$craft33) {
429
            $gql = Craft::$app->getGql();
430
            if (method_exists($gql, 'invalidateCaches')) {
431
                $gql->invalidateCaches();
432
            }
433
        }
434
        // If the FastCGI Cache Bust plugin is installed, clear its caches too
435
        $plugin = Craft::$app->getPlugins()->getPlugin('fastcgi-cache-bust');
436
        if ($plugin !== null) {
437
            FastcgiCacheBust::$plugin->cache->clearAll();
438
        }
439
    }
440
441
    /**
442
     * Determine whether our table schema exists or not; this is needed because
443
     * migrations such as the install migration and base_install migration may
444
     * not have been run by the time our init() method has been called
445
     *
446
     * @return bool
447
     */
448
    public function migrationsAndSchemaReady(): bool
449
    {
450
        $pluginsService = Craft::$app->getPlugins();
451
        if ($pluginsService->doesPluginRequireDatabaseUpdate(self::$plugin)) {
452
            return false;
453
        }
454
        if (Craft::$app->db->schema->getTableSchema('{{%seomatic_metabundles}}') === null) {
455
            return false;
456
        }
457
458
        return true;
459
    }
460
461
    // Protected Methods
462
    // =========================================================================
463
464
    /**
465
     * Install our event listeners.
466
     */
467
    protected function installEventListeners()
468
    {
469
        // Register the manifest service
470
        $this->set('manifest', [
471
            'class' => ManifestService::class,
472
            'assetClass' => SeomaticAsset::class,
473
            'devServerManifestPath' => 'http://craft-seomatic-buildchain:8080/',
474
            'devServerPublicPath' => 'http://craft-seomatic-buildchain:8080/',
475
        ]);
476
477
        // Install our event listeners only if our table schema exists
478
        if ($this->migrationsAndSchemaReady()) {
479
            // Add in our Twig extensions
480
            self::$view->registerTwigExtension(new SeomaticTwigExtension);
481
            $request = Craft::$app->getRequest();
482
            // Add in our event listeners that are needed for every request
483
            $this->installGlobalEventListeners();
484
            // Install only for non-console site requests
485
            if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()) {
486
                $this->installSiteEventListeners();
487
            }
488
            // Install only for non-console Control Panel requests
489
            if ($request->getIsCpRequest() && !$request->getIsConsoleRequest()) {
490
                $this->installCpEventListeners();
491
            }
492
        }
493
        // Handler: EVENT_AFTER_INSTALL_PLUGIN
494
        Event::on(
495
            Plugins::class,
496
            Plugins::EVENT_AFTER_INSTALL_PLUGIN,
497
            function (PluginEvent $event) {
498
                if ($event->plugin === $this) {
499
                    // Invalidate our caches after we've been installed
500
                    $this->clearAllCaches();
501
                    // Send them to our welcome screen
502
                    $request = Craft::$app->getRequest();
503
                    if ($request->isCpRequest) {
504
                        Craft::$app->getResponse()->redirect(UrlHelper::cpUrl(
505
                            'seomatic/dashboard',
506
                            [
507
                                'showWelcome' => true,
508
                            ]
509
                        ))->send();
510
                    }
511
                }
512
            }
513
        );
514
        // Handler: ClearCaches::EVENT_REGISTER_CACHE_OPTIONS
515
        Event::on(
516
            ClearCaches::class,
517
            ClearCaches::EVENT_REGISTER_CACHE_OPTIONS,
518
            function (RegisterCacheOptionsEvent $event) {
519
                Craft::debug(
520
                    'ClearCaches::EVENT_REGISTER_CACHE_OPTIONS',
521
                    __METHOD__
522
                );
523
                // Register our Cache Options
524
                $event->options = array_merge(
525
                    $event->options,
526
                    $this->customAdminCpCacheOptions()
527
                );
528
            }
529
        );
530
        // Handler: EVENT_BEFORE_SAVE_PLUGIN_SETTINGS
531
        Event::on(
532
            Plugins::class,
533
            Plugins::EVENT_BEFORE_SAVE_PLUGIN_SETTINGS,
534
            function (PluginEvent $event) {
535
                if ($event->plugin === $this && !Craft::$app->getDb()->getSupportsMb4()) {
536
                    // For all the emojis
537
                    $settingsModel = $this->getSettings();
538
                    self::$savingSettings = true;
539
                    if ($settingsModel !== null) {
540
                        $attributes = $settingsModel->attributes();
541
                        if ($attributes !== null) {
542
                            foreach ($attributes as $attribute) {
543
                                if (is_string($settingsModel->$attribute)) {
544
                                    $settingsModel->$attribute =
0 ignored issues
show
Coding Style introduced by
Multi-line assignments must have the equal sign on the second line
Loading history...
545
                                        StringHelper::encodeMb4($settingsModel->$attribute);
546
                                }
547
                            }
548
                        }
549
                    }
550
                }
551
            }
552
        );
553
    }
554
555
    /**
556
     * Install global event listeners for all request types
557
     */
558
    protected function installGlobalEventListeners()
559
    {
560
        // Handler: Plugins::EVENT_AFTER_LOAD_PLUGINS
561
        Event::on(
562
            Plugins::class,
563
            Plugins::EVENT_AFTER_LOAD_PLUGINS,
564
            function () {
565
                // Install these only after all other plugins have loaded
566
                $request = Craft::$app->getRequest();
567
                // Allow the SeoElements to register their own event handlers
568
                self::$plugin->seoElements->getAllSeoElementTypes();
569
                // Only respond to non-console site requests
570
                if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()) {
571
                    $this->handleSiteRequest();
572
                }
573
                // Respond to Control Panel requests
574
                if ($request->getIsCpRequest() && !$request->getIsConsoleRequest()) {
575
                    $this->handleAdminCpRequest();
576
                }
577
            }
578
        );
579
        // Handler: Fields::EVENT_REGISTER_FIELD_TYPES
580
        Event::on(
581
            Fields::class,
582
            Fields::EVENT_REGISTER_FIELD_TYPES,
583
            function (RegisterComponentTypesEvent $event) {
584
                $event->types[] = SeoSettingsField::class;
585
                $event->types[] = Seomatic_MetaField::class;
586
            }
587
        );
588
        // Handler: Elements::EVENT_AFTER_SAVE_ELEMENT
589
        Event::on(
590
            Elements::class,
591
            Elements::EVENT_AFTER_SAVE_ELEMENT,
592
            function (ElementEvent $event) {
593
                Craft::debug(
594
                    'Elements::EVENT_AFTER_SAVE_ELEMENT',
595
                    __METHOD__
596
                );
597
                /** @var  $element Element */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
598
                $element = $event->element;
599
                self::$plugin->metaBundles->invalidateMetaBundleByElement(
600
                    $element,
601
                    $event->isNew
602
                );
603
                if ($event->isNew) {
604
                    self::$plugin->sitemaps->submitSitemapForElement($element);
605
                }
606
            }
607
        );
608
        // Handler: Elements::EVENT_AFTER_DELETE_ELEMENT
609
        Event::on(
610
            Elements::class,
611
            Elements::EVENT_AFTER_DELETE_ELEMENT,
612
            function (ElementEvent $event) {
613
                Craft::debug(
614
                    'Elements::EVENT_AFTER_DELETE_ELEMENT',
615
                    __METHOD__
616
                );
617
                /** @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...
618
                $element = $event->element;
619
                self::$plugin->metaBundles->invalidateMetaBundleByElement(
620
                    $element,
621
                    false
622
                );
623
            }
624
        );
625
        // Add social media preview targets on Craft 3.2 or later
626
        if (self::$craft32 && Seomatic::$settings->socialMediaPreviewTarget) {
627
            // Handler: Entry::EVENT_REGISTER_PREVIEW_TARGETS
628
            Event::on(
629
                Entry::class,
630
                Entry::EVENT_REGISTER_PREVIEW_TARGETS,
631
                function (RegisterPreviewTargetsEvent $e) {
632
                    /** @var Element $element */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
633
                    $element = $e->sender;
634
                    if ($element->uri !== null) {
635
                        $e->previewTargets[] = [
636
                            'label' => '📣 '.Craft::t('seomatic', 'Social Media Preview'),
637
                            'url' => UrlHelper::siteUrl(self::FRONTEND_PREVIEW_PATH, [
638
                                'elementId' => $element->id,
639
                                'siteId' => $element->siteId,
640
                            ]),
641
                        ];
642
                        // Don't allow the preview to be accessed publicly
643
                        Craft::$app->getSession()->authorize(self::SEOMATIC_PREVIEW_AUTHORIZATION_KEY.$element->id);
644
                    }
645
                }
646
            );
647
        }
648
        // Add native GraphQL support on Craft 3.3 or later
649
        if (self::$craft33) {
650
            // Handler: Gql::EVENT_REGISTER_GQL_TYPES
651
            Event::on(
652
                Gql::class,
653
                Gql::EVENT_REGISTER_GQL_TYPES,
654
                function (RegisterGqlTypesEvent $event) {
655
                    Craft::debug(
656
                        'Gql::EVENT_REGISTER_GQL_TYPES',
657
                        __METHOD__
658
                    );
659
                    $event->types[] = SeomaticInterface::class;
660
                }
661
            );
662
            // Handler: Gql::EVENT_REGISTER_GQL_QUERIES
663
            Event::on(
664
                Gql::class,
665
                Gql::EVENT_REGISTER_GQL_QUERIES,
666
                function (RegisterGqlQueriesEvent $event) {
667
                    Craft::debug(
668
                        'Gql::EVENT_REGISTER_GQL_QUERIES',
669
                        __METHOD__
670
                    );
671
                    $queries = SeomaticQuery::getQueries();
672
                    foreach ($queries as $key => $value) {
673
                        $event->queries[$key] = $value;
674
                    }
675
                }
676
            );
677
        }
678
        // Add support for querying for SEOmatic metadata inside of element queries
679
        if (self::$craft34) {
680
            // Handler: TypeManager::EVENT_DEFINE_GQL_TYPE_FIELDS
681
            Event::on(
682
                TypeManager::class,
683
                TypeManager::EVENT_DEFINE_GQL_TYPE_FIELDS,
684
                function (DefineGqlTypeFieldsEvent $event) {
685
                if (in_array($event->typeName, self::GQL_ELEMENT_INTERFACES, true)) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 16
Loading history...
686
                    Craft::debug(
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 24 spaces, found 20
Loading history...
687
                        'TypeManager::EVENT_DEFINE_GQL_TYPE_FIELDS',
688
                        __METHOD__
689
                    );
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 28 spaces, found 20
Loading history...
690
                    // Make Seomatic tags available to all entries.
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 28 spaces, found 20
Loading history...
691
                    $event->fields['seomatic'] = [
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 28 spaces, found 20
Loading history...
692
                        'name' => 'seomatic',
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 28 spaces, found 24
Loading history...
693
                        'type' => SeomaticInterface::getType(),
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 28 spaces, found 24
Loading history...
694
                        'args' => SeomaticArguments::getArguments(),
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 28 spaces, found 24
Loading history...
695
                        'resolve' => SeomaticResolver::class . '::resolve',
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 28 spaces, found 24
Loading history...
696
                        'description' => Craft::t('seomatic', 'This query is used to query for SEOmatic meta data.')
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 28 spaces, found 24
Loading history...
697
                    ];
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 28 spaces, found 20
Loading history...
698
                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 16
Loading history...
699
            });
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 12
Loading history...
Coding Style introduced by
Closing brace indented incorrectly; expected 16 spaces, found 12
Loading history...
700
        }
701
        // CraftQL Support
702
        if (class_exists(CraftQL::class)) {
703
            Event::on(
704
                Schema::class,
705
                AlterSchemaFields::EVENT,
706
                [GetCraftQLSchema::class, 'handle']
707
            );
708
        }
709
        // FeedMe Support
710
        if (class_exists(FeedMe::class)) {
711
            Event::on(
712
                FeedMeFields::class,
713
                FeedMeFields::EVENT_REGISTER_FEED_ME_FIELDS,
714
                function(RegisterFeedMeFieldsEvent $e) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
715
                    Craft::debug(
716
                        'FeedMeFields::EVENT_REGISTER_FEED_ME_FIELDS',
717
                        __METHOD__
718
                    );
719
                    $e->fields[] = SeoSettingsFeedMe::class;
720
                }
721
            );
722
        }
723
    }
724
725
    /**
726
     * Install site event listeners for site requests only
727
     */
728
    protected function installSiteEventListeners()
729
    {
730
        // Load the sitemap containers
731
        self::$plugin->sitemaps->loadSitemapContainers();
732
        // Load the frontend template containers
733
        self::$plugin->frontendTemplates->loadFrontendTemplateContainers();
734
        // Handler: UrlManager::EVENT_REGISTER_SITE_URL_RULES
735
        Event::on(
736
            UrlManager::class,
737
            UrlManager::EVENT_REGISTER_SITE_URL_RULES,
738
            function (RegisterUrlRulesEvent $event) {
739
                Craft::debug(
740
                    'UrlManager::EVENT_REGISTER_SITE_URL_RULES',
741
                    __METHOD__
742
                );
743
                // FileController
744
                $route = self::$plugin->handle.'/file/seo-file-link';
745
                $event->rules[self::FRONTEND_SEO_FILE_LINK] = ['route' => $route];
746
                // PreviewController
747
                $route = self::$plugin->handle.'/preview/social-media';
748
                $event->rules[self::FRONTEND_PREVIEW_PATH] = ['route' => $route];
749
                // Register our Control Panel routes
750
                $event->rules = array_merge(
751
                    $event->rules,
752
                    $this->customFrontendRoutes()
753
                );
754
            }
755
        );
756
    }
757
758
    /**
759
     * Install site event listeners for Control Panel requests only
760
     */
761
    protected function installCpEventListeners()
762
    {
763
        // Load the frontend template containers
764
        self::$plugin->frontendTemplates->loadFrontendTemplateContainers();
765
        // Handler: UrlManager::EVENT_REGISTER_CP_URL_RULES
766
        Event::on(
767
            UrlManager::class,
768
            UrlManager::EVENT_REGISTER_CP_URL_RULES,
769
            function (RegisterUrlRulesEvent $event) {
770
                Craft::debug(
771
                    'UrlManager::EVENT_REGISTER_CP_URL_RULES',
772
                    __METHOD__
773
                );
774
                // Register our Control Panel routes
775
                $event->rules = array_merge(
776
                    $event->rules,
777
                    $this->customAdminCpRoutes()
778
                );
779
            }
780
        );
781
        // Handler: UserPermissions::EVENT_REGISTER_PERMISSIONS
782
        Event::on(
783
            UserPermissions::class,
784
            UserPermissions::EVENT_REGISTER_PERMISSIONS,
785
            function (RegisterUserPermissionsEvent $event) {
786
                Craft::debug(
787
                    'UserPermissions::EVENT_REGISTER_PERMISSIONS',
788
                    __METHOD__
789
                );
790
                // Register our custom permissions
791
                $event->permissions[Craft::t('seomatic', 'SEOmatic')] = $this->customAdminCpPermissions();
792
            }
793
        );
794
    }
795
796
    /**
797
     * Handle site requests.  We do it only after we receive the event
798
     * EVENT_AFTER_LOAD_PLUGINS so that any pending db migrations can be run
799
     * before our event listeners kick in
800
     */
801
    protected function handleSiteRequest()
802
    {
803
        // Handler: View::EVENT_BEGIN_BODY
804
        Event::on(
805
            View::class,
806
            View::EVENT_BEGIN_BODY,
807
            function () {
808
                Craft::debug(
809
                    'View::EVENT_BEGIN_BODY',
810
                    __METHOD__
811
                );
812
                // The <body> placeholder tag has just rendered, include any script HTML
813
                if (self::$settings->renderEnabled && self::$seomaticVariable) {
814
                    self::$plugin->metaContainers->includeScriptBodyHtml(View::POS_BEGIN);
815
                }
816
            }
817
        );
818
        // Handler: View::EVENT_END_BODY
819
        Event::on(
820
            View::class,
821
            View::EVENT_END_BODY,
822
            function () {
823
                Craft::debug(
824
                    'View::EVENT_END_BODY',
825
                    __METHOD__
826
                );
827
                // The </body> placeholder tag is about to be rendered, include any script HTML
828
                if (self::$settings->renderEnabled && self::$seomaticVariable) {
829
                    self::$plugin->metaContainers->includeScriptBodyHtml(View::POS_END);
830
                }
831
            }
832
        );
833
        // Handler: View::EVENT_END_PAGE
834
        Event::on(
835
            View::class,
836
            View::EVENT_END_PAGE,
837
            function () {
838
                Craft::debug(
839
                    'View::EVENT_END_PAGE',
840
                    __METHOD__
841
                );
842
                // The page is done rendering, include our meta containers
843
                if (self::$settings->renderEnabled && self::$seomaticVariable) {
844
                    self::$plugin->metaContainers->includeMetaContainers();
845
                }
846
            }
847
        );
848
    }
849
850
    /**
851
     * Handle Control Panel requests. We do it only after we receive the event
852
     * EVENT_AFTER_LOAD_PLUGINS so that any pending db migrations can be run
853
     * before our event listeners kick in
854
     */
855
    protected function handleAdminCpRequest()
856
    {
857
        // Don't cache Control Panel requests
858
        self::$cacheDuration = 1;
859
        // Prefix the Control Panel title
860
        self::$view->hook('cp.layouts.base', function (&$context) {
861
            if (self::$devMode) {
862
                $context['docTitle'] = self::$settings->devModeCpTitlePrefix.$context['docTitle'];
863
            } else {
864
                $context['docTitle'] = self::$settings->cpTitlePrefix.$context['docTitle'];
865
            }
866
        });
867
    }
868
869
    /**
870
     * @inheritdoc
871
     */
872
    protected function createSettingsModel()
873
    {
874
        return new Settings();
875
    }
876
877
    /**
878
     * Return the custom Control Panel routes
879
     *
880
     * @return array
881
     */
882
    protected function customAdminCpRoutes(): array
883
    {
884
        return [
885
            'seomatic' =>
886
                'seomatic/settings/dashboard',
887
            'seomatic/dashboard' =>
888
                'seomatic/settings/dashboard',
889
            'seomatic/dashboard/<siteHandle:{handle}>' =>
890
                'seomatic/settings/dashboard',
891
892
            'seomatic/global' => [
893
                'route' => 'seomatic/settings/global',
894
                'defaults' => ['subSection' => 'general'],
895
            ],
896
            'seomatic/global/<subSection:{handle}>' =>
897
                'seomatic/settings/global',
898
            'seomatic/global/<subSection:{handle}>/<siteHandle:{handle}>' =>
899
                'seomatic/settings/global',
900
901
            'seomatic/content' =>
902
                'seomatic/settings/content',
903
            'seomatic/content/<siteHandle:{handle}>' =>
904
                'seomatic/settings/content',
905
906
            'seomatic/edit-content/<subSection:{handle}>/<sourceBundleType:{handle}>/<sourceHandle:{handle}>' =>
907
                'seomatic/settings/edit-content',
908
            'seomatic/edit-content/<subSection:{handle}>/<sourceBundleType:{handle}>/<sourceHandle:{handle}>/<siteHandle:{handle}>' =>
909
                'seomatic/settings/edit-content',
910
911
            'seomatic/site' => [
912
                'route' => 'seomatic/settings/site',
913
                'defaults' => ['subSection' => 'identity'],
914
            ],
915
            'seomatic/site/<subSection:{handle}>' =>
916
                'seomatic/settings/site',
917
            'seomatic/site/<subSection:{handle}>/<siteHandle:{handle}>' =>
918
                'seomatic/settings/site',
919
920
            'seomatic/tracking' => [
921
                'route' => 'seomatic/settings/tracking',
922
                'defaults' => ['subSection' => 'googleAnalytics'],
923
            ],
924
            'seomatic/tracking/<subSection:{handle}>' =>
925
                'seomatic/settings/tracking',
926
            'seomatic/tracking/<subSection:{handle}>/<siteHandle:{handle}>' =>
927
                'seomatic/settings/tracking',
928
929
            'seomatic/plugin' =>
930
                'seomatic/settings/plugin',
931
        ];
932
    }
933
934
    /**
935
     * Return the custom frontend routes
936
     *
937
     * @return array
938
     */
939
    protected function customFrontendRoutes(): array
940
    {
941
        return [
942
        ];
943
    }
944
945
    /**
946
     * Returns the custom Control Panel cache options.
947
     *
948
     * @return array
949
     */
950
    protected function customAdminCpCacheOptions(): array
951
    {
952
        return [
953
            // Frontend template caches
954
            [
955
                'key' => 'seomatic-frontendtemplate-caches',
956
                'label' => Craft::t('seomatic', 'SEOmatic frontend template caches'),
957
                'action' => [self::$plugin->frontendTemplates, 'invalidateCaches'],
958
            ],
959
            // Meta bundle caches
960
            [
961
                'key' => 'seomatic-metabundle-caches',
962
                'label' => Craft::t('seomatic', 'SEOmatic metadata caches'),
963
                'action' => [self::$plugin->metaContainers, 'invalidateCaches'],
964
            ],
965
            // Sitemap caches
966
            [
967
                'key' => 'seomatic-sitemap-caches',
968
                'label' => Craft::t('seomatic', 'SEOmatic sitemap caches'),
969
                'action' => [self::$plugin->sitemaps, 'invalidateCaches'],
970
            ],
971
        ];
972
    }
973
974
    /**
975
     * Returns the custom Control Panel user permissions.
976
     *
977
     * @return array
978
     */
979
    protected function customAdminCpPermissions(): array
980
    {
981
        // The script meta containers for the global meta bundle
982
        try {
983
            $currentSiteId = Craft::$app->getSites()->getCurrentSite()->id ?? 1;
984
        } catch (SiteNotFoundException $e) {
985
            $currentSiteId = 1;
986
        }
987
        // Dynamic permissions for the scripts
988
        $metaBundle = self::$plugin->metaBundles->getGlobalMetaBundle($currentSiteId);
989
        $scriptsPerms = [];
990
        if ($metaBundle !== null) {
991
            $scripts = self::$plugin->metaBundles->getContainerDataFromBundle(
992
                $metaBundle,
993
                MetaScriptContainer::CONTAINER_TYPE
994
            );
995
            foreach ($scripts as $scriptHandle => $scriptData) {
996
                $scriptsPerms["seomatic:tracking-scripts:${scriptHandle}"] = [
997
                    'label' => Craft::t('seomatic', $scriptData->name),
998
                ];
999
            }
1000
        }
1001
1002
        return [
1003
            'seomatic:dashboard' => [
1004
                'label' => Craft::t('seomatic', 'Dashboard'),
1005
            ],
1006
            'seomatic:global-meta' => [
1007
                'label' => Craft::t('seomatic', 'Edit Global Meta'),
1008
                'nested' => [
1009
                    'seomatic:global-meta:general' => [
1010
                        'label' => Craft::t('seomatic', 'General'),
1011
                    ],
1012
                    'seomatic:global-meta:twitter' => [
1013
                        'label' => Craft::t('seomatic', 'Twitter'),
1014
                    ],
1015
                    'seomatic:global-meta:facebook' => [
1016
                        'label' => Craft::t('seomatic', 'Facebook'),
1017
                    ],
1018
                    'seomatic:global-meta:robots' => [
1019
                        'label' => Craft::t('seomatic', 'Robots'),
1020
                    ],
1021
                    'seomatic:global-meta:humans' => [
1022
                        'label' => Craft::t('seomatic', 'Humans'),
1023
                    ],
1024
                    'seomatic:global-meta:ads' => [
1025
                        'label' => Craft::t('seomatic', 'Ads'),
1026
                    ],
1027
                ],
1028
            ],
1029
            'seomatic:content-meta' => [
1030
                'label' => Craft::t('seomatic', 'Edit Content SEO'),
1031
                'nested' => [
1032
                    'seomatic:content-meta:general' => [
1033
                        'label' => Craft::t('seomatic', 'General'),
1034
                    ],
1035
                    'seomatic:content-meta:twitter' => [
1036
                        'label' => Craft::t('seomatic', 'Twitter'),
1037
                    ],
1038
                    'seomatic:content-meta:facebook' => [
1039
                        'label' => Craft::t('seomatic', 'Facebook'),
1040
                    ],
1041
                    'seomatic:content-meta:sitemap' => [
1042
                        'label' => Craft::t('seomatic', 'Sitemap'),
1043
                    ],
1044
                ],
1045
            ],
1046
            'seomatic:site-settings' => [
1047
                'label' => Craft::t('seomatic', 'Edit Site Settings'),
1048
                'nested' => [
1049
                    'seomatic:site-settings:identity' => [
1050
                        'label' => Craft::t('seomatic', 'Identity'),
1051
                    ],
1052
                    'seomatic:site-settings:creator' => [
1053
                        'label' => Craft::t('seomatic', 'Creator'),
1054
                    ],
1055
                    'seomatic:site-settings:social' => [
1056
                        'label' => Craft::t('seomatic', 'Social Media'),
1057
                    ],
1058
                    'seomatic:site-settings:sitemap' => [
1059
                        'label' => Craft::t('seomatic', 'Sitemap'),
1060
                    ],
1061
                    'seomatic:site-settings:miscellaneous' => [
1062
                        'label' => Craft::t('seomatic', 'Miscellaneous'),
1063
                    ],
1064
                ],
1065
            ],
1066
            'seomatic:tracking-scripts' => [
1067
                'label' => Craft::t('seomatic', 'Edit Tracking Scripts'),
1068
                'nested' => $scriptsPerms,
1069
            ],
1070
            'seomatic:plugin-settings' => [
1071
                'label' => Craft::t('seomatic', 'Edit Plugin Settings'),
1072
            ],
1073
        ];
1074
    }
1075
}
1076