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\services; |
||||||
13 | |||||||
14 | use nystudio107\retour\Retour; |
||||||
15 | use nystudio107\retour\events\RedirectEvent; |
||||||
16 | use nystudio107\retour\events\ResolveRedirectEvent; |
||||||
17 | use nystudio107\retour\events\RedirectResolvedEvent; |
||||||
18 | use nystudio107\retour\helpers\UrlHelper; |
||||||
19 | use nystudio107\retour\models\StaticRedirects as StaticRedirectsModel; |
||||||
20 | |||||||
21 | use Craft; |
||||||
22 | use craft\base\Component; |
||||||
23 | use craft\base\Plugin; |
||||||
24 | use craft\db\Query; |
||||||
25 | use craft\errors\SiteNotFoundException; |
||||||
26 | use craft\helpers\Db; |
||||||
27 | |||||||
28 | use yii\base\ExitException; |
||||||
29 | use yii\base\InvalidConfigException; |
||||||
30 | use yii\base\InvalidRouteException; |
||||||
31 | use yii\caching\TagDependency; |
||||||
32 | use yii\db\Exception; |
||||||
33 | |||||||
34 | /** @noinspection MissingPropertyAnnotationsInspection */ |
||||||
0 ignored issues
–
show
|
|||||||
35 | |||||||
36 | /** |
||||||
0 ignored issues
–
show
|
|||||||
37 | * @author nystudio107 |
||||||
0 ignored issues
–
show
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
|
|||||||
38 | * @package Retour |
||||||
0 ignored issues
–
show
|
|||||||
39 | * @since 3.0.0 |
||||||
0 ignored issues
–
show
|
|||||||
40 | */ |
||||||
0 ignored issues
–
show
|
|||||||
41 | class Redirects extends Component |
||||||
42 | { |
||||||
43 | // Constants |
||||||
44 | // ========================================================================= |
||||||
45 | |||||||
46 | const CACHE_KEY = 'retour_redirect_'; |
||||||
47 | |||||||
48 | const GLOBAL_REDIRECTS_CACHE_TAG = 'retour_redirects'; |
||||||
49 | |||||||
50 | const EVENT_REDIRECT_ID = 0; |
||||||
51 | |||||||
52 | /** |
||||||
0 ignored issues
–
show
|
|||||||
53 | * @event RedirectEvent The event that is triggered before the redirect is saved |
||||||
54 | * You may set [[RedirectEvent::isValid]] to `false` to prevent the redirect from getting saved. |
||||||
55 | * |
||||||
56 | * ```php |
||||||
57 | * use nystudio107\retour\services\Redirects; |
||||||
58 | * use nystudio107\retour\events\RedirectEvent; |
||||||
59 | * |
||||||
60 | * Event::on(Redirects::class, |
||||||
61 | * Redirects::EVENT_BEFORE_SAVE_REDIRECT, |
||||||
62 | * function(RedirectEvent $event) { |
||||||
63 | * // potentially set $event->isValid; |
||||||
64 | * } |
||||||
65 | * ); |
||||||
66 | * ``` |
||||||
67 | */ |
||||||
68 | const EVENT_BEFORE_SAVE_REDIRECT = 'beforeSaveRedirect'; |
||||||
69 | |||||||
70 | /** |
||||||
0 ignored issues
–
show
|
|||||||
71 | * @event RedirectEvent The event that is triggered after the redirect is saved |
||||||
72 | * |
||||||
73 | * ```php |
||||||
74 | * use nystudio107\retour\services\Redirects; |
||||||
75 | * use nystudio107\retour\events\RedirectEvent; |
||||||
76 | * |
||||||
77 | * Event::on(Redirects::class, |
||||||
78 | * Redirects::EVENT_AFTER_SAVE_REDIRECT, |
||||||
79 | * function(RedirectEvent $event) { |
||||||
80 | * // the redirect was saved |
||||||
81 | * } |
||||||
82 | * ); |
||||||
83 | * ``` |
||||||
84 | */ |
||||||
85 | const EVENT_AFTER_SAVE_REDIRECT = 'afterSaveRedirect'; |
||||||
86 | |||||||
87 | /** |
||||||
0 ignored issues
–
show
|
|||||||
88 | * @event ResolveRedirectEvent The event that is triggered before Retour has attempted |
||||||
89 | * to resolve redirects. You may set [[ResolveRedirectEvent::redirectDestUrl]] to |
||||||
90 | * to the URL that it should redirect to, or null if no redirect should happen |
||||||
91 | * |
||||||
92 | * ```php |
||||||
93 | * use nystudio107\retour\services\Redirects; |
||||||
94 | * use nystudio107\retour\events\ResolveRedirectEvent; |
||||||
95 | * |
||||||
96 | * Event::on(Redirects::class, |
||||||
97 | * Redirects::EVENT_AFTER_SAVE_REDIRECT, |
||||||
98 | * function(ResolveRedirectEvent $event) { |
||||||
99 | * // potentially set $event->redirectDestUrl; |
||||||
100 | * } |
||||||
101 | * ); |
||||||
102 | * ``` |
||||||
103 | */ |
||||||
104 | const EVENT_BEFORE_RESOLVE_REDIRECT = 'beforeResolveRedirect'; |
||||||
105 | |||||||
106 | /** |
||||||
0 ignored issues
–
show
|
|||||||
107 | * @event ResolveRedirectEvent The event that is triggered after Retour has attempted |
||||||
108 | * to resolve redirects. You may set [[ResolveRedirectEvent::redirectDestUrl]] to |
||||||
109 | * to the URL that it should redirect to, or null if no redirect should happen |
||||||
110 | * |
||||||
111 | * ```php |
||||||
112 | * use nystudio107\retour\services\Redirects; |
||||||
113 | * use nystudio107\retour\events\ResolveRedirectEvent; |
||||||
114 | * |
||||||
115 | * Event::on(Redirects::class, |
||||||
116 | * Redirects::EVENT_AFTER_RESOLVE_REDIRECT, |
||||||
117 | * function(ResolveRedirectEvent $event) { |
||||||
118 | * // potentially set $event->redirectDestUrl; |
||||||
119 | * } |
||||||
120 | * ); |
||||||
121 | * ``` |
||||||
122 | */ |
||||||
123 | const EVENT_AFTER_RESOLVE_REDIRECT = 'afterResolveRedirect'; |
||||||
124 | |||||||
125 | /** |
||||||
0 ignored issues
–
show
|
|||||||
126 | * @event RedirectResolvedEvent The event that is triggered once Retour has resolved |
||||||
127 | * a redirect. [[RedirectResolvedEvent::redirect]] will be set to the redirect |
||||||
128 | * that was resolved. You may set [[RedirectResolvedEvent::redirectDestUrl]] to |
||||||
129 | * to a different URL that it should redirect to, or leave it null if the |
||||||
130 | * redirect should happen as resolved. |
||||||
131 | * |
||||||
132 | * ```php |
||||||
133 | * use nystudio107\retour\services\Redirects; |
||||||
134 | * use nystudio107\retour\events\RedirectResolvedEvent; |
||||||
135 | * |
||||||
136 | * Event::on(Redirects::class, |
||||||
137 | * Redirects::EVENT_REDIRECT_RESOLVED, |
||||||
138 | * function(RedirectResolvedEvent $event) { |
||||||
139 | * // potentially set $event->redirectDestUrl; |
||||||
140 | * } |
||||||
141 | * ); |
||||||
142 | * ``` |
||||||
143 | */ |
||||||
144 | const EVENT_REDIRECT_RESOLVED = 'redirectResolved'; |
||||||
145 | |||||||
146 | // Protected Properties |
||||||
147 | // ========================================================================= |
||||||
148 | |||||||
149 | /** |
||||||
0 ignored issues
–
show
|
|||||||
150 | * @var null|array |
||||||
151 | */ |
||||||
152 | protected $cachedStaticRedirects; |
||||||
153 | |||||||
154 | // Public Methods |
||||||
155 | // ========================================================================= |
||||||
156 | |||||||
157 | /** |
||||||
158 | * Handle 404s by looking for redirects |
||||||
159 | */ |
||||||
0 ignored issues
–
show
|
|||||||
160 | public function handle404() |
||||||
161 | { |
||||||
162 | Craft::info( |
||||||
163 | Craft::t( |
||||||
164 | 'retour', |
||||||
165 | 'A 404 exception occurred' |
||||||
166 | ), |
||||||
167 | __METHOD__ |
||||||
168 | ); |
||||||
169 | $request = Craft::$app->getRequest(); |
||||||
170 | // We only want site requests that are not live preview or console requests |
||||||
171 | if ($request->getIsSiteRequest() && !$this->isPreview($request) && !$request->getIsConsoleRequest()) { |
||||||
172 | // See if we should redirect |
||||||
173 | try { |
||||||
174 | $fullUrl = urldecode($request->getAbsoluteUrl()); |
||||||
175 | $pathOnly = urldecode($request->getUrl()); |
||||||
176 | } catch (InvalidConfigException $e) { |
||||||
177 | Craft::error( |
||||||
178 | $e->getMessage(), |
||||||
179 | __METHOD__ |
||||||
180 | ); |
||||||
181 | $pathOnly = ''; |
||||||
182 | $fullUrl = ''; |
||||||
183 | } |
||||||
184 | // Strip the query string if `alwaysStripQueryString` is set |
||||||
185 | if (Retour::$settings->alwaysStripQueryString) { |
||||||
186 | $fullUrl = UrlHelper::stripQueryString($fullUrl); |
||||||
187 | $pathOnly = UrlHelper::stripQueryString($pathOnly); |
||||||
188 | } |
||||||
189 | // Stash the $pathOnly for use when incrementing the statistics |
||||||
190 | $originalPathOnly = $pathOnly; |
||||||
191 | Craft::info( |
||||||
192 | Craft::t( |
||||||
193 | 'retour', |
||||||
194 | '404 full URL: {fullUrl}, 404 path only: {pathOnly}', |
||||||
195 | ['fullUrl' => $fullUrl, 'pathOnly' => $pathOnly] |
||||||
196 | ), |
||||||
197 | __METHOD__ |
||||||
198 | ); |
||||||
199 | if (!$this->excludeUri($pathOnly)) { |
||||||
200 | // Redirect if we find a match, otherwise let Craft handle it |
||||||
201 | $redirect = $this->findRedirectMatch($fullUrl, $pathOnly); |
||||||
202 | if (!$this->doRedirect($fullUrl, $pathOnly, $redirect) && !Retour::$settings->alwaysStripQueryString) { |
||||||
203 | // Try it again without the query string |
||||||
204 | $fullUrl = UrlHelper::stripQueryString($fullUrl); |
||||||
205 | $pathOnly = UrlHelper::stripQueryString($pathOnly); |
||||||
206 | $redirect = $this->findRedirectMatch($fullUrl, $pathOnly); |
||||||
207 | $this->doRedirect($fullUrl, $pathOnly, $redirect); |
||||||
208 | } |
||||||
209 | // Increment the stats |
||||||
210 | Retour::$plugin->statistics->incrementStatistics($originalPathOnly, false); |
||||||
211 | } |
||||||
212 | } |
||||||
213 | } |
||||||
214 | |||||||
215 | /** |
||||||
216 | * Do the redirect |
||||||
217 | * |
||||||
218 | * @param string $fullUrl |
||||||
0 ignored issues
–
show
|
|||||||
219 | * @param string $pathOnly |
||||||
0 ignored issues
–
show
|
|||||||
220 | * @param null|array $redirect |
||||||
0 ignored issues
–
show
|
|||||||
221 | * |
||||||
222 | * @return bool false if not redirected |
||||||
223 | */ |
||||||
224 | public function doRedirect(string $fullUrl, string $pathOnly, $redirect): bool |
||||||
225 | { |
||||||
226 | $response = Craft::$app->getResponse(); |
||||||
227 | if ($redirect !== null) { |
||||||
228 | // Figure out what type of source matching was done |
||||||
229 | $redirectSrcMatch = $redirect['redirectSrcMatch'] ?? 'pathonly'; |
||||||
230 | switch ($redirectSrcMatch) { |
||||||
231 | case 'pathonly': |
||||||
0 ignored issues
–
show
|
|||||||
232 | $url = $pathOnly; |
||||||
233 | break; |
||||||
234 | case 'fullurl': |
||||||
0 ignored issues
–
show
|
|||||||
235 | $url = $fullUrl; |
||||||
236 | break; |
||||||
237 | default: |
||||||
0 ignored issues
–
show
|
|||||||
238 | $url = $pathOnly; |
||||||
239 | break; |
||||||
240 | } |
||||||
241 | $dest = $redirect['redirectDestUrl']; |
||||||
242 | // If this isn't a full URL, make it one based on the appropriate site |
||||||
243 | if (!UrlHelper::isFullUrl($dest)) { |
||||||
244 | try { |
||||||
245 | $siteId = $redirect['siteId'] ?? null; |
||||||
246 | if ($siteId !== null) { |
||||||
247 | $siteId = (int)$siteId; |
||||||
248 | } |
||||||
249 | $dest = UrlHelper::siteUrl($dest, null, null, $siteId); |
||||||
250 | } catch (\yii\base\Exception $e) { |
||||||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||||||
251 | } |
||||||
252 | } |
||||||
253 | if (Retour::$settings->preserveQueryString) { |
||||||
254 | $request = Craft::$app->getRequest(); |
||||||
255 | if (!empty($request->getQueryStringWithoutPath())) { |
||||||
256 | $dest .= '?' . $request->getQueryStringWithoutPath(); |
||||||
257 | } |
||||||
258 | } |
||||||
259 | $redirectMatchType = $redirect['redirectMatchType'] ?? 'notfound'; |
||||||
260 | // Parse reference tags for exact matches |
||||||
261 | if ($redirectMatchType === 'exactmatch') { |
||||||
262 | $dest = Craft::$app->elements->parseRefs($dest, $redirect['siteId'] ?? null); |
||||||
0 ignored issues
–
show
The method
parseRefs() 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.
Loading history...
|
|||||||
263 | } |
||||||
264 | $status = $redirect['redirectHttpCode']; |
||||||
265 | Craft::info( |
||||||
266 | Craft::t( |
||||||
267 | 'retour', |
||||||
268 | 'Redirecting {url} to {dest} with status {status}', |
||||||
269 | ['url' => $url, 'dest' => $dest, 'status' => $status] |
||||||
270 | ), |
||||||
271 | __METHOD__ |
||||||
272 | ); |
||||||
273 | // Increment the stats |
||||||
274 | Retour::$plugin->statistics->incrementStatistics($url, true); |
||||||
275 | // Handle a Retour return status > 400 to render the actual error template |
||||||
276 | if ($status >= 400) { |
||||||
277 | Retour::$currentException->statusCode = $status; |
||||||
278 | $errorHandler = Craft::$app->getErrorHandler(); |
||||||
279 | $errorHandler->exception = Retour::$currentException; |
||||||
280 | try { |
||||||
281 | $response = Craft::$app->runAction('templates/render-error'); |
||||||
282 | } catch (InvalidRouteException $e) { |
||||||
283 | Craft::error($e->getMessage(), __METHOD__); |
||||||
284 | } catch (\yii\console\Exception $e) { |
||||||
285 | Craft::error($e->getMessage(), __METHOD__); |
||||||
286 | } |
||||||
287 | } |
||||||
288 | // Sanitize the URL |
||||||
289 | $dest = UrlHelper::sanitizeUrl($dest); |
||||||
290 | // Add any additional headers (existing ones will be replaced) |
||||||
291 | if (!empty(Retour::$settings->additionalHeaders)) { |
||||||
292 | foreach (Retour::$settings->additionalHeaders as $additionalHeader) { |
||||||
293 | $response->headers->set($additionalHeader['name'], $additionalHeader['value']); |
||||||
294 | } |
||||||
295 | } |
||||||
296 | // Redirect the request away; |
||||||
297 | $response->redirect($dest, $status)->send(); |
||||||
298 | try { |
||||||
299 | Craft::$app->end(); |
||||||
300 | } catch (ExitException $e) { |
||||||
301 | Craft::error($e->getMessage(), __METHOD__); |
||||||
302 | } |
||||||
303 | } |
||||||
304 | |||||||
305 | return false; |
||||||
306 | } |
||||||
307 | |||||||
308 | /** |
||||||
0 ignored issues
–
show
|
|||||||
309 | * @param string $fullUrl |
||||||
0 ignored issues
–
show
|
|||||||
310 | * @param string $pathOnly |
||||||
0 ignored issues
–
show
|
|||||||
311 | * @param null $siteId |
||||||
0 ignored issues
–
show
|
|||||||
312 | * |
||||||
313 | * @return array|null |
||||||
314 | */ |
||||||
315 | public function findRedirectMatch(string $fullUrl, string $pathOnly, $siteId = null) |
||||||
316 | { |
||||||
317 | // Get the current site |
||||||
318 | if ($siteId === null) { |
||||||
0 ignored issues
–
show
|
|||||||
319 | $currentSite = Craft::$app->getSites()->currentSite; |
||||||
320 | if ($currentSite) { |
||||||
321 | $siteId = $currentSite->id; |
||||||
322 | } else { |
||||||
323 | $primarySite = Craft::$app->getSites()->primarySite; |
||||||
324 | if ($currentSite) { |
||||||
325 | $siteId = $primarySite->id; |
||||||
326 | } |
||||||
327 | } |
||||||
328 | } |
||||||
329 | // Try getting the full URL redirect from the cache |
||||||
330 | $redirect = $this->getRedirectFromCache($fullUrl, $siteId); |
||||||
331 | if ($redirect) { |
||||||
332 | $this->incrementRedirectHitCount($redirect); |
||||||
333 | $this->saveRedirectToCache($fullUrl, $redirect); |
||||||
334 | |||||||
335 | return $redirect; |
||||||
336 | } |
||||||
337 | // Try getting the path only redirect from the cache |
||||||
338 | $redirect = $this->getRedirectFromCache($pathOnly, $siteId); |
||||||
339 | if ($redirect) { |
||||||
340 | $this->incrementRedirectHitCount($redirect); |
||||||
341 | $this->saveRedirectToCache($pathOnly, $redirect); |
||||||
342 | |||||||
343 | return $redirect; |
||||||
344 | } |
||||||
345 | // Resolve static redirects |
||||||
346 | $redirects = $this->getAllStaticRedirects(null, $siteId); |
||||||
347 | $redirect = $this->resolveRedirect($fullUrl, $pathOnly, $redirects); |
||||||
348 | if ($redirect) { |
||||||
349 | return $redirect; |
||||||
350 | } |
||||||
351 | |||||||
352 | return null; |
||||||
353 | } |
||||||
354 | |||||||
355 | /** |
||||||
0 ignored issues
–
show
|
|||||||
356 | * @param $url |
||||||
0 ignored issues
–
show
|
|||||||
357 | * @param int|null $siteId |
||||||
0 ignored issues
–
show
|
|||||||
358 | * |
||||||
359 | * @return bool|array |
||||||
360 | */ |
||||||
361 | public function getRedirectFromCache($url, int $siteId = 0) |
||||||
362 | { |
||||||
363 | $cache = Craft::$app->getCache(); |
||||||
364 | $cacheKey = $this::CACHE_KEY.md5($url).$siteId; |
||||||
365 | $redirect = $cache->get($cacheKey); |
||||||
366 | Craft::info( |
||||||
367 | Craft::t( |
||||||
368 | 'retour', |
||||||
369 | 'Cached redirect hit for {url}', |
||||||
370 | ['url' => $url] |
||||||
371 | ), |
||||||
372 | __METHOD__ |
||||||
373 | ); |
||||||
374 | |||||||
375 | return $redirect; |
||||||
376 | } |
||||||
377 | |||||||
378 | /** |
||||||
0 ignored issues
–
show
|
|||||||
379 | * @param string $url |
||||||
0 ignored issues
–
show
|
|||||||
380 | * @param array $redirect |
||||||
0 ignored issues
–
show
|
|||||||
381 | */ |
||||||
0 ignored issues
–
show
|
|||||||
382 | public function saveRedirectToCache($url, $redirect) |
||||||
383 | { |
||||||
384 | $cache = Craft::$app->getCache(); |
||||||
385 | // Get the current site id |
||||||
386 | $sites = Craft::$app->getSites(); |
||||||
387 | try { |
||||||
388 | $siteId = $sites->getCurrentSite()->id; |
||||||
389 | } catch (SiteNotFoundException $e) { |
||||||
390 | $siteId = 1; |
||||||
391 | } |
||||||
392 | $cacheKey = $this::CACHE_KEY.md5($url).$siteId; |
||||||
393 | // Create the dependency tags |
||||||
394 | $dependency = new TagDependency([ |
||||||
0 ignored issues
–
show
|
|||||||
395 | 'tags' => [ |
||||||
396 | $this::GLOBAL_REDIRECTS_CACHE_TAG, |
||||||
397 | $this::GLOBAL_REDIRECTS_CACHE_TAG.$siteId, |
||||||
398 | ], |
||||||
399 | ]); |
||||||
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...
|
|||||||
400 | $cache->set($cacheKey, $redirect, Retour::$cacheDuration, $dependency); |
||||||
401 | Craft::info( |
||||||
402 | Craft::t( |
||||||
403 | 'retour', |
||||||
404 | 'Cached redirect saved for {url}', |
||||||
405 | ['url' => $url] |
||||||
406 | ), |
||||||
407 | __METHOD__ |
||||||
408 | ); |
||||||
409 | } |
||||||
410 | |||||||
411 | /** |
||||||
0 ignored issues
–
show
|
|||||||
412 | * @param string $fullUrl |
||||||
0 ignored issues
–
show
|
|||||||
413 | * @param string $pathOnly |
||||||
0 ignored issues
–
show
|
|||||||
414 | * @param array $redirects |
||||||
0 ignored issues
–
show
|
|||||||
415 | * |
||||||
416 | * @return array|null |
||||||
417 | */ |
||||||
418 | public function resolveRedirect(string $fullUrl, string $pathOnly, array $redirects) |
||||||
419 | { |
||||||
420 | $result = null; |
||||||
421 | // Throw the Redirects::EVENT_BEFORE_RESOLVE_REDIRECT event |
||||||
422 | $event = new ResolveRedirectEvent([ |
||||||
0 ignored issues
–
show
|
|||||||
423 | 'fullUrl' => $fullUrl, |
||||||
424 | 'pathOnly' => $pathOnly, |
||||||
425 | 'redirectDestUrl' => null, |
||||||
426 | 'redirectHttpCode' => 301, |
||||||
427 | ]); |
||||||
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...
|
|||||||
428 | $this->trigger(self::EVENT_BEFORE_RESOLVE_REDIRECT, $event); |
||||||
429 | if ($event->redirectDestUrl !== null) { |
||||||
430 | return $this->resolveEventRedirect($event); |
||||||
431 | } |
||||||
432 | // Iterate through the redirects |
||||||
433 | foreach ($redirects as $redirect) { |
||||||
434 | // Figure out what type of source matching to do |
||||||
435 | $redirectSrcMatch = $redirect['redirectSrcMatch'] ?? 'pathonly'; |
||||||
436 | $redirectEnabled = (bool)$redirect['enabled']; |
||||||
437 | if ($redirectEnabled === true) { |
||||||
438 | switch ($redirectSrcMatch) { |
||||||
439 | case 'pathonly': |
||||||
0 ignored issues
–
show
|
|||||||
440 | $url = $pathOnly; |
||||||
441 | break; |
||||||
442 | case 'fullurl': |
||||||
0 ignored issues
–
show
|
|||||||
443 | $url = $fullUrl; |
||||||
444 | break; |
||||||
445 | default: |
||||||
0 ignored issues
–
show
|
|||||||
446 | $url = $pathOnly; |
||||||
447 | break; |
||||||
448 | } |
||||||
449 | $redirectMatchType = $redirect['redirectMatchType'] ?? 'notfound'; |
||||||
450 | switch ($redirectMatchType) { |
||||||
451 | // Do a straight up match |
||||||
452 | case 'exactmatch': |
||||||
0 ignored issues
–
show
|
|||||||
453 | if (strcasecmp($redirect['redirectSrcUrlParsed'], $url) === 0) { |
||||||
0 ignored issues
–
show
|
|||||||
454 | $this->incrementRedirectHitCount($redirect); |
||||||
455 | $this->saveRedirectToCache($url, $redirect); |
||||||
456 | |||||||
457 | // Throw the Redirects::EVENT_REDIRECT_RESOLVED event |
||||||
458 | $event = new RedirectResolvedEvent([ |
||||||
0 ignored issues
–
show
|
|||||||
459 | 'fullUrl' => $fullUrl, |
||||||
460 | 'pathOnly' => $pathOnly, |
||||||
461 | 'redirectDestUrl' => null, |
||||||
462 | 'redirectHttpCode' => 301, |
||||||
463 | 'redirect' => $redirect, |
||||||
464 | ]); |
||||||
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...
|
|||||||
465 | $this->trigger(self::EVENT_REDIRECT_RESOLVED, $event); |
||||||
466 | if ($event->redirectDestUrl !== null) { |
||||||
0 ignored issues
–
show
|
|||||||
467 | return $this->resolveEventRedirect($event, $url, $redirect); |
||||||
468 | } |
||||||
0 ignored issues
–
show
|
|||||||
469 | |||||||
470 | return $redirect; |
||||||
471 | } |
||||||
0 ignored issues
–
show
|
|||||||
472 | break; |
||||||
473 | |||||||
474 | // Do a regex match |
||||||
475 | case 'regexmatch': |
||||||
0 ignored issues
–
show
|
|||||||
476 | $matchRegEx = '`'.$redirect['redirectSrcUrlParsed'].'`i'; |
||||||
477 | try { |
||||||
0 ignored issues
–
show
|
|||||||
478 | if (preg_match($matchRegEx, $url) === 1) { |
||||||
0 ignored issues
–
show
|
|||||||
479 | $this->incrementRedirectHitCount($redirect); |
||||||
480 | // If we're not associated with an EntryID, handle capture group replacement |
||||||
481 | if ((int)$redirect['associatedElementId'] === 0) { |
||||||
0 ignored issues
–
show
|
|||||||
482 | $redirect['redirectDestUrl'] = preg_replace( |
||||||
483 | $matchRegEx, |
||||||
484 | $redirect['redirectDestUrl'], |
||||||
485 | $url |
||||||
486 | ); |
||||||
487 | } |
||||||
0 ignored issues
–
show
|
|||||||
488 | $url = preg_replace('/([^:])(\/{2,})/', '$1/', $url); |
||||||
489 | $this->saveRedirectToCache($url, $redirect); |
||||||
490 | |||||||
491 | // Throw the Redirects::EVENT_REDIRECT_RESOLVED event |
||||||
492 | $event = new RedirectResolvedEvent([ |
||||||
0 ignored issues
–
show
|
|||||||
493 | 'fullUrl' => $fullUrl, |
||||||
494 | 'pathOnly' => $pathOnly, |
||||||
495 | 'redirectDestUrl' => null, |
||||||
496 | 'redirectHttpCode' => 301, |
||||||
497 | 'redirect' => $redirect, |
||||||
498 | ]); |
||||||
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...
|
|||||||
499 | $this->trigger(self::EVENT_REDIRECT_RESOLVED, $event); |
||||||
500 | if ($event->redirectDestUrl !== null) { |
||||||
0 ignored issues
–
show
|
|||||||
501 | return $this->resolveEventRedirect($event, $url, $redirect); |
||||||
502 | } |
||||||
0 ignored issues
–
show
|
|||||||
503 | |||||||
504 | return $redirect; |
||||||
505 | } |
||||||
0 ignored issues
–
show
|
|||||||
506 | } catch (\Exception $e) { |
||||||
0 ignored issues
–
show
|
|||||||
507 | // That's fine |
||||||
508 | Craft::error('Invalid Redirect Regex: '.$matchRegEx, __METHOD__); |
||||||
509 | } |
||||||
0 ignored issues
–
show
|
|||||||
510 | |||||||
511 | break; |
||||||
512 | |||||||
513 | // Otherwise try to look up a plugin's method by and call it for the match |
||||||
514 | default: |
||||||
0 ignored issues
–
show
|
|||||||
515 | $plugin = $redirectMatchType ? Craft::$app->getPlugins()->getPlugin($redirectMatchType) : null; |
||||||
516 | if ($plugin && method_exists($plugin, 'retourMatch')) { |
||||||
0 ignored issues
–
show
|
|||||||
517 | $args = [ |
||||||
518 | [ |
||||||
519 | 'redirect' => &$redirect, |
||||||
520 | ], |
||||||
521 | ]; |
||||||
522 | $result = \call_user_func_array([$plugin, 'retourMatch'], $args); |
||||||
523 | if ($result) { |
||||||
0 ignored issues
–
show
|
|||||||
524 | $this->incrementRedirectHitCount($redirect); |
||||||
525 | $this->saveRedirectToCache($url, $redirect); |
||||||
526 | |||||||
527 | // Throw the Redirects::EVENT_REDIRECT_RESOLVED event |
||||||
528 | $event = new RedirectResolvedEvent([ |
||||||
0 ignored issues
–
show
|
|||||||
529 | 'fullUrl' => $fullUrl, |
||||||
530 | 'pathOnly' => $pathOnly, |
||||||
531 | 'redirectDestUrl' => null, |
||||||
532 | 'redirectHttpCode' => 301, |
||||||
533 | 'redirect' => $redirect, |
||||||
534 | ]); |
||||||
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...
|
|||||||
535 | $this->trigger(self::EVENT_REDIRECT_RESOLVED, $event); |
||||||
536 | if ($event->redirectDestUrl !== null) { |
||||||
0 ignored issues
–
show
|
|||||||
537 | return $this->resolveEventRedirect($event, $url, $redirect); |
||||||
538 | } |
||||||
0 ignored issues
–
show
|
|||||||
539 | |||||||
540 | return $redirect; |
||||||
541 | } |
||||||
0 ignored issues
–
show
|
|||||||
542 | } |
||||||
0 ignored issues
–
show
|
|||||||
543 | break; |
||||||
544 | } |
||||||
545 | } |
||||||
546 | } |
||||||
547 | // Throw the Redirects::EVENT_AFTER_RESOLVE_REDIRECT event |
||||||
548 | $event = new ResolveRedirectEvent([ |
||||||
0 ignored issues
–
show
|
|||||||
549 | 'fullUrl' => $fullUrl, |
||||||
550 | 'pathOnly' => $pathOnly, |
||||||
551 | 'redirectDestUrl' => null, |
||||||
552 | 'redirectHttpCode' => 301, |
||||||
553 | ]); |
||||||
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...
|
|||||||
554 | $this->trigger(self::EVENT_AFTER_RESOLVE_REDIRECT, $event); |
||||||
555 | if ($event->redirectDestUrl !== null) { |
||||||
556 | return $this->resolveEventRedirect($event); |
||||||
557 | } |
||||||
558 | Craft::info( |
||||||
559 | Craft::t( |
||||||
560 | 'retour', |
||||||
561 | 'Not handled-> full URL: {fullUrl}, path only: {pathOnly}', |
||||||
562 | ['fullUrl' => $fullUrl, 'pathOnly' => $pathOnly] |
||||||
563 | ), |
||||||
564 | __METHOD__ |
||||||
565 | ); |
||||||
566 | |||||||
567 | return $result; |
||||||
568 | } |
||||||
569 | |||||||
570 | /** |
||||||
0 ignored issues
–
show
|
|||||||
571 | * @param ResolveRedirectEvent $event |
||||||
0 ignored issues
–
show
|
|||||||
572 | * |
||||||
573 | * @return null|array |
||||||
574 | */ |
||||||
575 | public function resolveEventRedirect(ResolveRedirectEvent $event, $url = null, $redirect = null) |
||||||
576 | { |
||||||
577 | $result = null; |
||||||
578 | |||||||
579 | if ($event->redirectDestUrl !== null) { |
||||||
580 | $resolvedRedirect = new StaticRedirectsModel([ |
||||||
0 ignored issues
–
show
|
|||||||
581 | 'id' => self::EVENT_REDIRECT_ID, |
||||||
582 | 'redirectDestUrl' => $event->redirectDestUrl, |
||||||
583 | 'redirectHttpCode' => $event->redirectHttpCode, |
||||||
584 | ]); |
||||||
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...
|
|||||||
585 | $result = $resolvedRedirect->toArray(); |
||||||
586 | |||||||
587 | if ($url !== null && $redirect !== null) { |
||||||
588 | // Save the modified redirect to the cache |
||||||
589 | $redirect['redirectDestUrl'] = $event->redirectDestUrl; |
||||||
590 | $redirect['redirectHttpCode'] = $event->redirectHttpCode; |
||||||
591 | $this->saveRedirectToCache($url, $redirect); |
||||||
592 | } |
||||||
593 | } |
||||||
594 | |||||||
595 | return $result; |
||||||
596 | } |
||||||
597 | |||||||
598 | /** |
||||||
599 | * Returns the list of matching schemes |
||||||
600 | * |
||||||
601 | * @return array |
||||||
0 ignored issues
–
show
|
|||||||
602 | */ |
||||||
603 | public function getMatchesList(): array |
||||||
604 | { |
||||||
605 | $result = [ |
||||||
606 | 'exactmatch' => Craft::t('retour', 'Exact Match'), |
||||||
607 | 'regexmatch' => Craft::t('retour', 'RegEx Match'), |
||||||
608 | ]; |
||||||
609 | |||||||
610 | // Add any plugins that offer the retourMatch() method |
||||||
611 | foreach (Craft::$app->getPlugins()->getAllPlugins() as $plugin) { |
||||||
612 | /** @var Plugin $plugin */ |
||||||
0 ignored issues
–
show
|
|||||||
613 | if (method_exists($plugin, 'retourMatch')) { |
||||||
614 | $result[$plugin->getHandle()] = $plugin->name.Craft::t('retour', ' Match'); |
||||||
615 | } |
||||||
616 | } |
||||||
617 | |||||||
618 | return $result; |
||||||
619 | } |
||||||
620 | |||||||
621 | /** |
||||||
0 ignored issues
–
show
|
|||||||
622 | * @param null|int $limit |
||||||
0 ignored issues
–
show
|
|||||||
623 | * @param int|null $siteId |
||||||
0 ignored issues
–
show
|
|||||||
624 | * |
||||||
625 | * @return array All of the statistics |
||||||
626 | */ |
||||||
627 | public function getAllStaticRedirects($limit = null, int $siteId = null): array |
||||||
628 | { |
||||||
629 | // Cache it in our class; no need to fetch it more than once |
||||||
630 | if ($this->cachedStaticRedirects !== null) { |
||||||
631 | return $this->cachedStaticRedirects; |
||||||
632 | } |
||||||
633 | // Query the db table |
||||||
634 | $query = (new Query()) |
||||||
635 | ->from(['{{%retour_static_redirects}}']) |
||||||
636 | ->orderBy('redirectMatchType ASC, redirectSrcMatch ASC, hitCount DESC'); |
||||||
637 | if ($siteId) { |
||||||
0 ignored issues
–
show
The expression
$siteId of type integer|null is loosely compared to true ; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
Loading history...
|
|||||||
638 | $query |
||||||
639 | ->where(['siteId' => $siteId]) |
||||||
640 | ->orWhere(['siteId' => null]); |
||||||
641 | } |
||||||
642 | if ($limit) { |
||||||
0 ignored issues
–
show
The expression
$limit of type integer|null is loosely compared to true ; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
Loading history...
|
|||||||
643 | $query->limit($limit); |
||||||
644 | } |
||||||
645 | $redirects = $query->all(); |
||||||
646 | // Cache for future accesses |
||||||
647 | $this->cachedStaticRedirects = $redirects; |
||||||
648 | |||||||
649 | return $redirects; |
||||||
650 | } |
||||||
651 | |||||||
652 | /** |
||||||
653 | * Return a redirect by id |
||||||
654 | * |
||||||
655 | * @param int $id |
||||||
0 ignored issues
–
show
|
|||||||
656 | * |
||||||
657 | * @return null|array The static redirect |
||||||
658 | */ |
||||||
659 | public function getRedirectById(int $id) |
||||||
660 | { |
||||||
661 | // Query the db table |
||||||
662 | $redirect = (new Query()) |
||||||
663 | ->from(['{{%retour_static_redirects}}']) |
||||||
664 | ->where(['id' => $id]) |
||||||
665 | ->one(); |
||||||
666 | |||||||
667 | return $redirect; |
||||||
668 | } |
||||||
669 | |||||||
670 | /** |
||||||
671 | * Return a redirect by redirectSrcUrl |
||||||
672 | * |
||||||
673 | * @param string $redirectSrcUrl |
||||||
0 ignored issues
–
show
|
|||||||
674 | * @param int|null $siteId |
||||||
0 ignored issues
–
show
|
|||||||
675 | * |
||||||
676 | * @return null|array |
||||||
677 | */ |
||||||
678 | public function getRedirectByRedirectSrcUrl(string $redirectSrcUrl, int $siteId = null) |
||||||
679 | { |
||||||
680 | // Query the db table |
||||||
681 | $query = (new Query()) |
||||||
682 | ->from(['{{%retour_static_redirects}}']) |
||||||
683 | ->where(['redirectSrcUrl' => $redirectSrcUrl]) |
||||||
0 ignored issues
–
show
|
|||||||
684 | ; |
||||||
685 | if ($siteId) { |
||||||
0 ignored issues
–
show
The expression
$siteId of type integer|null is loosely compared to true ; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
Loading history...
|
|||||||
686 | $query |
||||||
687 | ->andWhere(['or', [ |
||||||
0 ignored issues
–
show
|
|||||||
688 | 'siteId' => $siteId, |
||||||
689 | ], [ |
||||||
0 ignored issues
–
show
|
|||||||
690 | 'siteId' => null, |
||||||
691 | ]]); |
||||||
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...
|
|||||||
692 | } |
||||||
693 | $redirect = $query->one(); |
||||||
694 | |||||||
695 | return $redirect; |
||||||
696 | } |
||||||
697 | |||||||
698 | /** |
||||||
699 | * Delete a redirect by id |
||||||
700 | * |
||||||
701 | * @param int $id |
||||||
0 ignored issues
–
show
|
|||||||
702 | * |
||||||
703 | * @return int The result |
||||||
704 | */ |
||||||
705 | public function deleteRedirectById(int $id): int |
||||||
706 | { |
||||||
707 | $db = Craft::$app->getDb(); |
||||||
708 | // Delete a row from the db table |
||||||
709 | try { |
||||||
710 | $result = $db->createCommand()->delete( |
||||||
711 | '{{%retour_static_redirects}}', |
||||||
712 | [ |
||||||
713 | 'id' => $id, |
||||||
714 | ] |
||||||
715 | )->execute(); |
||||||
716 | } catch (Exception $e) { |
||||||
717 | Craft::error($e->getMessage(), __METHOD__); |
||||||
718 | $result = 0; |
||||||
719 | } |
||||||
720 | |||||||
721 | return $result; |
||||||
722 | } |
||||||
723 | |||||||
724 | /** |
||||||
725 | * Increment the retour_static_redirects record |
||||||
726 | * |
||||||
727 | * @param $redirectConfig |
||||||
0 ignored issues
–
show
|
|||||||
728 | */ |
||||||
0 ignored issues
–
show
|
|||||||
729 | public function incrementRedirectHitCount(&$redirectConfig) |
||||||
730 | { |
||||||
731 | if ($redirectConfig !== null) { |
||||||
732 | $db = Craft::$app->getDb(); |
||||||
733 | $redirectConfig['hitCount']++; |
||||||
734 | $redirectConfig['hitLastTime'] = Db::prepareDateForDb(new \DateTime()); |
||||||
735 | Craft::debug( |
||||||
736 | Craft::t( |
||||||
737 | 'retour', |
||||||
738 | 'Incrementing statistics for: {redirect}', |
||||||
739 | ['redirect' => print_r($redirectConfig, true)] |
||||||
740 | ), |
||||||
741 | __METHOD__ |
||||||
742 | ); |
||||||
743 | // Update the existing record |
||||||
744 | try { |
||||||
745 | $rowsAffected = $db->createCommand()->update( |
||||||
746 | '{{%retour_static_redirects}}', |
||||||
747 | [ |
||||||
748 | 'hitCount' => $redirectConfig['hitCount'], |
||||||
749 | 'hitLastTime' => $redirectConfig['hitLastTime'], |
||||||
750 | ], |
||||||
751 | [ |
||||||
752 | 'id' => $redirectConfig['id'], |
||||||
753 | ] |
||||||
754 | )->execute(); |
||||||
755 | Craft::debug('Rows affected: '.$rowsAffected, __METHOD__); |
||||||
756 | } catch (\Exception $e) { |
||||||
757 | Craft::error($e->getMessage(), __METHOD__); |
||||||
758 | } |
||||||
759 | } |
||||||
760 | } |
||||||
761 | |||||||
762 | /** |
||||||
0 ignored issues
–
show
|
|||||||
763 | * @param array $redirectConfig |
||||||
0 ignored issues
–
show
|
|||||||
764 | */ |
||||||
0 ignored issues
–
show
|
|||||||
765 | public function saveRedirect(array $redirectConfig) |
||||||
766 | { |
||||||
767 | // Handle URL encoded URLs by decoding them before saving them |
||||||
768 | if ($redirectConfig['redirectMatchType'] === 'exactmatch') { |
||||||
769 | $redirectConfig['redirectSrcUrl'] = urldecode($redirectConfig['redirectSrcUrl'] ?? ''); |
||||||
770 | $redirectConfig['redirectSrcUrlParsed'] = urldecode($redirectConfig['redirectSrcUrlParsed'] ?? ''); |
||||||
771 | } |
||||||
772 | // Validate the model before saving it to the db |
||||||
773 | $redirect = new StaticRedirectsModel($redirectConfig); |
||||||
774 | if ($redirect->validate() === false) { |
||||||
775 | Craft::error( |
||||||
776 | Craft::t( |
||||||
777 | 'retour', |
||||||
778 | 'Error validating redirect {id}: {errors}', |
||||||
779 | ['id' => $redirect->id, 'errors' => print_r($redirect->getErrors(), true)] |
||||||
780 | ), |
||||||
781 | __METHOD__ |
||||||
782 | ); |
||||||
783 | |||||||
784 | return; |
||||||
785 | } |
||||||
786 | // Get the validated model attributes and save them to the db |
||||||
787 | $redirectConfig = $redirect->getAttributes(); |
||||||
788 | // 0 for a siteId needs to be converted to null |
||||||
789 | if (empty($redirectConfig['siteId']) || (int)$redirectConfig['siteId'] === 0) { |
||||||
790 | $redirectConfig['siteId'] = null; |
||||||
791 | } |
||||||
792 | // Throw an event to before saving the redirect |
||||||
793 | $db = Craft::$app->getDb(); |
||||||
794 | // See if a redirect exists with this source URL already |
||||||
795 | if ((int)$redirectConfig['id'] === 0) { |
||||||
796 | // Query the db table |
||||||
797 | $redirect = (new Query()) |
||||||
798 | ->from(['{{%retour_static_redirects}}']) |
||||||
799 | ->where(['redirectSrcUrlParsed' => $redirectConfig['redirectSrcUrlParsed']]) |
||||||
800 | ->andWhere(['siteId' => $redirectConfig['siteId']]) |
||||||
801 | ->one(); |
||||||
802 | // If it exists, update it rather than having duplicates |
||||||
803 | if (!empty($redirect)) { |
||||||
804 | $redirectConfig['id'] = $redirect['id']; |
||||||
805 | } |
||||||
806 | } |
||||||
807 | // Trigger a 'beforeSaveRedirect' event |
||||||
808 | $isNew = (int)$redirectConfig['id'] === 0; |
||||||
809 | $event = new RedirectEvent([ |
||||||
0 ignored issues
–
show
|
|||||||
810 | 'isNew' => $isNew, |
||||||
811 | 'legacyUrl' => $redirectConfig['redirectSrcUrlParsed'], |
||||||
812 | 'destinationUrl' => $redirectConfig['redirectDestUrl'], |
||||||
813 | 'matchType' => $redirectConfig['redirectSrcMatch'], |
||||||
814 | 'redirectType' => $redirectConfig['redirectHttpCode'], |
||||||
815 | ]); |
||||||
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...
|
|||||||
816 | $this->trigger(self::EVENT_BEFORE_SAVE_REDIRECT, $event); |
||||||
817 | if (!$event->isValid) { |
||||||
818 | return; |
||||||
819 | } |
||||||
820 | // See if this is an existing redirect |
||||||
821 | if (!$isNew) { |
||||||
822 | Craft::debug( |
||||||
823 | Craft::t( |
||||||
824 | 'retour', |
||||||
825 | 'Updating existing redirect: {redirect}', |
||||||
826 | ['redirect' => print_r($redirectConfig, true)] |
||||||
827 | ), |
||||||
828 | __METHOD__ |
||||||
829 | ); |
||||||
830 | // Update the existing record |
||||||
831 | try { |
||||||
832 | $db->createCommand()->update( |
||||||
833 | '{{%retour_static_redirects}}', |
||||||
834 | $redirectConfig, |
||||||
835 | [ |
||||||
836 | 'id' => $redirectConfig['id'], |
||||||
837 | ] |
||||||
838 | )->execute(); |
||||||
839 | } catch (Exception $e) { |
||||||
840 | Craft::error($e->getMessage(), __METHOD__); |
||||||
841 | } |
||||||
842 | } else { |
||||||
843 | Craft::debug( |
||||||
844 | Craft::t( |
||||||
845 | 'retour', |
||||||
846 | 'Creating new redirect: {redirect}', |
||||||
847 | ['redirect' => print_r($redirectConfig, true)] |
||||||
848 | ), |
||||||
849 | __METHOD__ |
||||||
850 | ); |
||||||
851 | unset($redirectConfig['id']); |
||||||
852 | // Create a new record |
||||||
853 | try { |
||||||
854 | $db->createCommand()->insert( |
||||||
855 | '{{%retour_static_redirects}}', |
||||||
856 | $redirectConfig |
||||||
857 | )->execute(); |
||||||
858 | } catch (Exception $e) { |
||||||
859 | Craft::error($e->getMessage(), __METHOD__); |
||||||
860 | } |
||||||
861 | } |
||||||
862 | // To prevent redirect loops, see if any static redirects have our redirectDestUrl as their redirectSrcUrl |
||||||
863 | $testRedirectConfig = $this->getRedirectByRedirectSrcUrl( |
||||||
864 | $redirectConfig['redirectDestUrl'], |
||||||
865 | $redirectConfig['siteId'] |
||||||
866 | ); |
||||||
867 | if ($testRedirectConfig !== null) { |
||||||
868 | Craft::debug( |
||||||
869 | Craft::t( |
||||||
870 | 'retour', |
||||||
871 | 'Deleting redirect to prevent a loop: {redirect}', |
||||||
872 | ['redirect' => print_r($testRedirectConfig, true)] |
||||||
873 | ), |
||||||
874 | __METHOD__ |
||||||
875 | ); |
||||||
876 | // Delete the redirect that has a redirectSrcUrl the same as this record's redirectDestUrl |
||||||
877 | try { |
||||||
878 | $db->createCommand()->delete( |
||||||
879 | '{{%retour_static_redirects}}', |
||||||
880 | ['id' => $testRedirectConfig['id']] |
||||||
881 | )->execute(); |
||||||
882 | } catch (Exception $e) { |
||||||
883 | Craft::error($e->getMessage(), __METHOD__); |
||||||
884 | } |
||||||
885 | } |
||||||
886 | // Trigger a 'afterSaveRedirect' event |
||||||
887 | $this->trigger(self::EVENT_AFTER_SAVE_REDIRECT, $event); |
||||||
888 | |||||||
889 | // Invalidate caches after saving a redirect |
||||||
890 | $this->invalidateCaches(); |
||||||
891 | } |
||||||
892 | |||||||
893 | /** |
||||||
894 | * Invalidate all of the redirects caches |
||||||
895 | */ |
||||||
0 ignored issues
–
show
|
|||||||
896 | public function invalidateCaches() |
||||||
897 | { |
||||||
898 | $cache = Craft::$app->getCache(); |
||||||
899 | TagDependency::invalidate($cache, $this::GLOBAL_REDIRECTS_CACHE_TAG); |
||||||
900 | // If they are using Craft 3.3 or later, clear the GraphQL caches too |
||||||
901 | if (Retour::$craft33) { |
||||||
902 | $gql = Craft::$app->getGql(); |
||||||
903 | if (method_exists($gql, 'invalidateCaches')) { |
||||||
904 | $gql->invalidateCaches(); |
||||||
905 | } |
||||||
906 | } |
||||||
907 | Craft::info( |
||||||
908 | Craft::t( |
||||||
909 | 'retour', |
||||||
910 | 'All redirect caches cleared' |
||||||
911 | ), |
||||||
912 | __METHOD__ |
||||||
913 | ); |
||||||
914 | } |
||||||
915 | |||||||
916 | /** |
||||||
0 ignored issues
–
show
|
|||||||
917 | * @param $uri |
||||||
0 ignored issues
–
show
|
|||||||
918 | * |
||||||
919 | * @return bool |
||||||
920 | */ |
||||||
921 | public function excludeUri($uri): bool |
||||||
922 | { |
||||||
923 | $uri = '/'.ltrim($uri, '/'); |
||||||
924 | if (!empty(Retour::$settings->excludePatterns)) { |
||||||
925 | foreach (Retour::$settings->excludePatterns as $excludePattern) { |
||||||
926 | $pattern = '`'.$excludePattern['pattern'].'`i'; |
||||||
927 | try { |
||||||
928 | if (preg_match($pattern, $uri) === 1) { |
||||||
929 | return true; |
||||||
930 | } |
||||||
931 | } catch (\Exception $e) { |
||||||
932 | // That's fine |
||||||
933 | Craft::error('Invalid exclude URI Regex: '.$pattern, __METHOD__); |
||||||
934 | } |
||||||
935 | } |
||||||
936 | } |
||||||
937 | |||||||
938 | return false; |
||||||
939 | } |
||||||
940 | |||||||
941 | /** |
||||||
0 ignored issues
–
show
|
|||||||
942 | * Return whether this is a preview request of any kind |
||||||
943 | * |
||||||
944 | * @return bool |
||||||
945 | */ |
||||||
946 | public function isPreview($request): bool |
||||||
947 | { |
||||||
948 | $isPreview = false; |
||||||
949 | if (Retour::$craft32) { |
||||||
950 | $isPreview = $request->getIsPreview(); |
||||||
951 | } |
||||||
952 | $isLivePreview = $request->getIsLivePreview(); |
||||||
953 | |||||||
954 | return ($isPreview || $isLivePreview); |
||||||
955 | } |
||||||
956 | } |
||||||
957 |