|
10 | 10 |
|
11 | 11 | namespace Joomla\Plugin\System\Sef\Extension; |
12 | 12 |
|
| 13 | +use Joomla\CMS\Event\Router\AfterInitialiseRouterEvent; |
13 | 14 | use Joomla\CMS\Plugin\CMSPlugin; |
14 | 15 | use Joomla\CMS\Router\Route; |
| 16 | +use Joomla\CMS\Router\Router; |
| 17 | +use Joomla\CMS\Router\SiteRouter; |
15 | 18 | use Joomla\CMS\Uri\Uri; |
| 19 | +use Joomla\Event\SubscriberInterface; |
16 | 20 |
|
17 | 21 | // phpcs:disable PSR1.Files.SideEffects |
18 | 22 | \defined('_JEXEC') or die; |
|
23 | 27 | * |
24 | 28 | * @since 1.5 |
25 | 29 | */ |
26 | | -final class Sef extends CMSPlugin |
| 30 | +final class Sef extends CMSPlugin implements SubscriberInterface |
27 | 31 | { |
| 32 | + /** |
| 33 | + * Application object. |
| 34 | + * |
| 35 | + * @var \Joomla\CMS\Application\CMSApplication |
| 36 | + * @since __DEPLOY_VERSION__ |
| 37 | + */ |
| 38 | + protected $app; |
| 39 | + |
| 40 | + /** |
| 41 | + * Returns an array of CMS events this plugin will listen to and the respective handlers. |
| 42 | + * |
| 43 | + * @return array |
| 44 | + * |
| 45 | + * @since __DEPLOY_VERSION__ |
| 46 | + */ |
| 47 | + public static function getSubscribedEvents(): array |
| 48 | + { |
| 49 | + /** |
| 50 | + * Note that onAfterInitialise must be the first handlers to run for this |
| 51 | + * plugin to operate as expected. These handlers load compatibility code which |
| 52 | + * might be needed by other plugins |
| 53 | + */ |
| 54 | + return [ |
| 55 | + 'onAfterInitialiseRouter' => 'onAfterInitialiseRouter', |
| 56 | + 'onAfterDispatch' => 'onAfterDispatch', |
| 57 | + 'onAfterRender' => 'onAfterRender', |
| 58 | + ]; |
| 59 | + } |
| 60 | + |
| 61 | + /** |
| 62 | + * After initialise router. |
| 63 | + * |
| 64 | + * @return void |
| 65 | + * |
| 66 | + * @since __DEPLOY_VERSION__ |
| 67 | + */ |
| 68 | + public function onAfterInitialiseRouter(AfterInitialiseRouterEvent $event) |
| 69 | + { |
| 70 | + if ( |
| 71 | + !is_a($event->getRouter(), SiteRouter::class) |
| 72 | + || !$this->app->get('sef') |
| 73 | + || $this->app->get('sef_suffix') |
| 74 | + || !$this->params->get('trailingslash') |
| 75 | + ) { |
| 76 | + return; |
| 77 | + } |
| 78 | + |
| 79 | + if ($this->params->get('trailingslash') == 1) { |
| 80 | + // Remove trailingslash |
| 81 | + $event->getRouter()->attachBuildRule([$this, 'removeTrailingSlash'], SiteRouter::PROCESS_AFTER); |
| 82 | + } elseif ($this->params->get('trailingslash') == 2) { |
| 83 | + // Add trailingslash |
| 84 | + $event->getRouter()->attachBuildRule([$this, 'addTrailingSlash'], SiteRouter::PROCESS_AFTER); |
| 85 | + } |
| 86 | + |
| 87 | + $event->getRouter()->attachParseRule([$this, 'enforceTrailingSlash'], SiteRouter::PROCESS_BEFORE); |
| 88 | + } |
| 89 | + |
28 | 90 | /** |
29 | 91 | * Add the canonical uri to the head. |
30 | 92 | * |
@@ -188,6 +250,74 @@ function ($match) use ($base, $protocols) { |
188 | 250 | $this->getApplication()->setBody($buffer); |
189 | 251 | } |
190 | 252 |
|
| 253 | + /** |
| 254 | + * Remove any trailing slash from URLs built in Joomla |
| 255 | + * |
| 256 | + * @param Router &$router Router object. |
| 257 | + * @param Uri &$uri Uri object. |
| 258 | + * |
| 259 | + * @return void |
| 260 | + * |
| 261 | + * @since __DEPLOY_VERSION__ |
| 262 | + */ |
| 263 | + public function removeTrailingSlash(&$router, &$uri) |
| 264 | + { |
| 265 | + $path = $uri->getPath(); |
| 266 | + |
| 267 | + if (substr($path, -1) == '/') { |
| 268 | + $uri->setPath(substr($path, 0, -1)); |
| 269 | + } |
| 270 | + } |
| 271 | + |
| 272 | + /** |
| 273 | + * Add trailing slash to URLs built in Joomla |
| 274 | + * |
| 275 | + * @param Router &$router Router object. |
| 276 | + * @param Uri &$uri Uri object. |
| 277 | + * |
| 278 | + * @return void |
| 279 | + * |
| 280 | + * @since __DEPLOY_VERSION__ |
| 281 | + */ |
| 282 | + public function addTrailingSlash(&$router, &$uri) |
| 283 | + { |
| 284 | + $path = $uri->getPath(); |
| 285 | + |
| 286 | + if (substr($path, -1) !== '/') { |
| 287 | + $uri->setPath($path . '/'); |
| 288 | + } |
| 289 | + } |
| 290 | + |
| 291 | + /** |
| 292 | + * Redirect to a URL with or without trailing slash |
| 293 | + * |
| 294 | + * @param Router &$router Router object. |
| 295 | + * @param Uri &$uri Uri object. |
| 296 | + * |
| 297 | + * @return void |
| 298 | + * |
| 299 | + * @since __DEPLOY_VERSION__ |
| 300 | + */ |
| 301 | + public function enforceTrailingSlash(&$router, &$uri) |
| 302 | + { |
| 303 | + // We only want to redirect on GET requests |
| 304 | + if ($this->app->getInput()->getMethod() != 'GET') { |
| 305 | + return; |
| 306 | + } |
| 307 | + |
| 308 | + $originalUri = Uri::getInstance(); |
| 309 | + |
| 310 | + if ($this->params->get('trailingslash') == 1 && substr($originalUri->getPath(), -1) == '/' && $originalUri->toString() != Uri::root()) { |
| 311 | + // Remove trailingslash |
| 312 | + $originalUri->setPath(substr($originalUri->getPath(), 0, -1)); |
| 313 | + $this->app->redirect($originalUri->toString(), 301); |
| 314 | + } elseif ($this->params->get('trailingslash') == 2 && substr($originalUri->getPath(), -1) != '/') { |
| 315 | + // Add trailingslash |
| 316 | + $originalUri->setPath($originalUri->getPath() . '/'); |
| 317 | + $this->app->redirect($originalUri->toString(), 301); |
| 318 | + } |
| 319 | + } |
| 320 | + |
191 | 321 | /** |
192 | 322 | * Check the buffer. |
193 | 323 | * |
|
0 commit comments