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) 2017 nystudio107 |
||||||
10 | */ |
||||||
11 | |||||||
12 | namespace nystudio107\seomatic; |
||||||
13 | |||||||
14 | use Craft; |
||||||
15 | use craft\base\Element; |
||||||
16 | use craft\base\ElementInterface; |
||||||
0 ignored issues
–
show
|
|||||||
17 | use craft\base\Model; |
||||||
18 | use craft\base\Plugin; |
||||||
19 | use craft\elements\Entry; |
||||||
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 nystudio107\codeeditor\autocompletes\EnvironmentVariableAutocomplete; |
||||||
49 | use nystudio107\codeeditor\events\RegisterCodeEditorAutocompletesEvent; |
||||||
50 | use nystudio107\codeeditor\events\RegisterTwigValidatorVariablesEvent; |
||||||
51 | use nystudio107\codeeditor\services\AutocompleteService; |
||||||
52 | use nystudio107\codeeditor\validators\TwigTemplateValidator; |
||||||
53 | use nystudio107\crafttwigsandbox\helpers\SecurityPolicy; |
||||||
54 | use nystudio107\crafttwigsandbox\web\SandboxView; |
||||||
55 | use nystudio107\fastcgicachebust\FastcgiCacheBust; |
||||||
56 | use nystudio107\seomatic\autocompletes\TrackingVarsAutocomplete; |
||||||
57 | use nystudio107\seomatic\debug\panels\SeomaticPanel; |
||||||
58 | use nystudio107\seomatic\fields\Seomatic_Meta as Seomatic_MetaField; |
||||||
59 | use nystudio107\seomatic\fields\SeoSettings as SeoSettingsField; |
||||||
60 | use nystudio107\seomatic\gql\arguments\SeomaticArguments; |
||||||
61 | use nystudio107\seomatic\gql\interfaces\SeomaticInterface; |
||||||
62 | use nystudio107\seomatic\gql\queries\SeomaticQuery; |
||||||
63 | use nystudio107\seomatic\gql\resolvers\SeomaticResolver; |
||||||
64 | use nystudio107\seomatic\gql\types\SeomaticEnvironmentType; |
||||||
65 | use nystudio107\seomatic\helpers\Environment as EnvironmentHelper; |
||||||
66 | use nystudio107\seomatic\helpers\Gql as GqlHelper; |
||||||
67 | use nystudio107\seomatic\helpers\MetaValue as MetaValueHelper; |
||||||
68 | use nystudio107\seomatic\helpers\Schema as SchemaHelper; |
||||||
69 | use nystudio107\seomatic\helpers\UrlHelper; |
||||||
70 | use nystudio107\seomatic\integrations\feedme\SeoSettings as SeoSettingsFeedMe; |
||||||
71 | use nystudio107\seomatic\models\MetaScriptContainer; |
||||||
72 | use nystudio107\seomatic\models\Settings; |
||||||
73 | use nystudio107\seomatic\services\ServicesTrait; |
||||||
74 | use nystudio107\seomatic\twigextensions\SeomaticTwigExtension; |
||||||
75 | use nystudio107\seomatic\variables\SeomaticVariable; |
||||||
76 | use yii\base\Application as BaseApplication; |
||||||
77 | use yii\base\Event; |
||||||
78 | use yii\base\View as BaseView; |
||||||
79 | use yii\debug\Module; |
||||||
80 | |||||||
81 | /** @noinspection MissingPropertyAnnotationsInspection */ |
||||||
82 | |||||||
83 | /** |
||||||
84 | * Class Seomatic |
||||||
85 | * |
||||||
86 | * @author nystudio107 |
||||||
87 | * @package Seomatic |
||||||
88 | * @since 3.0.0 |
||||||
89 | */ |
||||||
90 | class Seomatic extends Plugin |
||||||
91 | { |
||||||
92 | // Traits |
||||||
93 | // ========================================================================= |
||||||
94 | |||||||
95 | use ServicesTrait; |
||||||
96 | |||||||
97 | // Constants |
||||||
98 | // ========================================================================= |
||||||
99 | |||||||
100 | public const SEOMATIC_HANDLE = 'Seomatic'; |
||||||
101 | |||||||
102 | public const DEVMODE_CACHE_DURATION = 30; |
||||||
103 | |||||||
104 | public const SEOMATIC_PREVIEW_AUTHORIZATION_KEY = 'seomaticPreviewAuthorizationKey'; |
||||||
105 | |||||||
106 | protected const FRONTEND_SEO_FILE_LINK = 'seomatic/seo-file-link/<url:[^\/]+>/<robots:[^\/]+>/<canonical:[^\/]+>/<inline:\d+>/<fileName:[-\w\.*]+>'; |
||||||
107 | |||||||
108 | protected const FRONTEND_PREVIEW_PATH = 'seomatic/preview-social-media'; |
||||||
109 | |||||||
110 | public const SEOMATIC_EXPRESSION_FIELD_TYPE = 'SeomaticExpressionField'; |
||||||
111 | public const SEOMATIC_TRACKING_FIELD_TYPE = 'SeomaticTrackingField'; |
||||||
112 | |||||||
113 | // Static Properties |
||||||
114 | // ========================================================================= |
||||||
115 | |||||||
116 | /** |
||||||
117 | * @var ?Seomatic |
||||||
118 | */ |
||||||
119 | public static ?Seomatic $plugin; |
||||||
120 | |||||||
121 | /** |
||||||
122 | * @var null|SeomaticVariable |
||||||
123 | */ |
||||||
124 | public static ?SeomaticVariable $seomaticVariable = null; |
||||||
125 | |||||||
126 | /** |
||||||
127 | * @var null|Settings |
||||||
128 | */ |
||||||
129 | public static ?Settings $settings = null; |
||||||
130 | |||||||
131 | /** |
||||||
132 | * @var null|ElementInterface |
||||||
133 | */ |
||||||
134 | public static ?ElementInterface $matchedElement = null; |
||||||
135 | |||||||
136 | /** |
||||||
137 | * @var bool |
||||||
138 | */ |
||||||
139 | public static bool $devMode; |
||||||
140 | |||||||
141 | /** |
||||||
142 | * @var null|View |
||||||
143 | */ |
||||||
144 | public static ?View $view = null; |
||||||
145 | |||||||
146 | /** |
||||||
147 | * @var null|View |
||||||
148 | */ |
||||||
149 | public static ?View $sandboxView = null; |
||||||
150 | |||||||
151 | /** |
||||||
152 | * @var string |
||||||
153 | */ |
||||||
154 | public static string $language = ''; |
||||||
155 | |||||||
156 | /** |
||||||
157 | * @var string |
||||||
158 | */ |
||||||
159 | public static string $environment = ''; |
||||||
160 | |||||||
161 | /** |
||||||
162 | * @var int |
||||||
163 | */ |
||||||
164 | public static int $cacheDuration = 0; |
||||||
165 | |||||||
166 | /** |
||||||
167 | * @var bool |
||||||
168 | */ |
||||||
169 | public static bool $previewingMetaContainers = false; |
||||||
170 | |||||||
171 | /** |
||||||
172 | * @var bool |
||||||
173 | */ |
||||||
174 | public static bool $loadingMetaContainers = false; |
||||||
175 | |||||||
176 | /** |
||||||
177 | * @var bool |
||||||
178 | */ |
||||||
179 | public static bool $savingSettings = false; |
||||||
180 | |||||||
181 | /** |
||||||
182 | * @var bool |
||||||
183 | */ |
||||||
184 | public static bool $headlessRequest = false; |
||||||
185 | |||||||
186 | // Public Properties |
||||||
187 | // ========================================================================= |
||||||
188 | |||||||
189 | /** |
||||||
190 | * @var string |
||||||
191 | */ |
||||||
192 | public string $schemaVersion = '3.0.13'; |
||||||
193 | |||||||
194 | /** |
||||||
195 | * @var bool |
||||||
196 | */ |
||||||
197 | public bool $hasCpSection = true; |
||||||
198 | |||||||
199 | /** |
||||||
200 | * @var bool |
||||||
201 | */ |
||||||
202 | public bool $hasCpSettings = true; |
||||||
203 | |||||||
204 | /** |
||||||
205 | * Set the matched element |
||||||
206 | * |
||||||
207 | * @param $element null|ElementInterface |
||||||
208 | */ |
||||||
209 | public static function setMatchedElement(?ElementInterface $element): void |
||||||
210 | { |
||||||
211 | self::$matchedElement = $element; |
||||||
212 | /** @var $element Element */ |
||||||
213 | if ($element) { |
||||||
0 ignored issues
–
show
|
|||||||
214 | self::$language = MetaValueHelper::getSiteLanguage($element->siteId); |
||||||
215 | } else { |
||||||
216 | self::$language = MetaValueHelper::getSiteLanguage(null); |
||||||
217 | } |
||||||
218 | MetaValueHelper::cache(); |
||||||
219 | } |
||||||
220 | |||||||
221 | // Public Methods |
||||||
222 | // ========================================================================= |
||||||
223 | |||||||
224 | /** |
||||||
225 | * @inheritdoc |
||||||
226 | */ |
||||||
227 | public function init(): void |
||||||
228 | { |
||||||
229 | parent::init(); |
||||||
230 | self::$plugin = $this; |
||||||
231 | // Handle any console commands |
||||||
232 | $request = Craft::$app->getRequest(); |
||||||
233 | if ($request->getIsConsoleRequest()) { |
||||||
234 | $this->controllerNamespace = 'nystudio107\seomatic\console\controllers'; |
||||||
235 | } |
||||||
236 | // Initialize properties |
||||||
237 | /** @var Settings $settings */ |
||||||
238 | $settings = self::$plugin->getSettings(); |
||||||
239 | self::$settings = $settings; |
||||||
240 | self::$devMode = Craft::$app->getConfig()->getGeneral()->devMode; |
||||||
241 | self::$view = Craft::$app->getView(); |
||||||
242 | // Use a Twig sandbox for SEOmatic rendering |
||||||
243 | $securityPolicy = SecurityPolicy::createFromFile('seomatic-sandbox', '@nystudio107/seomatic'); |
||||||
244 | self::$sandboxView = new SandboxView(['securityPolicy' => $securityPolicy]); |
||||||
245 | self::$cacheDuration = self::$devMode |
||||||
246 | ? self::DEVMODE_CACHE_DURATION |
||||||
247 | : self::$settings->metaCacheDuration ?? 0; |
||||||
248 | self::$environment = EnvironmentHelper::determineEnvironment(); |
||||||
249 | MetaValueHelper::cache(); |
||||||
250 | $this->name = self::$settings->pluginName; |
||||||
251 | // Install our event listeners |
||||||
252 | $this->installEventListeners(); |
||||||
253 | // We're loaded |
||||||
254 | Craft::info( |
||||||
255 | Craft::t( |
||||||
256 | 'seomatic', |
||||||
257 | '{name} plugin loaded', |
||||||
258 | ['name' => $this->name] |
||||||
259 | ), |
||||||
260 | __METHOD__ |
||||||
261 | ); |
||||||
262 | } |
||||||
263 | |||||||
264 | /** |
||||||
265 | * @inheritdoc |
||||||
266 | */ |
||||||
267 | public function getSettings(): ?Model |
||||||
268 | { |
||||||
269 | // For all the emojis |
||||||
270 | /* @var Settings $settingsModel */ |
||||||
271 | $settingsModel = parent::getSettings(); |
||||||
272 | if ($settingsModel !== null && !self::$savingSettings) { |
||||||
273 | $attributes = $settingsModel->attributes(); |
||||||
274 | if ($attributes !== null) { |
||||||
0 ignored issues
–
show
|
|||||||
275 | foreach ($attributes as $attribute) { |
||||||
276 | if (is_string($settingsModel->$attribute)) { |
||||||
277 | $settingsModel->$attribute = html_entity_decode( |
||||||
278 | $settingsModel->$attribute, |
||||||
279 | ENT_NOQUOTES, |
||||||
280 | 'UTF-8' |
||||||
281 | ); |
||||||
282 | } |
||||||
283 | } |
||||||
284 | } |
||||||
285 | self::$savingSettings = false; |
||||||
286 | } |
||||||
287 | |||||||
288 | return $settingsModel; |
||||||
289 | } |
||||||
290 | |||||||
291 | /** |
||||||
292 | * Determine whether our table schema exists or not; this is needed because |
||||||
293 | * migrations such as the install migration and base_install migration may |
||||||
294 | * not have been run by the time our init() method has been called |
||||||
295 | * |
||||||
296 | * @return bool |
||||||
297 | */ |
||||||
298 | public function migrationsAndSchemaReady(): bool |
||||||
299 | { |
||||||
300 | $pluginsService = Craft::$app->getPlugins(); |
||||||
301 | if ($pluginsService->isPluginUpdatePending(self::$plugin)) { |
||||||
302 | return false; |
||||||
303 | } |
||||||
304 | if (Craft::$app->db->schema->getTableSchema('{{%seomatic_metabundles}}') === null) { |
||||||
305 | return false; |
||||||
306 | } |
||||||
307 | |||||||
308 | return true; |
||||||
309 | } |
||||||
310 | |||||||
311 | /** |
||||||
312 | * Clear all the caches! |
||||||
313 | */ |
||||||
314 | public function clearAllCaches(): void |
||||||
315 | { |
||||||
316 | // Clear all of SEOmatic's caches |
||||||
317 | self::$plugin->frontendTemplates->invalidateCaches(); |
||||||
318 | self::$plugin->metaContainers->invalidateCaches(); |
||||||
319 | self::$plugin->sitemaps->invalidateCaches(); |
||||||
320 | SchemaHelper::invalidateCaches(); |
||||||
321 | // Clear the GraphQL caches too |
||||||
322 | $gql = Craft::$app->getGql(); |
||||||
323 | if (method_exists($gql, 'invalidateCaches')) { |
||||||
324 | $gql->invalidateCaches(); |
||||||
325 | } |
||||||
326 | // If the FastCGI Cache Bust plugin is installed, clear its caches too |
||||||
327 | /** @var ?FastcgiCacheBust $plugin */ |
||||||
328 | $plugin = Craft::$app->getPlugins()->getPlugin('fastcgi-cache-bust'); |
||||||
329 | if ($plugin !== null) { |
||||||
330 | $plugin->cache->clearAll(); |
||||||
331 | } |
||||||
332 | } |
||||||
333 | |||||||
334 | // Protected Methods |
||||||
335 | // ========================================================================= |
||||||
336 | |||||||
337 | /** |
||||||
338 | * @inheritdoc |
||||||
339 | */ |
||||||
340 | public function getSettingsResponse(): mixed |
||||||
341 | { |
||||||
342 | // Just redirect to the plugin settings page |
||||||
343 | return Craft::$app->getResponse()->redirect(UrlHelper::cpUrl('seomatic/plugin')); |
||||||
344 | } |
||||||
345 | |||||||
346 | /** |
||||||
347 | * @inheritdoc |
||||||
348 | */ |
||||||
349 | public function getCpNavItem(): ?array |
||||||
350 | { |
||||||
351 | $subNavs = []; |
||||||
352 | $navItem = parent::getCpNavItem(); |
||||||
353 | $request = Craft::$app->getRequest(); |
||||||
354 | $siteSuffix = ''; |
||||||
355 | if ($request->getSegment(1) === 'seomatic') { |
||||||
356 | $segments = $request->getSegments(); |
||||||
357 | $lastSegment = end($segments); |
||||||
358 | $site = Craft::$app->getSites()->getSiteByHandle($lastSegment); |
||||||
359 | if ($site !== null) { |
||||||
360 | $siteSuffix = '/' . $lastSegment; |
||||||
361 | } |
||||||
362 | } |
||||||
363 | $currentUser = Craft::$app->getUser()->getIdentity(); |
||||||
364 | // Only show sub-navs the user has permission to view |
||||||
365 | if ($currentUser->can('seomatic:dashboard')) { |
||||||
0 ignored issues
–
show
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
![]() |
|||||||
366 | $subNavs['dashboard'] = [ |
||||||
367 | 'label' => Craft::t('seomatic', 'Dashboard'), |
||||||
368 | 'url' => 'seomatic/dashboard' . $siteSuffix, |
||||||
369 | ]; |
||||||
370 | } |
||||||
371 | if ($currentUser->can('seomatic:global-meta')) { |
||||||
372 | $subNavs['global'] = [ |
||||||
373 | 'label' => Craft::t('seomatic', 'Global SEO'), |
||||||
374 | 'url' => 'seomatic/global/general' . $siteSuffix, |
||||||
375 | ]; |
||||||
376 | } |
||||||
377 | if ($currentUser->can('seomatic:content-meta')) { |
||||||
378 | $subNavs['content'] = [ |
||||||
379 | 'label' => Craft::t('seomatic', 'Content SEO'), |
||||||
380 | 'url' => 'seomatic/content' . $siteSuffix, |
||||||
381 | ]; |
||||||
382 | } |
||||||
383 | if ($currentUser->can('seomatic:site-settings')) { |
||||||
384 | $subNavs['site'] = [ |
||||||
385 | 'label' => Craft::t('seomatic', 'Site Settings'), |
||||||
386 | 'url' => 'seomatic/site/identity' . $siteSuffix, |
||||||
387 | ]; |
||||||
388 | } |
||||||
389 | if ($currentUser->can('seomatic:tracking-scripts')) { |
||||||
390 | $subNavs['tracking'] = [ |
||||||
391 | 'label' => Craft::t('seomatic', 'Tracking Scripts'), |
||||||
392 | 'url' => 'seomatic/tracking/gtag' . $siteSuffix, |
||||||
393 | ]; |
||||||
394 | } |
||||||
395 | $editableSettings = true; |
||||||
396 | $general = Craft::$app->getConfig()->getGeneral(); |
||||||
397 | if (!$general->allowAdminChanges) { |
||||||
398 | $editableSettings = false; |
||||||
399 | } |
||||||
400 | if ($editableSettings && $currentUser->can('seomatic:plugin-settings')) { |
||||||
401 | $subNavs['plugin'] = [ |
||||||
402 | 'label' => Craft::t('seomatic', 'Plugin Settings'), |
||||||
403 | 'url' => 'seomatic/plugin', |
||||||
404 | ]; |
||||||
405 | } |
||||||
406 | // 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 |
||||||
407 | if (empty($subNavs)) { |
||||||
408 | return null; |
||||||
409 | } |
||||||
410 | // A single sub nav item is redundant |
||||||
411 | if (count($subNavs) === 1) { |
||||||
412 | $subNavs = []; |
||||||
413 | } |
||||||
414 | |||||||
415 | return array_merge($navItem, [ |
||||||
416 | 'subnav' => $subNavs, |
||||||
417 | ]); |
||||||
418 | } |
||||||
419 | |||||||
420 | /** |
||||||
421 | * Install our event listeners. |
||||||
422 | */ |
||||||
423 | protected function installEventListeners(): void |
||||||
424 | { |
||||||
425 | // Install our event listeners only if our table schema exists |
||||||
426 | if ($this->migrationsAndSchemaReady()) { |
||||||
427 | // Add in our Twig extensions |
||||||
428 | $seomaticTwigExtension = new SeomaticTwigExtension(); |
||||||
429 | self::$view->registerTwigExtension($seomaticTwigExtension); |
||||||
0 ignored issues
–
show
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
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. ![]() |
|||||||
430 | self::$sandboxView->registerTwigExtension($seomaticTwigExtension); |
||||||
0 ignored issues
–
show
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
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. ![]() |
|||||||
431 | $request = Craft::$app->getRequest(); |
||||||
432 | // Add in our event listeners that are needed for every request |
||||||
433 | $this->installGlobalEventListeners(); |
||||||
434 | // Install only for non-console site requests |
||||||
435 | if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()) { |
||||||
436 | $this->installSiteEventListeners(); |
||||||
437 | } |
||||||
438 | // Install only for non-console Control Panel requests |
||||||
439 | if ($request->getIsCpRequest() && !$request->getIsConsoleRequest()) { |
||||||
440 | $this->installCpEventListeners(); |
||||||
441 | } |
||||||
442 | } |
||||||
443 | // Handler: EVENT_AFTER_INSTALL_PLUGIN |
||||||
444 | Event::on( |
||||||
445 | Plugins::class, |
||||||
446 | Plugins::EVENT_AFTER_INSTALL_PLUGIN, |
||||||
447 | function(PluginEvent $event) { |
||||||
448 | if ($event->plugin === $this) { |
||||||
0 ignored issues
–
show
|
|||||||
449 | // Invalidate our caches after we've been installed |
||||||
450 | $this->clearAllCaches(); |
||||||
451 | // Send them to our welcome screen |
||||||
452 | $request = Craft::$app->getRequest(); |
||||||
453 | if ($request->isCpRequest) { |
||||||
454 | Craft::$app->getResponse()->redirect(UrlHelper::cpUrl( |
||||||
455 | 'seomatic/dashboard', |
||||||
456 | [ |
||||||
457 | 'showWelcome' => true, |
||||||
458 | ] |
||||||
459 | ))->send(); |
||||||
460 | } |
||||||
461 | } |
||||||
462 | } |
||||||
463 | ); |
||||||
464 | // Handler: ClearCaches::EVENT_REGISTER_CACHE_OPTIONS |
||||||
465 | Event::on( |
||||||
466 | ClearCaches::class, |
||||||
467 | ClearCaches::EVENT_REGISTER_CACHE_OPTIONS, |
||||||
468 | function(RegisterCacheOptionsEvent $event) { |
||||||
469 | Craft::debug( |
||||||
470 | 'ClearCaches::EVENT_REGISTER_CACHE_OPTIONS', |
||||||
471 | __METHOD__ |
||||||
472 | ); |
||||||
473 | // Register our Cache Options |
||||||
474 | $event->options = array_merge( |
||||||
475 | $event->options, |
||||||
476 | $this->customAdminCpCacheOptions() |
||||||
477 | ); |
||||||
478 | } |
||||||
479 | ); |
||||||
480 | // Handler: EVENT_BEFORE_SAVE_PLUGIN_SETTINGS |
||||||
481 | Event::on( |
||||||
482 | Plugins::class, |
||||||
483 | Plugins::EVENT_BEFORE_SAVE_PLUGIN_SETTINGS, |
||||||
484 | function(PluginEvent $event) { |
||||||
485 | if ($event->plugin === $this && !Craft::$app->getDb()->getSupportsMb4()) { |
||||||
0 ignored issues
–
show
|
|||||||
486 | // For all the emojis |
||||||
487 | $settingsModel = $this->getSettings(); |
||||||
488 | self::$savingSettings = true; |
||||||
489 | if ($settingsModel !== null) { |
||||||
490 | $attributes = $settingsModel->attributes(); |
||||||
491 | if ($attributes !== null) { |
||||||
492 | foreach ($attributes as $attribute) { |
||||||
493 | if (is_string($settingsModel->$attribute)) { |
||||||
494 | $settingsModel->$attribute = |
||||||
495 | StringHelper::encodeMb4($settingsModel->$attribute); |
||||||
496 | } |
||||||
497 | } |
||||||
498 | } |
||||||
499 | } |
||||||
500 | } |
||||||
501 | } |
||||||
502 | ); |
||||||
503 | } |
||||||
504 | |||||||
505 | /** |
||||||
506 | * Install global event listeners for all request types |
||||||
507 | */ |
||||||
508 | protected function installGlobalEventListeners(): void |
||||||
509 | { |
||||||
510 | // Handler: Plugins::EVENT_AFTER_LOAD_PLUGINS |
||||||
511 | Event::on( |
||||||
512 | Plugins::class, |
||||||
513 | Plugins::EVENT_AFTER_LOAD_PLUGINS, |
||||||
514 | function() { |
||||||
515 | // Delay registering SEO Elements to give other plugins a chance to load first |
||||||
516 | $this->seoElements->getAllSeoElementTypes(false); |
||||||
517 | // Delay installing GQL handlers to give other plugins a chance to register their own first |
||||||
518 | $this->installGqlHandlers(); |
||||||
519 | // Install these only after all other plugins have loaded |
||||||
520 | $request = Craft::$app->getRequest(); |
||||||
521 | // Only respond to non-console site requests |
||||||
522 | if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()) { |
||||||
523 | $this->handleSiteRequest(); |
||||||
524 | } |
||||||
525 | // Respond to Control Panel requests |
||||||
526 | if ($request->getIsCpRequest() && !$request->getIsConsoleRequest()) { |
||||||
527 | $this->handleAdminCpRequest(); |
||||||
528 | } |
||||||
529 | } |
||||||
530 | ); |
||||||
531 | // Handler: Fields::EVENT_REGISTER_FIELD_TYPES |
||||||
532 | Event::on( |
||||||
533 | Fields::class, |
||||||
534 | Fields::EVENT_REGISTER_FIELD_TYPES, |
||||||
535 | static function(RegisterComponentTypesEvent $event) { |
||||||
536 | $event->types[] = SeoSettingsField::class; |
||||||
537 | $event->types[] = Seomatic_MetaField::class; |
||||||
538 | } |
||||||
539 | ); |
||||||
540 | // Handler: Element::EVENT_AFTER_PROPAGATE |
||||||
541 | Event::on( |
||||||
542 | Element::class, |
||||||
543 | Element::EVENT_AFTER_PROPAGATE, |
||||||
544 | static function(ModelEvent $event) { |
||||||
545 | Craft::debug( |
||||||
546 | 'Element::EVENT_AFTER_PROPAGATE', |
||||||
547 | __METHOD__ |
||||||
548 | ); |
||||||
549 | /** @var Element $element */ |
||||||
550 | $element = $event->sender; |
||||||
551 | self::$plugin->metaBundles->invalidateMetaBundleByElement( |
||||||
552 | $element, |
||||||
553 | $event->isNew |
||||||
554 | ); |
||||||
555 | if ($event->isNew) { |
||||||
556 | self::$plugin->sitemaps->submitSitemapForElement($element); |
||||||
557 | } |
||||||
558 | } |
||||||
559 | ); |
||||||
560 | // Handler: Elements::EVENT_AFTER_DELETE_ELEMENT |
||||||
561 | Event::on( |
||||||
562 | Elements::class, |
||||||
563 | Elements::EVENT_AFTER_DELETE_ELEMENT, |
||||||
564 | static function(ElementEvent $event) { |
||||||
565 | Craft::debug( |
||||||
566 | 'Elements::EVENT_AFTER_DELETE_ELEMENT', |
||||||
567 | __METHOD__ |
||||||
568 | ); |
||||||
569 | /** @var Element $element */ |
||||||
570 | $element = $event->element; |
||||||
571 | self::$plugin->metaBundles->invalidateMetaBundleByElement( |
||||||
572 | $element, |
||||||
573 | false |
||||||
574 | ); |
||||||
575 | } |
||||||
576 | ); |
||||||
577 | // Add social media preview targets on Craft 3.2 or later |
||||||
578 | if (self::$settings->socialMediaPreviewTarget) { |
||||||
579 | // Handler: Entry::EVENT_REGISTER_PREVIEW_TARGETS |
||||||
580 | Event::on( |
||||||
581 | Entry::class, |
||||||
582 | Element::EVENT_REGISTER_PREVIEW_TARGETS, |
||||||
583 | static function(RegisterPreviewTargetsEvent $e) { |
||||||
584 | /** @var Element $element */ |
||||||
585 | $element = $e->sender; |
||||||
586 | if ($element->uri !== null) { |
||||||
587 | $e->previewTargets[] = [ |
||||||
588 | 'label' => '📣 ' . Craft::t('seomatic', 'Social Media Preview'), |
||||||
589 | 'url' => UrlHelper::siteUrl(self::FRONTEND_PREVIEW_PATH, [ |
||||||
590 | 'elementId' => $element->id, |
||||||
591 | 'siteId' => $element->siteId, |
||||||
592 | ]), |
||||||
593 | ]; |
||||||
594 | // Don't allow the preview to be accessed publicly |
||||||
595 | Craft::$app->getSession()->authorize(self::SEOMATIC_PREVIEW_AUTHORIZATION_KEY . $element->id); |
||||||
596 | } |
||||||
597 | } |
||||||
598 | ); |
||||||
599 | } |
||||||
600 | // Yii2 Debug Toolbar support |
||||||
601 | Event::on( |
||||||
602 | Application::class, |
||||||
603 | BaseApplication::EVENT_BEFORE_REQUEST, |
||||||
604 | static function() { |
||||||
605 | /** @var Module|null $debugModule */ |
||||||
606 | $debugModule = Seomatic::$settings->enableDebugToolbarPanel ? Craft::$app->getModule('debug') : null; |
||||||
607 | |||||||
608 | if ($debugModule) { |
||||||
0 ignored issues
–
show
|
|||||||
609 | $debugModule->panels['seomatic'] = new SeomaticPanel([ |
||||||
610 | 'id' => 'seomatic', |
||||||
611 | 'module' => $debugModule, |
||||||
612 | ]); |
||||||
613 | } |
||||||
614 | } |
||||||
615 | ); |
||||||
616 | // FeedMe Support |
||||||
617 | if (class_exists(FeedMe::class)) { |
||||||
618 | Event::on( |
||||||
619 | FeedMeFields::class, |
||||||
620 | FeedMeFields::EVENT_REGISTER_FEED_ME_FIELDS, |
||||||
621 | static function(RegisterFeedMeFieldsEvent $e) { |
||||||
622 | Craft::debug( |
||||||
623 | 'FeedMeFields::EVENT_REGISTER_FEED_ME_FIELDS', |
||||||
624 | __METHOD__ |
||||||
625 | ); |
||||||
626 | $e->fields[] = SeoSettingsFeedMe::class; |
||||||
627 | } |
||||||
628 | ); |
||||||
629 | } |
||||||
630 | $updateMetaBundles = static function($message) { |
||||||
631 | Craft::debug( |
||||||
632 | $message, |
||||||
633 | __METHOD__ |
||||||
634 | ); |
||||||
635 | $seoElementTypes = Seomatic::$plugin->seoElements->getAllSeoElementTypes(); |
||||||
636 | foreach ($seoElementTypes as $seoElementType) { |
||||||
637 | $metaBundleType = $seoElementType::META_BUNDLE_TYPE ?? ''; |
||||||
638 | |||||||
639 | if ($metaBundleType) { |
||||||
640 | Seomatic::$plugin->metaBundles->resaveMetaBundles($metaBundleType); |
||||||
641 | } |
||||||
642 | } |
||||||
643 | }; |
||||||
644 | |||||||
645 | // Handler: Elements::EVENT_AFTER_SAVE_SITE |
||||||
646 | Event::on( |
||||||
647 | SitesService::class, |
||||||
648 | SitesService::EVENT_AFTER_SAVE_SITE, |
||||||
649 | static function() use ($updateMetaBundles) { |
||||||
650 | $updateMetaBundles('SitesService::EVENT_AFTER_SAVE_SITE'); |
||||||
651 | } |
||||||
652 | ); |
||||||
653 | |||||||
654 | // Handler: Elements::EVENT_AFTER_DELETE_SITE |
||||||
655 | Event::on( |
||||||
656 | SitesService::class, |
||||||
657 | SitesService::EVENT_AFTER_DELETE_SITE, |
||||||
658 | static function() use ($updateMetaBundles) { |
||||||
659 | $updateMetaBundles('SitesService::EVENT_AFTER_DELETE_SITE'); |
||||||
660 | } |
||||||
661 | ); |
||||||
662 | } |
||||||
663 | |||||||
664 | /** |
||||||
665 | * Register our GraphQL handlers |
||||||
666 | * |
||||||
667 | * @return void |
||||||
668 | */ |
||||||
669 | protected function installGqlHandlers(): void |
||||||
670 | { |
||||||
671 | // Handler: Gql::EVENT_REGISTER_GQL_TYPES |
||||||
672 | Event::on( |
||||||
673 | Gql::class, |
||||||
674 | Gql::EVENT_REGISTER_GQL_TYPES, |
||||||
675 | static function(RegisterGqlTypesEvent $event) { |
||||||
676 | Craft::debug( |
||||||
677 | 'Gql::EVENT_REGISTER_GQL_TYPES', |
||||||
678 | __METHOD__ |
||||||
679 | ); |
||||||
680 | $event->types[] = SeomaticInterface::class; |
||||||
681 | $event->types[] = SeomaticEnvironmentType::class; |
||||||
682 | } |
||||||
683 | ); |
||||||
684 | // Handler: Gql::EVENT_REGISTER_GQL_QUERIES |
||||||
685 | Event::on( |
||||||
686 | Gql::class, |
||||||
687 | Gql::EVENT_REGISTER_GQL_QUERIES, |
||||||
688 | static function(RegisterGqlQueriesEvent $event) { |
||||||
689 | Craft::debug( |
||||||
690 | 'Gql::EVENT_REGISTER_GQL_QUERIES', |
||||||
691 | __METHOD__ |
||||||
692 | ); |
||||||
693 | $queries = SeomaticQuery::getQueries(); |
||||||
694 | foreach ($queries as $key => $value) { |
||||||
695 | $event->queries[$key] = $value; |
||||||
696 | } |
||||||
697 | } |
||||||
698 | ); |
||||||
699 | // Handler: Gql::EVENT_REGISTER_GQL_SCHEMA_COMPONENTS |
||||||
700 | Event::on( |
||||||
701 | Gql::class, |
||||||
702 | Gql::EVENT_REGISTER_GQL_SCHEMA_COMPONENTS, |
||||||
703 | static function(RegisterGqlSchemaComponentsEvent $event) { |
||||||
704 | Craft::debug( |
||||||
705 | 'Gql::EVENT_REGISTER_GQL_SCHEMA_COMPONENTS', |
||||||
706 | __METHOD__ |
||||||
707 | ); |
||||||
708 | $label = Craft::t('seomatic', 'Seomatic'); |
||||||
709 | $event->queries[$label]['seomatic.all:read'] = ['label' => Craft::t('seomatic', 'Query Seomatic data')]; |
||||||
710 | } |
||||||
711 | ); |
||||||
712 | // Add support for querying for SEOmatic metadata inside of element queries |
||||||
713 | // Handler: TypeManager::EVENT_DEFINE_GQL_TYPE_FIELDS |
||||||
714 | $knownInterfaceNames = self::$plugin->seoElements->getAllSeoElementGqlInterfaceNames(); |
||||||
715 | Event::on( |
||||||
716 | TypeManager::class, |
||||||
717 | TypeManager::EVENT_DEFINE_GQL_TYPE_FIELDS, |
||||||
718 | static function(DefineGqlTypeFieldsEvent $event) use ($knownInterfaceNames) { |
||||||
719 | if (in_array($event->typeName, $knownInterfaceNames, true)) { |
||||||
720 | Craft::debug( |
||||||
721 | 'TypeManager::EVENT_DEFINE_GQL_TYPE_FIELDS', |
||||||
722 | __METHOD__ |
||||||
723 | ); |
||||||
724 | |||||||
725 | if (GqlHelper::canQuerySeo()) { |
||||||
726 | // Make Seomatic tags available to all entries. |
||||||
727 | $event->fields['seomatic'] = [ |
||||||
728 | 'name' => 'seomatic', |
||||||
729 | 'type' => SeomaticInterface::getType(), |
||||||
730 | 'args' => SeomaticArguments::getArguments(), |
||||||
731 | 'resolve' => SeomaticResolver::class . '::resolve', |
||||||
732 | 'description' => Craft::t('seomatic', 'This query is used to query for SEOmatic meta data.'), |
||||||
733 | ]; |
||||||
734 | } |
||||||
735 | } |
||||||
736 | }); |
||||||
737 | } |
||||||
738 | |||||||
739 | /** |
||||||
740 | * Handle site requests. We do it only after we receive the event |
||||||
741 | * EVENT_AFTER_LOAD_PLUGINS so that any pending db migrations can be run |
||||||
742 | * before our event listeners kick in |
||||||
743 | */ |
||||||
744 | protected function handleSiteRequest(): void |
||||||
745 | { |
||||||
746 | // Handler: View::EVENT_END_PAGE |
||||||
747 | Event::on( |
||||||
748 | View::class, |
||||||
749 | BaseView::EVENT_END_PAGE, |
||||||
750 | static function() { |
||||||
751 | Craft::debug( |
||||||
752 | 'View::EVENT_END_PAGE', |
||||||
753 | __METHOD__ |
||||||
754 | ); |
||||||
755 | // The page is done rendering, include our meta containers |
||||||
756 | if (self::$settings->renderEnabled && self::$seomaticVariable) { |
||||||
757 | self::$plugin->metaContainers->includeMetaContainers(); |
||||||
758 | } |
||||||
759 | } |
||||||
760 | ); |
||||||
761 | } |
||||||
762 | |||||||
763 | /** |
||||||
764 | * Handle Control Panel requests. We do it only after we receive the event |
||||||
765 | * EVENT_AFTER_LOAD_PLUGINS so that any pending db migrations can be run |
||||||
766 | * before our event listeners kick in |
||||||
767 | */ |
||||||
768 | protected function handleAdminCpRequest(): void |
||||||
769 | { |
||||||
770 | // Don't cache Control Panel requests |
||||||
771 | self::$cacheDuration = 1; |
||||||
772 | // Prefix the Control Panel title |
||||||
773 | self::$view->hook('cp.layouts.base', function(&$context) { |
||||||
774 | if (self::$devMode) { |
||||||
775 | $context['docTitle'] = self::$settings->devModeCpTitlePrefix . $context['docTitle']; |
||||||
776 | } else { |
||||||
777 | $context['docTitle'] = self::$settings->cpTitlePrefix . $context['docTitle']; |
||||||
778 | } |
||||||
779 | }); |
||||||
780 | } |
||||||
781 | |||||||
782 | /** |
||||||
783 | * Install site event listeners for site requests only |
||||||
784 | */ |
||||||
785 | protected function installSiteEventListeners(): void |
||||||
786 | { |
||||||
787 | // Load the sitemap containers |
||||||
788 | self::$plugin->sitemaps->loadSitemapContainers(); |
||||||
789 | // Load the frontend template containers |
||||||
790 | self::$plugin->frontendTemplates->loadFrontendTemplateContainers(); |
||||||
791 | // Handler: UrlManager::EVENT_REGISTER_SITE_URL_RULES |
||||||
792 | Event::on( |
||||||
793 | UrlManager::class, |
||||||
794 | UrlManager::EVENT_REGISTER_SITE_URL_RULES, |
||||||
795 | function(RegisterUrlRulesEvent $event) { |
||||||
796 | Craft::debug( |
||||||
797 | 'UrlManager::EVENT_REGISTER_SITE_URL_RULES', |
||||||
798 | __METHOD__ |
||||||
799 | ); |
||||||
800 | // FileController |
||||||
801 | $route = self::$plugin->handle . '/file/seo-file-link'; |
||||||
802 | $event->rules[self::FRONTEND_SEO_FILE_LINK] = ['route' => $route]; |
||||||
803 | // PreviewController |
||||||
804 | $route = self::$plugin->handle . '/preview/social-media'; |
||||||
805 | $event->rules[self::FRONTEND_PREVIEW_PATH] = ['route' => $route]; |
||||||
806 | // Register our Control Panel routes |
||||||
807 | $event->rules = array_merge( |
||||||
808 | $event->rules, |
||||||
809 | $this->customFrontendRoutes() |
||||||
810 | ); |
||||||
811 | } |
||||||
812 | ); |
||||||
813 | } |
||||||
814 | |||||||
815 | /** |
||||||
816 | * Return the custom frontend routes |
||||||
817 | * |
||||||
818 | * @return array |
||||||
819 | */ |
||||||
820 | protected function customFrontendRoutes(): array |
||||||
821 | { |
||||||
822 | return [ |
||||||
823 | ]; |
||||||
824 | } |
||||||
825 | |||||||
826 | /** |
||||||
827 | * Install site event listeners for Control Panel requests only |
||||||
828 | */ |
||||||
829 | protected function installCpEventListeners(): void |
||||||
830 | { |
||||||
831 | // Load the frontend template containers |
||||||
832 | self::$plugin->frontendTemplates->loadFrontendTemplateContainers(); |
||||||
833 | // Handler: UrlManager::EVENT_REGISTER_CP_URL_RULES |
||||||
834 | Event::on( |
||||||
835 | UrlManager::class, |
||||||
836 | UrlManager::EVENT_REGISTER_CP_URL_RULES, |
||||||
837 | function(RegisterUrlRulesEvent $event) { |
||||||
838 | Craft::debug( |
||||||
839 | 'UrlManager::EVENT_REGISTER_CP_URL_RULES', |
||||||
840 | __METHOD__ |
||||||
841 | ); |
||||||
842 | // Register our Control Panel routes |
||||||
843 | $event->rules = array_merge( |
||||||
844 | $event->rules, |
||||||
845 | $this->customAdminCpRoutes() |
||||||
846 | ); |
||||||
847 | } |
||||||
848 | ); |
||||||
849 | // Handler: UserPermissions::EVENT_REGISTER_PERMISSIONS |
||||||
850 | Event::on( |
||||||
851 | UserPermissions::class, |
||||||
852 | UserPermissions::EVENT_REGISTER_PERMISSIONS, |
||||||
853 | function(RegisterUserPermissionsEvent $event) { |
||||||
854 | Craft::debug( |
||||||
855 | 'UserPermissions::EVENT_REGISTER_PERMISSIONS', |
||||||
856 | __METHOD__ |
||||||
857 | ); |
||||||
858 | // Register our custom permissions |
||||||
859 | $event->permissions[] = [ |
||||||
860 | 'heading' => Craft::t('seomatic', 'SEOmatic'), |
||||||
861 | 'permissions' => $this->customAdminCpPermissions(), |
||||||
862 | ]; |
||||||
863 | } |
||||||
864 | ); |
||||||
865 | // Handler: AutocompleteService::EVENT_REGISTER_CODEEDITOR_AUTOCOMPLETES |
||||||
866 | Event::on(AutocompleteService::class, AutocompleteService::EVENT_REGISTER_CODEEDITOR_AUTOCOMPLETES, |
||||||
867 | function(RegisterCodeEditorAutocompletesEvent $event) { |
||||||
868 | if ($event->fieldType === self::SEOMATIC_EXPRESSION_FIELD_TYPE) { |
||||||
869 | $event->types[] = EnvironmentVariableAutocomplete::class; |
||||||
870 | } |
||||||
871 | if ($event->fieldType === self::SEOMATIC_TRACKING_FIELD_TYPE) { |
||||||
872 | $event->types[] = TrackingVarsAutocomplete::class; |
||||||
873 | } |
||||||
874 | } |
||||||
875 | ); |
||||||
876 | // Handler: TwigTemplateValidator::EVENT_REGISTER_TWIG_VALIDATOR_VARIABLES |
||||||
877 | Event::on(TwigTemplateValidator::class, |
||||||
878 | TwigTemplateValidator::EVENT_REGISTER_TWIG_VALIDATOR_VARIABLES, |
||||||
879 | function(RegisterTwigValidatorVariablesEvent $event) { |
||||||
880 | if (Seomatic::$seomaticVariable === null) { |
||||||
881 | Seomatic::$seomaticVariable = new SeomaticVariable(); |
||||||
882 | Seomatic::$plugin->metaContainers->loadGlobalMetaContainers(); |
||||||
883 | Seomatic::$seomaticVariable->init(); |
||||||
0 ignored issues
–
show
The method
init() 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
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. ![]() |
|||||||
884 | } |
||||||
885 | $event->variables['seomatic'] = Seomatic::$seomaticVariable; |
||||||
886 | } |
||||||
887 | ); |
||||||
888 | } |
||||||
889 | |||||||
890 | /** |
||||||
891 | * Return the custom Control Panel routes |
||||||
892 | * |
||||||
893 | * @return array |
||||||
894 | */ |
||||||
895 | protected function customAdminCpRoutes(): array |
||||||
896 | { |
||||||
897 | return [ |
||||||
898 | 'seomatic' => |
||||||
899 | '', |
||||||
900 | 'seomatic/dashboard' => |
||||||
901 | 'seomatic/settings/dashboard', |
||||||
902 | 'seomatic/dashboard/<siteHandle:{handle}>' => |
||||||
903 | 'seomatic/settings/dashboard', |
||||||
904 | |||||||
905 | 'seomatic/global' => [ |
||||||
906 | 'route' => 'seomatic/settings/global', |
||||||
907 | 'defaults' => ['subSection' => 'general'], |
||||||
908 | ], |
||||||
909 | 'seomatic/global/<subSection:{handle}>' => |
||||||
910 | 'seomatic/settings/global', |
||||||
911 | 'seomatic/global/<subSection:{handle}>/<siteHandle:{handle}>' => |
||||||
912 | 'seomatic/settings/global', |
||||||
913 | |||||||
914 | 'seomatic/content' => |
||||||
915 | 'seomatic/settings/content', |
||||||
916 | 'seomatic/content/<siteHandle:{handle}>' => |
||||||
917 | 'seomatic/settings/content', |
||||||
918 | |||||||
919 | 'seomatic/edit-content/<subSection:{handle}>/<sourceBundleType:{handle}>/<sourceHandle:{handle}>' => |
||||||
920 | 'seomatic/settings/edit-content', |
||||||
921 | 'seomatic/edit-content/<subSection:{handle}>/<sourceBundleType:{handle}>/<sourceHandle:{handle}>/<siteHandle:{handle}>' => |
||||||
922 | 'seomatic/settings/edit-content', |
||||||
923 | |||||||
924 | // Seemingly duplicate route needed to handle Solspace Calendar, which allows characters like -'s |
||||||
925 | // in their handles |
||||||
926 | 'seomatic/edit-content/<subSection:{handle}>/<sourceBundleType:{handle}>/<sourceHandle:{slug}>' => |
||||||
927 | 'seomatic/settings/edit-content', |
||||||
928 | 'seomatic/edit-content/<subSection:{handle}>/<sourceBundleType:{handle}>/<sourceHandle:{slug}>/<siteHandle:{handle}>' => |
||||||
929 | 'seomatic/settings/edit-content', |
||||||
930 | |||||||
931 | 'seomatic/site' => [ |
||||||
932 | 'route' => 'seomatic/settings/site', |
||||||
933 | 'defaults' => ['subSection' => 'identity'], |
||||||
934 | ], |
||||||
935 | 'seomatic/site/<subSection:{handle}>' => |
||||||
936 | 'seomatic/settings/site', |
||||||
937 | 'seomatic/site/<subSection:{handle}>/<siteHandle:{handle}>' => |
||||||
938 | 'seomatic/settings/site', |
||||||
939 | |||||||
940 | 'seomatic/tracking' => [ |
||||||
941 | 'route' => 'seomatic/settings/tracking', |
||||||
942 | 'defaults' => ['subSection' => 'googleAnalytics'], |
||||||
943 | ], |
||||||
944 | 'seomatic/tracking/<subSection:{handle}>' => |
||||||
945 | 'seomatic/settings/tracking', |
||||||
946 | 'seomatic/tracking/<subSection:{handle}>/<siteHandle:{handle}>' => |
||||||
947 | 'seomatic/settings/tracking', |
||||||
948 | |||||||
949 | 'seomatic/plugin' => |
||||||
950 | 'seomatic/settings/plugin', |
||||||
951 | ]; |
||||||
952 | } |
||||||
953 | |||||||
954 | /** |
||||||
955 | * Returns the custom Control Panel user permissions. |
||||||
956 | * |
||||||
957 | * @return array |
||||||
958 | * @noinspection PhpArrayShapeAttributeCanBeAddedInspection |
||||||
959 | */ |
||||||
960 | protected function customAdminCpPermissions(): array |
||||||
961 | { |
||||||
962 | // The script meta containers for the global meta bundle |
||||||
963 | try { |
||||||
964 | $currentSiteId = Craft::$app->getSites()->getCurrentSite()->id ?? 1; |
||||||
965 | } catch (SiteNotFoundException $e) { |
||||||
966 | $currentSiteId = 1; |
||||||
967 | Craft::error($e->getMessage(), __METHOD__); |
||||||
968 | } |
||||||
969 | // Dynamic permissions for the scripts |
||||||
970 | $metaBundle = self::$plugin->metaBundles->getGlobalMetaBundle($currentSiteId); |
||||||
971 | $scriptsPerms = []; |
||||||
972 | if ($metaBundle !== null) { |
||||||
973 | $scripts = self::$plugin->metaBundles->getContainerDataFromBundle( |
||||||
974 | $metaBundle, |
||||||
975 | MetaScriptContainer::CONTAINER_TYPE |
||||||
976 | ); |
||||||
977 | foreach ($scripts as $scriptHandle => $scriptData) { |
||||||
978 | $scriptsPerms["seomatic:tracking-scripts:$scriptHandle"] = [ |
||||||
979 | 'label' => Craft::t('seomatic', $scriptData->name), |
||||||
980 | ]; |
||||||
981 | } |
||||||
982 | } |
||||||
983 | |||||||
984 | return [ |
||||||
985 | 'seomatic:dashboard' => [ |
||||||
986 | 'label' => Craft::t('seomatic', 'Dashboard'), |
||||||
987 | ], |
||||||
988 | 'seomatic:global-meta' => [ |
||||||
989 | 'label' => Craft::t('seomatic', 'Edit Global Meta'), |
||||||
990 | 'nested' => [ |
||||||
991 | 'seomatic:global-meta:general' => [ |
||||||
992 | 'label' => Craft::t('seomatic', 'General'), |
||||||
993 | ], |
||||||
994 | 'seomatic:global-meta:twitter' => [ |
||||||
995 | 'label' => Craft::t('seomatic', 'Twitter'), |
||||||
996 | ], |
||||||
997 | 'seomatic:global-meta:facebook' => [ |
||||||
998 | 'label' => Craft::t('seomatic', 'Facebook'), |
||||||
999 | ], |
||||||
1000 | 'seomatic:global-meta:robots' => [ |
||||||
1001 | 'label' => Craft::t('seomatic', 'Robots'), |
||||||
1002 | ], |
||||||
1003 | 'seomatic:global-meta:humans' => [ |
||||||
1004 | 'label' => Craft::t('seomatic', 'Humans'), |
||||||
1005 | ], |
||||||
1006 | 'seomatic:global-meta:ads' => [ |
||||||
1007 | 'label' => Craft::t('seomatic', 'Ads'), |
||||||
1008 | ], |
||||||
1009 | 'seomatic:global-meta:security' => [ |
||||||
1010 | 'label' => Craft::t('seomatic', 'Security'), |
||||||
1011 | ], |
||||||
1012 | ], |
||||||
1013 | ], |
||||||
1014 | 'seomatic:content-meta' => [ |
||||||
1015 | 'label' => Craft::t('seomatic', 'Edit Content SEO'), |
||||||
1016 | 'nested' => [ |
||||||
1017 | 'seomatic:content-meta:general' => [ |
||||||
1018 | 'label' => Craft::t('seomatic', 'General'), |
||||||
1019 | ], |
||||||
1020 | 'seomatic:content-meta:twitter' => [ |
||||||
1021 | 'label' => Craft::t('seomatic', 'Twitter'), |
||||||
1022 | ], |
||||||
1023 | 'seomatic:content-meta:facebook' => [ |
||||||
1024 | 'label' => Craft::t('seomatic', 'Facebook'), |
||||||
1025 | ], |
||||||
1026 | 'seomatic:content-meta:sitemap' => [ |
||||||
1027 | 'label' => Craft::t('seomatic', 'Sitemap'), |
||||||
1028 | ], |
||||||
1029 | ], |
||||||
1030 | ], |
||||||
1031 | 'seomatic:site-settings' => [ |
||||||
1032 | 'label' => Craft::t('seomatic', 'Edit Site Settings'), |
||||||
1033 | 'nested' => [ |
||||||
1034 | 'seomatic:site-settings:identity' => [ |
||||||
1035 | 'label' => Craft::t('seomatic', 'Identity'), |
||||||
1036 | ], |
||||||
1037 | 'seomatic:site-settings:creator' => [ |
||||||
1038 | 'label' => Craft::t('seomatic', 'Creator'), |
||||||
1039 | ], |
||||||
1040 | 'seomatic:site-settings:social' => [ |
||||||
1041 | 'label' => Craft::t('seomatic', 'Social Media'), |
||||||
1042 | ], |
||||||
1043 | 'seomatic:site-settings:sitemap' => [ |
||||||
1044 | 'label' => Craft::t('seomatic', 'Sitemap'), |
||||||
1045 | ], |
||||||
1046 | 'seomatic:site-settings:miscellaneous' => [ |
||||||
1047 | 'label' => Craft::t('seomatic', 'Miscellaneous'), |
||||||
1048 | ], |
||||||
1049 | ], |
||||||
1050 | ], |
||||||
1051 | 'seomatic:tracking-scripts' => [ |
||||||
1052 | 'label' => Craft::t('seomatic', 'Edit Tracking Scripts'), |
||||||
1053 | 'nested' => $scriptsPerms, |
||||||
1054 | ], |
||||||
1055 | 'seomatic:plugin-settings' => [ |
||||||
1056 | 'label' => Craft::t('seomatic', 'Edit Plugin Settings'), |
||||||
1057 | ], |
||||||
1058 | ]; |
||||||
1059 | } |
||||||
1060 | |||||||
1061 | /** |
||||||
1062 | * Returns the custom Control Panel cache options. |
||||||
1063 | * |
||||||
1064 | * @return array |
||||||
1065 | */ |
||||||
1066 | protected function customAdminCpCacheOptions(): array |
||||||
1067 | { |
||||||
1068 | return [ |
||||||
1069 | // Frontend template caches |
||||||
1070 | [ |
||||||
1071 | 'key' => 'seomatic-frontendtemplate-caches', |
||||||
1072 | 'label' => Craft::t('seomatic', 'SEOmatic frontend template caches'), |
||||||
1073 | 'action' => [self::$plugin->frontendTemplates, 'invalidateCaches'], |
||||||
1074 | ], |
||||||
1075 | // Meta bundle caches |
||||||
1076 | [ |
||||||
1077 | 'key' => 'seomatic-metabundle-caches', |
||||||
1078 | 'label' => Craft::t('seomatic', 'SEOmatic metadata caches'), |
||||||
1079 | 'action' => [self::$plugin->metaContainers, 'invalidateCaches'], |
||||||
1080 | ], |
||||||
1081 | // Sitemap caches |
||||||
1082 | [ |
||||||
1083 | 'key' => 'seomatic-sitemap-caches', |
||||||
1084 | 'label' => Craft::t('seomatic', 'SEOmatic sitemap caches'), |
||||||
1085 | 'action' => [self::$plugin->sitemaps, 'invalidateCaches'], |
||||||
1086 | ], |
||||||
1087 | // Schema caches |
||||||
1088 | [ |
||||||
1089 | 'key' => 'seomatic-schema-caches', |
||||||
1090 | 'label' => Craft::t('seomatic', 'SEOmatic schema caches'), |
||||||
1091 | 'action' => [SchemaHelper::class, 'invalidateCaches'], |
||||||
1092 | ], |
||||||
1093 | ]; |
||||||
1094 | } |
||||||
1095 | |||||||
1096 | /** |
||||||
1097 | * @inheritdoc |
||||||
1098 | */ |
||||||
1099 | protected function createSettingsModel(): ?Model |
||||||
1100 | { |
||||||
1101 | return new Settings(); |
||||||
1102 | } |
||||||
1103 | } |
||||||
1104 |
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:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths