1 | <?php |
||||
2 | /** |
||||
3 | * Retour plugin for Craft CMS 3.x |
||||
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/ |
||||
0 ignored issues
–
show
Coding Style
introduced
by
Loading history...
|
|||||
9 | * @copyright Copyright (c) 2018 nystudio107 |
||||
0 ignored issues
–
show
|
|||||
10 | */ |
||||
0 ignored issues
–
show
|
|||||
11 | |||||
12 | namespace nystudio107\retour; |
||||
13 | |||||
14 | use craft\events\RegisterGqlSchemaComponentsEvent; |
||||
15 | use nystudio107\retour\assetbundles\retour\RetourAsset; |
||||
16 | use nystudio107\retour\gql\interfaces\RetourInterface; |
||||
17 | use nystudio107\retour\gql\queries\RetourQuery; |
||||
18 | use nystudio107\retour\listeners\GetCraftQLSchema; |
||||
19 | use nystudio107\retour\models\Settings; |
||||
20 | use nystudio107\retour\services\Redirects; |
||||
21 | use nystudio107\retour\services\Statistics; |
||||
22 | use nystudio107\retour\variables\RetourVariable; |
||||
23 | use nystudio107\retour\widgets\RetourWidget; |
||||
24 | |||||
25 | use nystudio107\pluginmanifest\services\ManifestService; |
||||
26 | |||||
27 | use Craft; |
||||
28 | use craft\base\Element; |
||||
29 | use craft\base\Plugin; |
||||
30 | use craft\events\ElementEvent; |
||||
31 | use craft\events\ExceptionEvent; |
||||
32 | use craft\events\PluginEvent; |
||||
33 | use craft\events\RegisterCacheOptionsEvent; |
||||
34 | use craft\events\RegisterComponentTypesEvent; |
||||
35 | use craft\events\RegisterGqlQueriesEvent; |
||||
36 | use craft\events\RegisterGqlTypesEvent; |
||||
37 | use craft\events\RegisterUrlRulesEvent; |
||||
38 | use craft\events\RegisterUserPermissionsEvent; |
||||
39 | use craft\helpers\ElementHelper; |
||||
40 | use craft\helpers\UrlHelper; |
||||
41 | use craft\services\Elements; |
||||
42 | use craft\services\Dashboard; |
||||
43 | use craft\services\Gql; |
||||
44 | use craft\services\Plugins; |
||||
45 | use craft\services\UserPermissions; |
||||
46 | use craft\utilities\ClearCaches; |
||||
47 | use craft\web\ErrorHandler; |
||||
48 | use craft\web\twig\variables\CraftVariable; |
||||
49 | use craft\web\UrlManager; |
||||
50 | |||||
51 | use yii\base\Event; |
||||
52 | use yii\base\Exception; |
||||
53 | use yii\web\HttpException; |
||||
54 | |||||
55 | use markhuot\CraftQL\Builders\Schema; |
||||
0 ignored issues
–
show
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. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths
Loading history...
|
|||||
56 | use markhuot\CraftQL\CraftQL; |
||||
0 ignored issues
–
show
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. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths
Loading history...
|
|||||
57 | use markhuot\CraftQL\Events\AlterSchemaFields; |
||||
0 ignored issues
–
show
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. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths
Loading history...
|
|||||
58 | |||||
59 | /** @noinspection MissingPropertyAnnotationsInspection */ |
||||
0 ignored issues
–
show
|
|||||
60 | |||||
61 | /** |
||||
62 | * Class Retour |
||||
63 | * |
||||
64 | * @author nystudio107 |
||||
0 ignored issues
–
show
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
|
|||||
65 | * @package Retour |
||||
0 ignored issues
–
show
|
|||||
66 | * @since 3.0.0 |
||||
0 ignored issues
–
show
|
|||||
67 | * |
||||
68 | * @property Redirects $redirects |
||||
69 | * @property Statistics $statistics |
||||
70 | * @property ManifestService $manifest |
||||
71 | */ |
||||
0 ignored issues
–
show
|
|||||
72 | class Retour extends Plugin |
||||
73 | { |
||||
74 | // Constants |
||||
75 | // ========================================================================= |
||||
76 | |||||
77 | const DEVMODE_CACHE_DURATION = 30; |
||||
78 | |||||
79 | // Static Properties |
||||
80 | // ========================================================================= |
||||
81 | |||||
82 | /** |
||||
0 ignored issues
–
show
|
|||||
83 | * @var Retour |
||||
84 | */ |
||||
85 | public static $plugin; |
||||
86 | |||||
87 | /** |
||||
0 ignored issues
–
show
|
|||||
88 | * @var Settings |
||||
89 | */ |
||||
90 | public static $settings; |
||||
91 | |||||
92 | /** |
||||
0 ignored issues
–
show
|
|||||
93 | * @var int |
||||
94 | */ |
||||
95 | public static $cacheDuration; |
||||
96 | |||||
97 | /** |
||||
0 ignored issues
–
show
|
|||||
98 | * @var HttpException |
||||
99 | */ |
||||
100 | public static $currentException; |
||||
101 | |||||
102 | /** |
||||
0 ignored issues
–
show
|
|||||
103 | * @var bool |
||||
104 | */ |
||||
105 | public static $craft31 = false; |
||||
106 | |||||
107 | /** |
||||
0 ignored issues
–
show
|
|||||
108 | * @var bool |
||||
109 | */ |
||||
110 | public static $craft32 = false; |
||||
111 | |||||
112 | /** |
||||
0 ignored issues
–
show
|
|||||
113 | * @var bool |
||||
114 | */ |
||||
115 | public static $craft33 = false; |
||||
116 | |||||
117 | // Static Methods |
||||
118 | // ========================================================================= |
||||
119 | |||||
120 | /** |
||||
0 ignored issues
–
show
|
|||||
121 | * @inheritdoc |
||||
122 | */ |
||||
123 | public function __construct($id, $parent = null, array $config = []) |
||||
124 | { |
||||
125 | $config['components'] = [ |
||||
126 | 'redirects' => Redirects::class, |
||||
127 | 'statistics' => Statistics::class, |
||||
128 | // Register the manifest service |
||||
129 | 'manifest' => [ |
||||
130 | 'class' => ManifestService::class, |
||||
131 | 'assetClass' => RetourAsset::class, |
||||
132 | 'devServerManifestPath' => 'http://craft-retour-buildchain:8080/', |
||||
133 | 'devServerPublicPath' => 'http://craft-retour-buildchain:8080/', |
||||
134 | ] |
||||
135 | ]; |
||||
136 | |||||
137 | parent::__construct($id, $parent, $config); |
||||
138 | } |
||||
139 | |||||
140 | // Public Properties |
||||
141 | // ========================================================================= |
||||
142 | |||||
143 | /** |
||||
0 ignored issues
–
show
|
|||||
144 | * @var string |
||||
145 | */ |
||||
146 | public $schemaVersion = '3.0.10'; |
||||
147 | |||||
148 | /** |
||||
0 ignored issues
–
show
|
|||||
149 | * @var bool |
||||
150 | */ |
||||
151 | public $hasCpSection = true; |
||||
152 | |||||
153 | /** |
||||
0 ignored issues
–
show
|
|||||
154 | * @var bool |
||||
155 | */ |
||||
156 | public $hasCpSettings = true; |
||||
157 | |||||
158 | /** |
||||
0 ignored issues
–
show
|
|||||
159 | * @var array The URIs for the element before it was saved |
||||
160 | */ |
||||
161 | public $oldElementUris = []; |
||||
162 | |||||
163 | // Public Methods |
||||
164 | // ========================================================================= |
||||
165 | |||||
166 | /** |
||||
0 ignored issues
–
show
|
|||||
167 | * @inheritdoc |
||||
168 | */ |
||||
0 ignored issues
–
show
|
|||||
169 | public function init() |
||||
170 | { |
||||
171 | parent::init(); |
||||
172 | self::$plugin = $this; |
||||
173 | // Initialize properties |
||||
174 | self::$settings = $this->getSettings(); |
||||
175 | self::$craft31 = version_compare(Craft::$app->getVersion(), '3.1', '>='); |
||||
0 ignored issues
–
show
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 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...
|
|||||
176 | self::$craft32 = version_compare(Craft::$app->getVersion(), '3.2', '>='); |
||||
0 ignored issues
–
show
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 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...
|
|||||
177 | self::$craft33 = version_compare(Craft::$app->getVersion(), '3.3', '>='); |
||||
0 ignored issues
–
show
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 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...
|
|||||
178 | $this->name = self::$settings->pluginName; |
||||
179 | self::$cacheDuration = Craft::$app->getConfig()->getGeneral()->devMode |
||||
180 | ? $this::DEVMODE_CACHE_DURATION |
||||
181 | : null; |
||||
182 | // Handle any console commands |
||||
183 | $request = Craft::$app->getRequest(); |
||||
184 | if ($request->getIsConsoleRequest()) { |
||||
185 | $this->controllerNamespace = 'nystudio107\retour\console\controllers'; |
||||
186 | } |
||||
187 | // Install our event listeners |
||||
188 | $this->installEventListeners(); |
||||
189 | // Log that Retour has been loaded |
||||
190 | Craft::info( |
||||
191 | Craft::t( |
||||
192 | 'retour', |
||||
193 | '{name} plugin loaded', |
||||
194 | ['name' => $this->name] |
||||
195 | ), |
||||
196 | __METHOD__ |
||||
197 | ); |
||||
198 | } |
||||
199 | |||||
200 | /** |
||||
0 ignored issues
–
show
|
|||||
201 | * @inheritdoc |
||||
202 | */ |
||||
0 ignored issues
–
show
|
|||||
203 | public function getSettingsResponse() |
||||
204 | { |
||||
205 | // Just redirect to the plugin settings page |
||||
206 | Craft::$app->getResponse()->redirect(UrlHelper::cpUrl('retour/settings')); |
||||
207 | } |
||||
208 | |||||
209 | /** |
||||
0 ignored issues
–
show
|
|||||
210 | * @inheritdoc |
||||
211 | */ |
||||
0 ignored issues
–
show
|
|||||
212 | public function getCpNavItem() |
||||
213 | { |
||||
214 | $subNavs = []; |
||||
215 | $navItem = parent::getCpNavItem(); |
||||
216 | $currentUser = Craft::$app->getUser()->getIdentity(); |
||||
217 | // Only show sub-navs the user has permission to view |
||||
218 | 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
Loading history...
|
|||||
219 | $subNavs['dashboard'] = [ |
||||
220 | 'label' => 'Dashboard', |
||||
221 | 'url' => 'retour/dashboard', |
||||
222 | ]; |
||||
223 | } |
||||
224 | if ($currentUser->can('retour:redirects')) { |
||||
225 | $subNavs['redirects'] = [ |
||||
226 | 'label' => 'Redirects', |
||||
227 | 'url' => 'retour/redirects', |
||||
228 | ]; |
||||
229 | } |
||||
230 | $editableSettings = true; |
||||
231 | $general = Craft::$app->getConfig()->getGeneral(); |
||||
232 | if (self::$craft31 && !$general->allowAdminChanges) { |
||||
233 | $editableSettings = false; |
||||
234 | } |
||||
235 | if ($currentUser->can('retour:settings') && $editableSettings) { |
||||
236 | $subNavs['settings'] = [ |
||||
237 | 'label' => 'Settings', |
||||
238 | 'url' => 'retour/settings', |
||||
239 | ]; |
||||
240 | } |
||||
241 | // 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 |
||||
242 | if (empty($subNavs)) { |
||||
243 | return null; |
||||
244 | } |
||||
245 | // A single sub nav item is redundant |
||||
246 | if (count($subNavs) === 1) { |
||||
247 | $subNavs = []; |
||||
248 | } |
||||
249 | $navItem = array_merge($navItem, [ |
||||
0 ignored issues
–
show
|
|||||
250 | 'subnav' => $subNavs, |
||||
251 | ]); |
||||
0 ignored issues
–
show
For multi-line function calls, the closing parenthesis should be on a new line.
If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line: someFunctionCall(
$firstArgument,
$secondArgument,
$thirdArgument
); // Closing parenthesis on a new line.
Loading history...
|
|||||
252 | |||||
253 | return $navItem; |
||||
254 | } |
||||
255 | |||||
256 | /** |
||||
257 | * Clear all the caches! |
||||
258 | */ |
||||
0 ignored issues
–
show
|
|||||
259 | public function clearAllCaches() |
||||
260 | { |
||||
261 | // Clear all of Retour's caches |
||||
262 | self::$plugin->redirects->invalidateCaches(); |
||||
263 | } |
||||
264 | |||||
265 | // Protected Methods |
||||
266 | // ========================================================================= |
||||
267 | |||||
268 | /** |
||||
269 | * Determine whether our table schema exists or not; this is needed because |
||||
270 | * migrations such as the install migration and base_install migration may |
||||
271 | * not have been run by the time our init() method has been called |
||||
272 | * |
||||
273 | * @return bool |
||||
274 | */ |
||||
275 | protected function tableSchemaExists(): bool |
||||
276 | { |
||||
277 | return (Craft::$app->db->schema->getTableSchema('{{%retour_redirects}}') !== null); |
||||
278 | } |
||||
279 | |||||
280 | /** |
||||
281 | * Install our event listeners. |
||||
282 | */ |
||||
0 ignored issues
–
show
|
|||||
283 | protected function installEventListeners() |
||||
284 | { |
||||
285 | // Install our event listeners only if our table schema exists |
||||
286 | if ($this->tableSchemaExists()) { |
||||
287 | $request = Craft::$app->getRequest(); |
||||
288 | // Add in our event listeners that are needed for every request |
||||
289 | $this->installGlobalEventListeners(); |
||||
290 | // Install only for non-console site requests |
||||
291 | if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()) { |
||||
292 | $this->installSiteEventListeners(); |
||||
293 | } |
||||
294 | // Install only for non-console Control Panel requests |
||||
295 | if ($request->getIsCpRequest() && !$request->getIsConsoleRequest()) { |
||||
296 | $this->installCpEventListeners(); |
||||
297 | } |
||||
298 | } |
||||
299 | // Handler: ClearCaches::EVENT_REGISTER_CACHE_OPTIONS |
||||
300 | Event::on( |
||||
301 | ClearCaches::class, |
||||
302 | ClearCaches::EVENT_REGISTER_CACHE_OPTIONS, |
||||
303 | function (RegisterCacheOptionsEvent $event) { |
||||
304 | Craft::debug( |
||||
305 | 'ClearCaches::EVENT_REGISTER_CACHE_OPTIONS', |
||||
306 | __METHOD__ |
||||
307 | ); |
||||
308 | // Register our Control Panel routes |
||||
309 | $event->options = array_merge( |
||||
310 | $event->options, |
||||
311 | $this->customAdminCpCacheOptions() |
||||
312 | ); |
||||
313 | } |
||||
314 | ); |
||||
315 | // Handler: EVENT_AFTER_INSTALL_PLUGIN |
||||
316 | Event::on( |
||||
317 | Plugins::class, |
||||
318 | Plugins::EVENT_AFTER_INSTALL_PLUGIN, |
||||
319 | function (PluginEvent $event) { |
||||
320 | if ($event->plugin === $this) { |
||||
321 | // Invalidate our caches after we've been installed |
||||
322 | $this->clearAllCaches(); |
||||
323 | // Send them to our welcome screen |
||||
324 | $request = Craft::$app->getRequest(); |
||||
325 | if ($request->isCpRequest) { |
||||
326 | Craft::$app->getResponse()->redirect(UrlHelper::cpUrl( |
||||
0 ignored issues
–
show
|
|||||
327 | 'retour/dashboard', |
||||
328 | [ |
||||
329 | 'showWelcome' => true, |
||||
330 | ] |
||||
331 | ))->send(); |
||||
0 ignored issues
–
show
For multi-line function calls, the closing parenthesis should be on a new line.
If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line: someFunctionCall(
$firstArgument,
$secondArgument,
$thirdArgument
); // Closing parenthesis on a new line.
Loading history...
|
|||||
332 | } |
||||
333 | } |
||||
334 | } |
||||
335 | ); |
||||
336 | } |
||||
337 | |||||
338 | /** |
||||
339 | * Install global event listeners for all request types |
||||
340 | */ |
||||
0 ignored issues
–
show
|
|||||
341 | protected function installGlobalEventListeners() |
||||
342 | { |
||||
343 | Event::on( |
||||
344 | CraftVariable::class, |
||||
345 | CraftVariable::EVENT_INIT, |
||||
346 | function (Event $event) { |
||||
347 | /** @var CraftVariable $variable */ |
||||
0 ignored issues
–
show
|
|||||
348 | $variable = $event->sender; |
||||
349 | $variable->set('retour', [ |
||||
0 ignored issues
–
show
|
|||||
350 | 'class' => RetourVariable::class, |
||||
351 | 'manifestService' => $this->manifest, |
||||
352 | ]); |
||||
0 ignored issues
–
show
For multi-line function calls, the closing parenthesis should be on a new line.
If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line: someFunctionCall(
$firstArgument,
$secondArgument,
$thirdArgument
); // Closing parenthesis on a new line.
Loading history...
|
|||||
353 | } |
||||
354 | ); |
||||
355 | // Handler: Elements::EVENT_BEFORE_SAVE_ELEMENT |
||||
356 | Event::on( |
||||
357 | Elements::class, |
||||
358 | Elements::EVENT_BEFORE_SAVE_ELEMENT, |
||||
359 | function (ElementEvent $event) { |
||||
360 | Craft::debug( |
||||
361 | 'Elements::EVENT_BEFORE_SAVE_ELEMENT', |
||||
362 | __METHOD__ |
||||
363 | ); |
||||
364 | /** @var Element $element */ |
||||
0 ignored issues
–
show
|
|||||
365 | $element = $event->element; |
||||
366 | if ($element !== null && !$event->isNew && $element->getUrl() !== null && !$element->propagating) { |
||||
367 | $checkElementSlug = true; |
||||
368 | // If we're running Craft 3.2 or later, also check that isn't not a draft or revision |
||||
369 | if (Retour::$craft32 && ( |
||||
370 | ElementHelper::isDraftOrRevision($element) |
||||
0 ignored issues
–
show
|
|||||
371 | )) { |
||||
0 ignored issues
–
show
|
|||||
372 | $checkElementSlug = false; |
||||
373 | } |
||||
374 | // Only do this for elements that aren't new, pass $checkElementSlug, and the user |
||||
375 | // has turned on the setting |
||||
376 | if (self::$settings->createUriChangeRedirects && $checkElementSlug) { |
||||
377 | // Make sure this isn't a transitioning temporary draft/revision and that it's |
||||
378 | // not propagating to other sites |
||||
379 | if (strpos($element->uri, '__temp_') === false && !$element->propagating) { |
||||
0 ignored issues
–
show
It seems like
$element->uri can also be of type null ; however, parameter $haystack of strpos() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
380 | // Stash the old URLs by element id, and do so only once, |
||||
381 | // in case we are called more than once per request |
||||
382 | if (empty($this->oldElementUris[$element->id])) { |
||||
383 | $this->oldElementUris[$element->id] = $this->getAllElementUris($element); |
||||
384 | } |
||||
385 | } |
||||
386 | } |
||||
387 | } |
||||
388 | } |
||||
389 | ); |
||||
390 | // Handler: Elements::EVENT_AFTER_SAVE_ELEMENT |
||||
391 | Event::on( |
||||
392 | Elements::class, |
||||
393 | Elements::EVENT_AFTER_SAVE_ELEMENT, |
||||
394 | function (ElementEvent $event) { |
||||
395 | Craft::debug( |
||||
396 | 'Elements::EVENT_AFTER_SAVE_ELEMENT', |
||||
397 | __METHOD__ |
||||
398 | ); |
||||
399 | /** @var Element $element */ |
||||
0 ignored issues
–
show
|
|||||
400 | $element = $event->element; |
||||
401 | if ($element !== null && !$event->isNew && $element->getUrl() !== null) { |
||||
402 | $checkElementSlug = true; |
||||
403 | if (Retour::$craft32 && ElementHelper::isDraftOrRevision($element)) { |
||||
404 | $checkElementSlug = false; |
||||
405 | } |
||||
406 | if (self::$settings->createUriChangeRedirects && $checkElementSlug) { |
||||
407 | $this->handleElementUriChange($element); |
||||
408 | } |
||||
409 | } |
||||
410 | } |
||||
411 | ); |
||||
412 | // Handler: Plugins::EVENT_AFTER_LOAD_PLUGINS |
||||
413 | Event::on( |
||||
414 | Plugins::class, |
||||
415 | Plugins::EVENT_AFTER_LOAD_PLUGINS, |
||||
416 | function () { |
||||
417 | // Install these only after all other plugins have loaded |
||||
418 | $request = Craft::$app->getRequest(); |
||||
419 | // Only respond to non-console site requests |
||||
420 | if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()) { |
||||
421 | $this->handleSiteRequest(); |
||||
422 | } |
||||
423 | // Respond to Control Panel requests |
||||
424 | if ($request->getIsCpRequest() && !$request->getIsConsoleRequest()) { |
||||
425 | $this->handleAdminCpRequest(); |
||||
426 | } |
||||
427 | } |
||||
428 | ); |
||||
429 | if (self::$craft33) { |
||||
430 | // Handler: Gql::EVENT_REGISTER_GQL_TYPES |
||||
431 | Event::on( |
||||
432 | Gql::class, |
||||
433 | Gql::EVENT_REGISTER_GQL_TYPES, |
||||
434 | function (RegisterGqlTypesEvent $event) { |
||||
435 | Craft::debug( |
||||
436 | 'Gql::EVENT_REGISTER_GQL_TYPES', |
||||
437 | __METHOD__ |
||||
438 | ); |
||||
439 | $event->types[] = RetourInterface::class; |
||||
440 | } |
||||
441 | ); |
||||
442 | // Handler: Gql::EVENT_REGISTER_GQL_QUERIES |
||||
443 | Event::on( |
||||
444 | Gql::class, |
||||
445 | Gql::EVENT_REGISTER_GQL_QUERIES, |
||||
446 | function (RegisterGqlQueriesEvent $event) { |
||||
447 | Craft::debug( |
||||
448 | 'Gql::EVENT_REGISTER_GQL_QUERIES', |
||||
449 | __METHOD__ |
||||
450 | ); |
||||
451 | $queries = RetourQuery::getQueries(); |
||||
452 | foreach ($queries as $key => $value) { |
||||
453 | $event->queries[$key] = $value; |
||||
454 | } |
||||
455 | } |
||||
456 | ); |
||||
457 | // Handler: Gql::EVENT_REGISTER_SCHEMA_COMPONENTS |
||||
458 | Event::on( |
||||
459 | Gql::class, |
||||
460 | Gql::EVENT_REGISTER_GQL_SCHEMA_COMPONENTS, |
||||
461 | function (RegisterGqlSchemaComponentsEvent $event) { |
||||
462 | Craft::debug( |
||||
463 | 'Gql::EVENT_REGISTER_GQL_SCHEMA_COMPONENTS', |
||||
464 | __METHOD__ |
||||
465 | ); |
||||
466 | $label = Craft::t('retour', 'Retour'); |
||||
467 | $event->queries[$label]['retour.all:read'] = ['label' => Craft::t('retour', 'Query Retour data')]; |
||||
468 | } |
||||
469 | ); |
||||
470 | } |
||||
471 | // CraftQL Support |
||||
472 | if (class_exists(CraftQL::class)) { |
||||
473 | Event::on( |
||||
474 | Schema::class, |
||||
475 | AlterSchemaFields::EVENT, |
||||
476 | [GetCraftQLSchema::class, 'handle'] |
||||
477 | ); |
||||
478 | } |
||||
479 | } |
||||
480 | |||||
481 | /** |
||||
482 | * Install site event listeners for site requests only |
||||
483 | */ |
||||
0 ignored issues
–
show
|
|||||
484 | protected function installSiteEventListeners() |
||||
485 | { |
||||
486 | // Handler: UrlManager::EVENT_REGISTER_SITE_URL_RULES |
||||
487 | Event::on( |
||||
488 | UrlManager::class, |
||||
489 | UrlManager::EVENT_REGISTER_SITE_URL_RULES, |
||||
490 | function (RegisterUrlRulesEvent $event) { |
||||
491 | Craft::debug( |
||||
492 | 'UrlManager::EVENT_REGISTER_SITE_URL_RULES', |
||||
493 | __METHOD__ |
||||
494 | ); |
||||
495 | // Register our Control Panel routes |
||||
496 | $event->rules = array_merge( |
||||
497 | $event->rules, |
||||
498 | $this->customFrontendRoutes() |
||||
499 | ); |
||||
500 | } |
||||
501 | ); |
||||
502 | } |
||||
503 | |||||
504 | /** |
||||
505 | * Install site event listeners for Control Panel requests only |
||||
506 | */ |
||||
0 ignored issues
–
show
|
|||||
507 | protected function installCpEventListeners() |
||||
508 | { |
||||
509 | // Handler: Dashboard::EVENT_REGISTER_WIDGET_TYPES |
||||
510 | Event::on( |
||||
511 | Dashboard::class, |
||||
512 | Dashboard::EVENT_REGISTER_WIDGET_TYPES, |
||||
513 | function (RegisterComponentTypesEvent $event) { |
||||
514 | $event->types[] = RetourWidget::class; |
||||
515 | } |
||||
516 | ); |
||||
517 | // Handler: UrlManager::EVENT_REGISTER_CP_URL_RULES |
||||
518 | Event::on( |
||||
519 | UrlManager::class, |
||||
520 | UrlManager::EVENT_REGISTER_CP_URL_RULES, |
||||
521 | function (RegisterUrlRulesEvent $event) { |
||||
522 | Craft::debug( |
||||
523 | 'UrlManager::EVENT_REGISTER_CP_URL_RULES', |
||||
524 | __METHOD__ |
||||
525 | ); |
||||
526 | // Register our Control Panel routes |
||||
527 | $event->rules = array_merge( |
||||
528 | $event->rules, |
||||
529 | $this->customAdminCpRoutes() |
||||
530 | ); |
||||
531 | } |
||||
532 | ); |
||||
533 | // Handler: UserPermissions::EVENT_REGISTER_PERMISSIONS |
||||
534 | Event::on( |
||||
535 | UserPermissions::class, |
||||
536 | UserPermissions::EVENT_REGISTER_PERMISSIONS, |
||||
537 | function (RegisterUserPermissionsEvent $event) { |
||||
538 | Craft::debug( |
||||
539 | 'UserPermissions::EVENT_REGISTER_PERMISSIONS', |
||||
540 | __METHOD__ |
||||
541 | ); |
||||
542 | // Register our custom permissions |
||||
543 | $event->permissions[Craft::t('retour', 'Retour')] = $this->customAdminCpPermissions(); |
||||
544 | } |
||||
545 | ); |
||||
546 | } |
||||
547 | |||||
548 | /** |
||||
549 | * Handle site requests. We do it only after we receive the event |
||||
550 | * EVENT_AFTER_LOAD_PLUGINS so that any pending db migrations can be run |
||||
551 | * before our event listeners kick in |
||||
552 | */ |
||||
0 ignored issues
–
show
|
|||||
553 | protected function handleSiteRequest() |
||||
554 | { |
||||
555 | // Handler: ErrorHandler::EVENT_BEFORE_HANDLE_EXCEPTION |
||||
556 | Event::on( |
||||
557 | ErrorHandler::class, |
||||
558 | ErrorHandler::EVENT_BEFORE_HANDLE_EXCEPTION, |
||||
559 | function (ExceptionEvent $event) { |
||||
560 | Craft::debug( |
||||
561 | 'ErrorHandler::EVENT_BEFORE_HANDLE_EXCEPTION', |
||||
562 | __METHOD__ |
||||
563 | ); |
||||
564 | $exception = $event->exception; |
||||
565 | // If this is a Twig Runtime exception, use the previous one instead |
||||
566 | if ($exception instanceof \Twig\Error\RuntimeError && |
||||
567 | ($previousException = $exception->getPrevious()) !== null) { |
||||
0 ignored issues
–
show
|
|||||
568 | $exception = $previousException; |
||||
569 | } |
||||
570 | // If this is a 404 error, see if we can handle it |
||||
571 | if ($exception instanceof HttpException && $exception->statusCode === 404) { |
||||
572 | self::$currentException = $exception; |
||||
573 | Retour::$plugin->redirects->handle404(); |
||||
574 | } |
||||
575 | } |
||||
576 | ); |
||||
577 | } |
||||
578 | |||||
579 | /** |
||||
580 | * Handle Control Panel requests. We do it only after we receive the event |
||||
581 | * EVENT_AFTER_LOAD_PLUGINS so that any pending db migrations can be run |
||||
582 | * before our event listeners kick in |
||||
583 | */ |
||||
0 ignored issues
–
show
|
|||||
584 | protected function handleAdminCpRequest() |
||||
585 | { |
||||
586 | } |
||||
587 | |||||
588 | /** |
||||
0 ignored issues
–
show
|
|||||
589 | * @inheritdoc |
||||
590 | */ |
||||
0 ignored issues
–
show
|
|||||
591 | protected function createSettingsModel() |
||||
592 | { |
||||
593 | return new Settings(); |
||||
594 | } |
||||
595 | |||||
596 | /** |
||||
0 ignored issues
–
show
|
|||||
597 | * @param Element $element |
||||
0 ignored issues
–
show
|
|||||
598 | */ |
||||
0 ignored issues
–
show
|
|||||
599 | protected function handleElementUriChange(Element $element) |
||||
600 | { |
||||
601 | $uris = $this->getAllElementUris($element); |
||||
602 | if (!empty($this->oldElementUris[$element->id])) { |
||||
603 | $oldElementUris = $this->oldElementUris[$element->id]; |
||||
604 | foreach ($uris as $siteId => $newUri) { |
||||
605 | if (!empty($oldElementUris[$siteId])) { |
||||
606 | $oldUri = $oldElementUris[$siteId]; |
||||
607 | Craft::debug( |
||||
608 | Craft::t( |
||||
609 | 'retour', |
||||
610 | 'Comparing old: {oldUri} to new: {newUri}', |
||||
611 | ['oldUri' => print_r($oldUri, true), 'newUri' => print_r($newUri, true)] |
||||
612 | ), |
||||
613 | __METHOD__ |
||||
614 | ); |
||||
615 | // Handle the siteId |
||||
616 | $redirectSiteId = null; |
||||
617 | if (Craft::$app->getIsMultiSite()) { |
||||
618 | $redirectSiteId = $siteId; |
||||
619 | } |
||||
620 | // Make sure the URIs are not the same |
||||
621 | if (strcmp($oldUri, $newUri) !== 0) { |
||||
622 | // Handle trailing slash config setting |
||||
623 | if (Craft::$app->config->general->addTrailingSlashesToUrls) { |
||||
624 | $oldUri = rtrim($oldUri, '/') . '/'; |
||||
625 | $newUri = rtrim($newUri, '/') . '/'; |
||||
626 | } |
||||
627 | // Handle the URL match type |
||||
628 | if (self::$settings->uriChangeRedirectSrcMatch === 'fullurl') { |
||||
629 | try { |
||||
630 | if ($redirectSiteId !== null) { |
||||
631 | $redirectSiteId = (int)$redirectSiteId; |
||||
632 | } |
||||
633 | $oldUri = UrlHelper::siteUrl($oldUri, null, null, $redirectSiteId); |
||||
634 | $newUri = UrlHelper::siteUrl($newUri, null, null, $redirectSiteId); |
||||
635 | } catch (Exception $e) { |
||||
636 | // That's fine |
||||
637 | } |
||||
638 | } |
||||
639 | $redirectConfig = [ |
||||
640 | 'id' => 0, |
||||
641 | 'redirectMatchType' => 'exactmatch', |
||||
642 | 'redirectHttpCode' => 301, |
||||
643 | 'redirectSrcMatch' => self::$settings->uriChangeRedirectSrcMatch, |
||||
644 | 'redirectSrcUrl' => $oldUri, |
||||
645 | 'redirectDestUrl' => $newUri, |
||||
646 | 'siteId' => $redirectSiteId, |
||||
647 | ]; |
||||
648 | Retour::$plugin->redirects->saveRedirect($redirectConfig); |
||||
649 | } |
||||
650 | } |
||||
651 | } |
||||
652 | } |
||||
653 | } |
||||
654 | |||||
655 | /** |
||||
656 | * Get the URIs for each site for the element |
||||
657 | * |
||||
658 | * @param Element $element |
||||
0 ignored issues
–
show
|
|||||
659 | * |
||||
660 | * @return array |
||||
661 | */ |
||||
662 | protected function getAllElementUris(Element $element): array |
||||
663 | { |
||||
664 | $uris = []; |
||||
665 | if (!self::$craft32 || !ElementHelper::isDraftOrRevision($element)) { |
||||
666 | $sites = Craft::$app->getSites()->getAllSites(); |
||||
667 | foreach ($sites as $site) { |
||||
668 | $uri = Craft::$app->getElements()->getElementUriForSite($element->id, $site->id); |
||||
669 | if ($uri !== null) { |
||||
670 | $uris[$site->id] = $uri; |
||||
671 | } |
||||
672 | } |
||||
673 | } |
||||
674 | |||||
675 | Craft::debug( |
||||
676 | Craft::t( |
||||
677 | 'retour', |
||||
678 | 'Getting Element URIs: {uris}', |
||||
679 | ['uris' => print_r($uris, true)] |
||||
680 | ), |
||||
681 | __METHOD__ |
||||
682 | ); |
||||
683 | |||||
684 | return $uris; |
||||
685 | } |
||||
686 | |||||
687 | /** |
||||
688 | * Return the custom Control Panel routes |
||||
689 | * |
||||
690 | * @return array |
||||
691 | */ |
||||
692 | protected function customAdminCpRoutes(): array |
||||
693 | { |
||||
694 | return [ |
||||
695 | 'retour' => '', |
||||
696 | |||||
697 | 'retour/redirects' => 'retour/redirects/redirects', |
||||
698 | 'retour/redirects/<siteHandle:{handle}>' => 'retour/redirects/redirects', |
||||
699 | |||||
700 | 'retour/edit-redirect/<redirectId:\d+>' => 'retour/redirects/edit-redirect', |
||||
701 | |||||
702 | 'retour/add-redirect' => 'retour/redirects/edit-redirect', |
||||
703 | 'retour/add-redirect/<siteId:\d+>' => 'retour/redirects/edit-redirect', |
||||
704 | |||||
705 | 'retour/dashboard' => 'retour/statistics/dashboard', |
||||
706 | 'retour/dashboard/<siteHandle:{handle}>' => 'retour/statistics/dashboard', |
||||
707 | |||||
708 | 'retour/settings' => 'retour/settings/plugin-settings', |
||||
709 | ]; |
||||
710 | } |
||||
711 | |||||
712 | /** |
||||
713 | * Return the custom frontend routes |
||||
714 | * |
||||
715 | * @return array |
||||
716 | */ |
||||
717 | protected function customFrontendRoutes(): array |
||||
718 | { |
||||
719 | return [ |
||||
720 | ]; |
||||
721 | } |
||||
722 | |||||
723 | /** |
||||
724 | * Returns the custom Control Panel cache options. |
||||
725 | * |
||||
726 | * @return array |
||||
727 | */ |
||||
728 | protected function customAdminCpCacheOptions(): array |
||||
729 | { |
||||
730 | return [ |
||||
731 | [ |
||||
732 | 'key' => 'retour-redirect-caches', |
||||
733 | 'label' => Craft::t('retour', 'Retour redirect caches'), |
||||
734 | 'action' => [self::$plugin->redirects, 'invalidateCaches'], |
||||
735 | ], |
||||
736 | ]; |
||||
737 | } |
||||
738 | |||||
739 | /** |
||||
740 | * Returns the custom Control Panel user permissions. |
||||
741 | * |
||||
742 | * @return array |
||||
743 | */ |
||||
744 | protected function customAdminCpPermissions(): array |
||||
745 | { |
||||
746 | return [ |
||||
747 | 'retour:dashboard' => [ |
||||
748 | 'label' => Craft::t('retour', 'Dashboard'), |
||||
749 | ], |
||||
750 | 'retour:redirects' => [ |
||||
751 | 'label' => Craft::t('retour', 'Redirects'), |
||||
752 | ], |
||||
753 | 'retour:settings' => [ |
||||
754 | 'label' => Craft::t('retour', 'Settings'), |
||||
755 | ], |
||||
756 | ]; |
||||
757 | } |
||||
758 | } |
||||
759 |