1 | <?php |
||||||
2 | /** |
||||||
3 | * Retour plugin for Craft CMS |
||||||
4 | * |
||||||
5 | * Retour allows you to intelligently redirect legacy URLs, so that you don't |
||||||
6 | * lose SEO value when rebuilding & restructuring a website |
||||||
7 | * |
||||||
8 | * @link https://nystudio107.com/ |
||||||
9 | * @copyright Copyright (c) 2018 nystudio107 |
||||||
10 | */ |
||||||
11 | |||||||
12 | namespace nystudio107\retour; |
||||||
13 | |||||||
14 | use Craft; |
||||||
15 | use craft\base\Element; |
||||||
16 | use craft\base\Model; |
||||||
17 | use craft\base\Plugin; |
||||||
18 | use craft\events\ElementEvent; |
||||||
19 | use craft\events\ExceptionEvent; |
||||||
20 | use craft\events\PluginEvent; |
||||||
21 | use craft\events\RegisterCacheOptionsEvent; |
||||||
22 | use craft\events\RegisterComponentTypesEvent; |
||||||
23 | use craft\events\RegisterGqlQueriesEvent; |
||||||
24 | use craft\events\RegisterGqlSchemaComponentsEvent; |
||||||
25 | use craft\events\RegisterGqlTypesEvent; |
||||||
26 | use craft\events\RegisterUrlRulesEvent; |
||||||
27 | use craft\events\RegisterUserPermissionsEvent; |
||||||
28 | use craft\helpers\ElementHelper; |
||||||
29 | use craft\helpers\UrlHelper; |
||||||
30 | use craft\services\Dashboard; |
||||||
31 | use craft\services\Elements; |
||||||
32 | use craft\services\Fields; |
||||||
33 | use craft\services\Gql; |
||||||
34 | use craft\services\Plugins; |
||||||
35 | use craft\services\UserPermissions; |
||||||
36 | use craft\utilities\ClearCaches; |
||||||
37 | use craft\web\ErrorHandler; |
||||||
38 | use craft\web\twig\variables\CraftVariable; |
||||||
39 | use craft\web\UrlManager; |
||||||
40 | use nystudio107\retour\fields\ShortLink as ShortLinkField; |
||||||
41 | use nystudio107\retour\gql\interfaces\RetourInterface; |
||||||
42 | use nystudio107\retour\gql\queries\RetourQuery; |
||||||
43 | use nystudio107\retour\models\Settings; |
||||||
44 | use nystudio107\retour\services\ServicesTrait; |
||||||
45 | use nystudio107\retour\variables\RetourVariable; |
||||||
46 | use nystudio107\retour\widgets\RetourWidget; |
||||||
47 | use Twig\Error\RuntimeError; |
||||||
48 | use yii\base\Event; |
||||||
49 | use yii\web\HttpException; |
||||||
50 | |||||||
51 | /** @noinspection MissingPropertyAnnotationsInspection */ |
||||||
52 | |||||||
53 | /** |
||||||
54 | * Class Retour |
||||||
55 | * |
||||||
56 | * @author nystudio107 |
||||||
57 | * @package Retour |
||||||
58 | * @since 3.0.0 |
||||||
59 | * @method Settings getSettings() |
||||||
60 | */ |
||||||
61 | class Retour extends Plugin |
||||||
62 | { |
||||||
63 | // Traits |
||||||
64 | // ========================================================================= |
||||||
65 | |||||||
66 | use ServicesTrait; |
||||||
67 | |||||||
68 | // Constants |
||||||
69 | // ========================================================================= |
||||||
70 | |||||||
71 | public const DEVMODE_CACHE_DURATION = 30; |
||||||
72 | |||||||
73 | // Static Properties |
||||||
74 | // ========================================================================= |
||||||
75 | |||||||
76 | /** |
||||||
77 | * @var Retour |
||||||
78 | */ |
||||||
79 | public static ?Plugin $plugin = null; |
||||||
80 | |||||||
81 | /** |
||||||
82 | * @var ?Settings |
||||||
83 | */ |
||||||
84 | public static ?Settings $settings = null; |
||||||
85 | |||||||
86 | /** |
||||||
87 | * @var int |
||||||
88 | */ |
||||||
89 | public static int $cacheDuration = 0; |
||||||
90 | |||||||
91 | /** |
||||||
92 | * @var ?HttpException |
||||||
93 | */ |
||||||
94 | public static ?HttpException $currentException = null; |
||||||
95 | |||||||
96 | // Public Properties |
||||||
97 | // ========================================================================= |
||||||
98 | |||||||
99 | /** |
||||||
100 | * @var string |
||||||
101 | */ |
||||||
102 | public string $schemaVersion = '3.0.12'; |
||||||
103 | |||||||
104 | /** |
||||||
105 | * @var bool |
||||||
106 | */ |
||||||
107 | public bool $hasCpSection = true; |
||||||
108 | |||||||
109 | /** |
||||||
110 | * @var bool |
||||||
111 | */ |
||||||
112 | public bool $hasCpSettings = true; |
||||||
113 | |||||||
114 | // Public Methods |
||||||
115 | // ========================================================================= |
||||||
116 | |||||||
117 | /** |
||||||
118 | * @inheritdoc |
||||||
119 | */ |
||||||
120 | public function init(): void |
||||||
121 | { |
||||||
122 | parent::init(); |
||||||
123 | self::$plugin = $this; |
||||||
124 | // Initialize properties |
||||||
125 | self::$settings = $this->getSettings(); |
||||||
126 | $this->name = self::$settings->pluginName; |
||||||
127 | self::$cacheDuration = Craft::$app->getConfig()->getGeneral()->devMode |
||||||
128 | ? $this::DEVMODE_CACHE_DURATION |
||||||
129 | : 0; |
||||||
130 | // Handle any console commands |
||||||
131 | $request = Craft::$app->getRequest(); |
||||||
132 | if ($request->getIsConsoleRequest()) { |
||||||
133 | $this->controllerNamespace = 'nystudio107\retour\console\controllers'; |
||||||
134 | } |
||||||
135 | // Install our event listeners |
||||||
136 | $this->installEventListeners(); |
||||||
137 | // Log that Retour has been loaded |
||||||
138 | Craft::info( |
||||||
139 | Craft::t( |
||||||
140 | 'retour', |
||||||
141 | '{name} plugin loaded', |
||||||
142 | ['name' => $this->name] |
||||||
143 | ), |
||||||
144 | __METHOD__ |
||||||
145 | ); |
||||||
146 | } |
||||||
147 | |||||||
148 | /** |
||||||
149 | * Clear all the caches! |
||||||
150 | */ |
||||||
151 | public function clearAllCaches(): void |
||||||
152 | { |
||||||
153 | // Clear all of Retour's caches |
||||||
154 | self::$plugin->redirects->invalidateCaches(); |
||||||
0 ignored issues
–
show
|
|||||||
155 | } |
||||||
156 | |||||||
157 | /** |
||||||
158 | * @inheritdoc |
||||||
159 | */ |
||||||
160 | public function getSettingsResponse(): mixed |
||||||
161 | { |
||||||
162 | // Just redirect to the plugin settings page |
||||||
163 | return Craft::$app->getResponse()->redirect(UrlHelper::cpUrl('retour/settings')); |
||||||
164 | } |
||||||
165 | |||||||
166 | /** |
||||||
167 | * @inheritdoc |
||||||
168 | */ |
||||||
169 | public function getCpNavItem(): ?array |
||||||
170 | { |
||||||
171 | $subNavs = []; |
||||||
172 | $navItem = parent::getCpNavItem(); |
||||||
173 | $currentUser = Craft::$app->getUser()->getIdentity(); |
||||||
174 | // Only show sub-navs the user has permission to view |
||||||
175 | if ($currentUser->can('retour: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
![]() |
|||||||
176 | $subNavs['dashboard'] = [ |
||||||
177 | 'label' => 'Dashboard', |
||||||
178 | 'url' => 'retour/dashboard', |
||||||
179 | ]; |
||||||
180 | } |
||||||
181 | if ($currentUser->can('retour:redirects')) { |
||||||
182 | $subNavs['redirects'] = [ |
||||||
183 | 'label' => 'Redirects', |
||||||
184 | 'url' => 'retour/redirects', |
||||||
185 | ]; |
||||||
186 | } |
||||||
187 | if ($currentUser->can('retour:shortlinks')) { |
||||||
188 | $subNavs['shortlinks'] = [ |
||||||
189 | 'label' => 'Short Links', |
||||||
190 | 'url' => 'retour/shortlinks', |
||||||
191 | ]; |
||||||
192 | } |
||||||
193 | $editableSettings = true; |
||||||
194 | $general = Craft::$app->getConfig()->getGeneral(); |
||||||
195 | if (!$general->allowAdminChanges) { |
||||||
196 | $editableSettings = false; |
||||||
197 | } |
||||||
198 | if ($currentUser->can('retour:settings') && $editableSettings) { |
||||||
199 | $subNavs['settings'] = [ |
||||||
200 | 'label' => 'Settings', |
||||||
201 | 'url' => 'retour/settings', |
||||||
202 | ]; |
||||||
203 | } |
||||||
204 | // Retour 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 |
||||||
205 | if (empty($subNavs)) { |
||||||
206 | return null; |
||||||
207 | } |
||||||
208 | // A single sub nav item is redundant |
||||||
209 | if (count($subNavs) === 1) { |
||||||
210 | $subNavs = []; |
||||||
211 | } |
||||||
212 | $navItem = array_merge($navItem, [ |
||||||
213 | 'subnav' => $subNavs, |
||||||
214 | ]); |
||||||
215 | |||||||
216 | return $navItem; |
||||||
217 | } |
||||||
218 | |||||||
219 | // Protected Methods |
||||||
220 | // ========================================================================= |
||||||
221 | |||||||
222 | /** |
||||||
223 | * Install our event listeners. |
||||||
224 | */ |
||||||
225 | protected function installEventListeners(): void |
||||||
226 | { |
||||||
227 | // Install our event listeners only if our table schema exists |
||||||
228 | if ($this->tableSchemaExists()) { |
||||||
229 | $request = Craft::$app->getRequest(); |
||||||
230 | // Add in our event listeners that are needed for every request |
||||||
231 | $this->installGlobalEventListeners(); |
||||||
232 | // Install only for non-console site requests |
||||||
233 | if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()) { |
||||||
234 | $this->installSiteEventListeners(); |
||||||
235 | } |
||||||
236 | // Install only for non-console Control Panel requests |
||||||
237 | if ($request->getIsCpRequest() && !$request->getIsConsoleRequest()) { |
||||||
238 | $this->installCpEventListeners(); |
||||||
239 | } |
||||||
240 | } |
||||||
241 | // Handler: ClearCaches::EVENT_REGISTER_CACHE_OPTIONS |
||||||
242 | Event::on( |
||||||
243 | ClearCaches::class, |
||||||
244 | ClearCaches::EVENT_REGISTER_CACHE_OPTIONS, |
||||||
245 | function(RegisterCacheOptionsEvent $event) { |
||||||
246 | Craft::debug( |
||||||
247 | 'ClearCaches::EVENT_REGISTER_CACHE_OPTIONS', |
||||||
248 | __METHOD__ |
||||||
249 | ); |
||||||
250 | // Register our Control Panel routes |
||||||
251 | $event->options = array_merge( |
||||||
252 | $event->options, |
||||||
253 | $this->customAdminCpCacheOptions() |
||||||
254 | ); |
||||||
255 | } |
||||||
256 | ); |
||||||
257 | // Handler: EVENT_AFTER_INSTALL_PLUGIN |
||||||
258 | Event::on( |
||||||
259 | Plugins::class, |
||||||
260 | Plugins::EVENT_AFTER_INSTALL_PLUGIN, |
||||||
261 | function(PluginEvent $event) { |
||||||
262 | if ($event->plugin === $this) { |
||||||
263 | // Invalidate our caches after we've been installed |
||||||
264 | $this->clearAllCaches(); |
||||||
265 | // Send them to our welcome screen |
||||||
266 | $request = Craft::$app->getRequest(); |
||||||
267 | if ($request->isCpRequest) { |
||||||
268 | Craft::$app->getResponse()->redirect(UrlHelper::cpUrl( |
||||||
269 | 'retour/dashboard', |
||||||
270 | [ |
||||||
271 | 'showWelcome' => true, |
||||||
272 | ] |
||||||
273 | ))->send(); |
||||||
274 | } |
||||||
275 | } |
||||||
276 | } |
||||||
277 | ); |
||||||
278 | } |
||||||
279 | |||||||
280 | /** |
||||||
281 | * Determine whether our table schema exists or not; this is needed because |
||||||
282 | * migrations such as the install migration and base_install migration may |
||||||
283 | * not have been run by the time our init() method has been called |
||||||
284 | * |
||||||
285 | * @return bool |
||||||
286 | */ |
||||||
287 | protected function tableSchemaExists(): bool |
||||||
288 | { |
||||||
289 | return (Craft::$app->db->schema->getTableSchema('{{%retour_redirects}}') !== null); |
||||||
290 | } |
||||||
291 | |||||||
292 | /** |
||||||
293 | * Install global event listeners for all request types |
||||||
294 | */ |
||||||
295 | protected function installGlobalEventListeners(): void |
||||||
296 | { |
||||||
297 | Event::on( |
||||||
298 | CraftVariable::class, |
||||||
299 | CraftVariable::EVENT_INIT, |
||||||
300 | function(Event $event) { |
||||||
301 | /** @var CraftVariable $variable */ |
||||||
302 | $variable = $event->sender; |
||||||
303 | $variable->set('retour', [ |
||||||
304 | 'class' => RetourVariable::class, |
||||||
305 | 'viteService' => $this->vite, |
||||||
306 | ]); |
||||||
307 | } |
||||||
308 | ); |
||||||
309 | |||||||
310 | $prepareRedirectOnElementChange = function(ElementEvent $event) { |
||||||
311 | /** @var Element $element */ |
||||||
312 | $element = $event->element; |
||||||
313 | if (!$event->isNew && $element->getUrl() !== null && !$element->propagating) { |
||||||
314 | $checkElementSlug = true; |
||||||
315 | // Make sure the element is enabled |
||||||
316 | if (!$element->enabled || !$element->getEnabledForSite()) { |
||||||
317 | $checkElementSlug = false; |
||||||
318 | } |
||||||
319 | // If we're running Craft 3.2 or later, also check that isn't not a draft or revision |
||||||
320 | if (ElementHelper::isDraftOrRevision($element)) { |
||||||
321 | $checkElementSlug = false; |
||||||
322 | } |
||||||
323 | // Only do this for elements that aren't new, pass $checkElementSlug, and the user |
||||||
324 | // has turned on the setting |
||||||
325 | if (self::$settings->createUriChangeRedirects && $checkElementSlug) { |
||||||
326 | // Make sure this isn't a transitioning temporary draft/revision and that it's |
||||||
327 | // not propagating to other sites |
||||||
328 | if ($element->uri && !str_contains($element->uri, '__temp_')) { |
||||||
329 | Retour::$plugin->events->stashElementUris($element); |
||||||
0 ignored issues
–
show
The method
stashElementUris() 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. ![]() |
|||||||
330 | } |
||||||
331 | } |
||||||
332 | } |
||||||
333 | }; |
||||||
334 | |||||||
335 | $insertRedirectOnElementChange = function(ElementEvent $event) { |
||||||
336 | /** @var Element $element */ |
||||||
337 | $element = $event->element; |
||||||
338 | if ($element !== null && !$event->isNew && $element->getUrl() !== null) { |
||||||
339 | $checkElementSlug = true; |
||||||
340 | // Make sure the element is enabled |
||||||
341 | if (!$element->enabled || !$element->getEnabledForSite()) { |
||||||
342 | $checkElementSlug = false; |
||||||
343 | } |
||||||
344 | if (ElementHelper::isDraftOrRevision($element)) { |
||||||
345 | $checkElementSlug = false; |
||||||
346 | } |
||||||
347 | if (self::$settings->createUriChangeRedirects && $checkElementSlug) { |
||||||
348 | Retour::$plugin->events->handleElementUriChange($element); |
||||||
349 | } |
||||||
350 | } |
||||||
351 | }; |
||||||
352 | |||||||
353 | // Handler: Elements::EVENT_BEFORE_SAVE_ELEMENT |
||||||
354 | Event::on( |
||||||
355 | Elements::class, |
||||||
356 | Elements::EVENT_BEFORE_SAVE_ELEMENT, |
||||||
357 | static function(ElementEvent $event) use ($prepareRedirectOnElementChange) { |
||||||
358 | Craft::debug( |
||||||
359 | 'Elements::EVENT_BEFORE_SAVE_ELEMENT', |
||||||
360 | __METHOD__ |
||||||
361 | ); |
||||||
362 | $prepareRedirectOnElementChange($event); |
||||||
363 | } |
||||||
364 | ); |
||||||
365 | // Handler: Elements::EVENT_AFTER_SAVE_ELEMENT |
||||||
366 | Event::on( |
||||||
367 | Elements::class, |
||||||
368 | Elements::EVENT_AFTER_SAVE_ELEMENT, |
||||||
369 | static function(ElementEvent $event) use ($insertRedirectOnElementChange) { |
||||||
370 | Craft::debug( |
||||||
371 | 'Elements::EVENT_AFTER_SAVE_ELEMENT', |
||||||
372 | __METHOD__ |
||||||
373 | ); |
||||||
374 | $insertRedirectOnElementChange($event); |
||||||
375 | } |
||||||
376 | ); |
||||||
377 | // Handler: Elements::EVENT_BEFORE_UPDATE_SLUG_AND_URI |
||||||
378 | Event::on( |
||||||
379 | Elements::class, |
||||||
380 | Elements::EVENT_BEFORE_UPDATE_SLUG_AND_URI, |
||||||
381 | static function(ElementEvent $event) use ($prepareRedirectOnElementChange) { |
||||||
382 | Craft::debug( |
||||||
383 | 'Elements::EVENT_BEFORE_UPDATE_SLUG_AND_URI', |
||||||
384 | __METHOD__ |
||||||
385 | ); |
||||||
386 | $prepareRedirectOnElementChange($event); |
||||||
387 | } |
||||||
388 | ); |
||||||
389 | // Handler: Elements::EVENT_AFTER_UPDATE_SLUG_AND_URI |
||||||
390 | Event::on( |
||||||
391 | Elements::class, |
||||||
392 | Elements::EVENT_AFTER_UPDATE_SLUG_AND_URI, |
||||||
393 | static function(ElementEvent $event) use ($insertRedirectOnElementChange) { |
||||||
394 | Craft::debug( |
||||||
395 | 'Elements::EVENT_AFTER_UPDATE_SLUG_AND_URI', |
||||||
396 | __METHOD__ |
||||||
397 | ); |
||||||
398 | $insertRedirectOnElementChange($event); |
||||||
399 | } |
||||||
400 | ); |
||||||
401 | // Handler: Plugins::EVENT_AFTER_LOAD_PLUGINS |
||||||
402 | Event::on( |
||||||
403 | Plugins::class, |
||||||
404 | Plugins::EVENT_AFTER_LOAD_PLUGINS, |
||||||
405 | function() { |
||||||
406 | // Install these only after all other plugins have loaded |
||||||
407 | $request = Craft::$app->getRequest(); |
||||||
408 | // Only respond to non-console site requests |
||||||
409 | if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()) { |
||||||
410 | $this->handleSiteRequest(); |
||||||
411 | } |
||||||
412 | // Respond to Control Panel requests |
||||||
413 | if ($request->getIsCpRequest() && !$request->getIsConsoleRequest()) { |
||||||
414 | $this->handleAdminCpRequest(); |
||||||
415 | } |
||||||
416 | } |
||||||
417 | ); |
||||||
418 | // Handler: Fields::EVENT_REGISTER_FIELD_TYPES |
||||||
419 | Event::on( |
||||||
420 | Fields::class, |
||||||
421 | Fields::EVENT_REGISTER_FIELD_TYPES, |
||||||
422 | function(RegisterComponentTypesEvent $event) { |
||||||
423 | $event->types[] = ShortLinkField::class; |
||||||
424 | } |
||||||
425 | ); |
||||||
426 | // Handler: Gql::EVENT_REGISTER_GQL_TYPES |
||||||
427 | Event::on( |
||||||
428 | Gql::class, |
||||||
429 | Gql::EVENT_REGISTER_GQL_TYPES, |
||||||
430 | static function(RegisterGqlTypesEvent $event) { |
||||||
431 | Craft::debug( |
||||||
432 | 'Gql::EVENT_REGISTER_GQL_TYPES', |
||||||
433 | __METHOD__ |
||||||
434 | ); |
||||||
435 | $event->types[] = RetourInterface::class; |
||||||
436 | } |
||||||
437 | ); |
||||||
438 | // Handler: Gql::EVENT_REGISTER_GQL_QUERIES |
||||||
439 | Event::on( |
||||||
440 | Gql::class, |
||||||
441 | Gql::EVENT_REGISTER_GQL_QUERIES, |
||||||
442 | static function(RegisterGqlQueriesEvent $event) { |
||||||
443 | Craft::debug( |
||||||
444 | 'Gql::EVENT_REGISTER_GQL_QUERIES', |
||||||
445 | __METHOD__ |
||||||
446 | ); |
||||||
447 | $queries = RetourQuery::getQueries(); |
||||||
448 | foreach ($queries as $key => $value) { |
||||||
449 | $event->queries[$key] = $value; |
||||||
450 | } |
||||||
451 | } |
||||||
452 | ); |
||||||
453 | // Handler: Gql::EVENT_REGISTER_SCHEMA_COMPONENTS |
||||||
454 | Event::on( |
||||||
455 | Gql::class, |
||||||
456 | Gql::EVENT_REGISTER_GQL_SCHEMA_COMPONENTS, |
||||||
457 | static function(RegisterGqlSchemaComponentsEvent $event) { |
||||||
458 | Craft::debug( |
||||||
459 | 'Gql::EVENT_REGISTER_GQL_SCHEMA_COMPONENTS', |
||||||
460 | __METHOD__ |
||||||
461 | ); |
||||||
462 | $label = Craft::t('retour', 'Retour'); |
||||||
463 | $event->queries[$label]['retour.all:read'] = ['label' => Craft::t('retour', 'Query Retour data')]; |
||||||
464 | } |
||||||
465 | ); |
||||||
466 | } |
||||||
467 | |||||||
468 | /** |
||||||
469 | * Handle site requests. We do it only after we receive the event |
||||||
470 | * EVENT_AFTER_LOAD_PLUGINS so that any pending db migrations can be run |
||||||
471 | * before our event listeners kick in |
||||||
472 | */ |
||||||
473 | protected function handleSiteRequest(): void |
||||||
474 | { |
||||||
475 | // Handler: ErrorHandler::EVENT_BEFORE_HANDLE_EXCEPTION |
||||||
476 | Event::on( |
||||||
477 | ErrorHandler::class, |
||||||
478 | ErrorHandler::EVENT_BEFORE_HANDLE_EXCEPTION, |
||||||
479 | static function(ExceptionEvent $event) { |
||||||
480 | Craft::debug( |
||||||
481 | 'ErrorHandler::EVENT_BEFORE_HANDLE_EXCEPTION', |
||||||
482 | __METHOD__ |
||||||
483 | ); |
||||||
484 | $exception = $event->exception; |
||||||
485 | // If this is a Twig Runtime exception, use the previous one instead |
||||||
486 | if ($exception instanceof RuntimeError && |
||||||
487 | ($previousException = $exception->getPrevious()) !== null) { |
||||||
488 | $exception = $previousException; |
||||||
489 | } |
||||||
490 | // If this is a 404 error, see if we can handle it |
||||||
491 | if ($exception instanceof HttpException && $exception->statusCode === 404) { |
||||||
492 | self::$currentException = $exception; |
||||||
493 | Retour::$plugin->redirects->handle404(); |
||||||
0 ignored issues
–
show
The method
handle404() 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. ![]() |
|||||||
494 | } |
||||||
495 | } |
||||||
496 | ); |
||||||
497 | } |
||||||
498 | |||||||
499 | /** |
||||||
500 | * Handle Control Panel requests. We do it only after we receive the event |
||||||
501 | * EVENT_AFTER_LOAD_PLUGINS so that any pending db migrations can be run |
||||||
502 | * before our event listeners kick in |
||||||
503 | */ |
||||||
504 | protected function handleAdminCpRequest(): void |
||||||
505 | { |
||||||
506 | } |
||||||
507 | |||||||
508 | /** |
||||||
509 | * Install site event listeners for site requests only |
||||||
510 | */ |
||||||
511 | protected function installSiteEventListeners(): void |
||||||
512 | { |
||||||
513 | // Handler: UrlManager::EVENT_REGISTER_SITE_URL_RULES |
||||||
514 | Event::on( |
||||||
515 | UrlManager::class, |
||||||
516 | UrlManager::EVENT_REGISTER_SITE_URL_RULES, |
||||||
517 | function(RegisterUrlRulesEvent $event) { |
||||||
518 | Craft::debug( |
||||||
519 | 'UrlManager::EVENT_REGISTER_SITE_URL_RULES', |
||||||
520 | __METHOD__ |
||||||
521 | ); |
||||||
522 | // Register our Control Panel routes |
||||||
523 | $event->rules = array_merge( |
||||||
524 | $event->rules, |
||||||
525 | $this->customFrontendRoutes() |
||||||
526 | ); |
||||||
527 | } |
||||||
528 | ); |
||||||
529 | } |
||||||
530 | |||||||
531 | /** |
||||||
532 | * Return the custom frontend routes |
||||||
533 | * |
||||||
534 | * @return array |
||||||
535 | */ |
||||||
536 | protected function customFrontendRoutes(): array |
||||||
537 | { |
||||||
538 | return [ |
||||||
539 | ]; |
||||||
540 | } |
||||||
541 | |||||||
542 | /** |
||||||
543 | * Install site event listeners for Control Panel requests only |
||||||
544 | */ |
||||||
545 | protected function installCpEventListeners(): void |
||||||
546 | { |
||||||
547 | // Handler: Dashboard::EVENT_REGISTER_WIDGET_TYPES |
||||||
548 | Event::on( |
||||||
549 | Dashboard::class, |
||||||
550 | Dashboard::EVENT_REGISTER_WIDGET_TYPES, |
||||||
551 | function(RegisterComponentTypesEvent $event) { |
||||||
552 | $currentUser = Craft::$app->getUser()->getIdentity(); |
||||||
553 | if ($currentUser->can('accessPlugin-retour')) { |
||||||
554 | $event->types[] = RetourWidget::class; |
||||||
555 | } |
||||||
556 | } |
||||||
557 | ); |
||||||
558 | // Handler: UrlManager::EVENT_REGISTER_CP_URL_RULES |
||||||
559 | Event::on( |
||||||
560 | UrlManager::class, |
||||||
561 | UrlManager::EVENT_REGISTER_CP_URL_RULES, |
||||||
562 | function(RegisterUrlRulesEvent $event) { |
||||||
563 | Craft::debug( |
||||||
564 | 'UrlManager::EVENT_REGISTER_CP_URL_RULES', |
||||||
565 | __METHOD__ |
||||||
566 | ); |
||||||
567 | // Register our Control Panel routes |
||||||
568 | $event->rules = array_merge( |
||||||
569 | $event->rules, |
||||||
570 | $this->customAdminCpRoutes() |
||||||
571 | ); |
||||||
572 | } |
||||||
573 | ); |
||||||
574 | // Handler: UserPermissions::EVENT_REGISTER_PERMISSIONS |
||||||
575 | Event::on( |
||||||
576 | UserPermissions::class, |
||||||
577 | UserPermissions::EVENT_REGISTER_PERMISSIONS, |
||||||
578 | function(RegisterUserPermissionsEvent $event) { |
||||||
579 | Craft::debug( |
||||||
580 | 'UserPermissions::EVENT_REGISTER_PERMISSIONS', |
||||||
581 | __METHOD__ |
||||||
582 | ); |
||||||
583 | // Register our custom permissions |
||||||
584 | $event->permissions[] = [ |
||||||
585 | 'heading' => Craft::t('retour', 'Retour'), |
||||||
586 | 'permissions' => $this->customAdminCpPermissions(), |
||||||
587 | ]; |
||||||
588 | } |
||||||
589 | ); |
||||||
590 | } |
||||||
591 | |||||||
592 | /** |
||||||
593 | * Return the custom Control Panel routes |
||||||
594 | * |
||||||
595 | * @return array |
||||||
596 | */ |
||||||
597 | protected function customAdminCpRoutes(): array |
||||||
598 | { |
||||||
599 | return [ |
||||||
600 | 'retour' => '', |
||||||
601 | |||||||
602 | 'retour/redirects' => 'retour/redirects/redirects', |
||||||
603 | 'retour/redirects/<siteHandle:{handle}>' => 'retour/redirects/redirects', |
||||||
604 | |||||||
605 | 'retour/edit-redirect/<redirectId:\d+>' => 'retour/redirects/edit-redirect', |
||||||
606 | |||||||
607 | 'retour/add-redirect' => 'retour/redirects/edit-redirect', |
||||||
608 | 'retour/add-redirect/<siteId:\d+>' => 'retour/redirects/edit-redirect', |
||||||
609 | |||||||
610 | 'retour/dashboard' => 'retour/statistics/dashboard', |
||||||
611 | 'retour/dashboard/<siteHandle:{handle}>' => 'retour/statistics/dashboard', |
||||||
612 | |||||||
613 | 'retour/shortlinks' => 'retour/redirects/shortlinks', |
||||||
614 | 'retour/shortlinks/<siteHandle:{handle}>' => 'retour/redirects/shortlinks', |
||||||
615 | |||||||
616 | 'retour/settings' => 'retour/settings/plugin-settings', |
||||||
617 | ]; |
||||||
618 | } |
||||||
619 | |||||||
620 | /** |
||||||
621 | * Returns the custom Control Panel user permissions. |
||||||
622 | * |
||||||
623 | * @return array |
||||||
624 | */ |
||||||
625 | protected function customAdminCpPermissions(): array |
||||||
626 | { |
||||||
627 | return [ |
||||||
628 | 'retour:dashboard' => [ |
||||||
629 | 'label' => Craft::t('retour', 'Dashboard'), |
||||||
630 | ], |
||||||
631 | 'retour:redirects' => [ |
||||||
632 | 'label' => Craft::t('retour', 'Redirects'), |
||||||
633 | ], |
||||||
634 | 'retour:shortlinks' => [ |
||||||
635 | 'label' => Craft::t('retour', 'Short Links'), |
||||||
636 | ], |
||||||
637 | 'retour:settings' => [ |
||||||
638 | 'label' => Craft::t('retour', 'Settings'), |
||||||
639 | ], |
||||||
640 | ]; |
||||||
641 | } |
||||||
642 | |||||||
643 | /** |
||||||
644 | * Returns the custom Control Panel cache options. |
||||||
645 | * |
||||||
646 | * @return array |
||||||
647 | */ |
||||||
648 | protected function customAdminCpCacheOptions(): array |
||||||
649 | { |
||||||
650 | return [ |
||||||
651 | [ |
||||||
652 | 'key' => 'retour-redirect-caches', |
||||||
653 | 'label' => Craft::t('retour', 'Retour redirect caches'), |
||||||
654 | 'action' => [self::$plugin->redirects, 'invalidateCaches'], |
||||||
655 | ], |
||||||
656 | ]; |
||||||
657 | } |
||||||
658 | |||||||
659 | /** |
||||||
660 | * @inheritdoc |
||||||
661 | */ |
||||||
662 | protected function createSettingsModel(): ?Model |
||||||
663 | { |
||||||
664 | return new Settings(); |
||||||
665 | } |
||||||
666 | } |
||||||
667 |
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.