Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.3] SEF: Fix URLs when preprocessing #43992

Merged
merged 12 commits into from
Oct 29, 2024
18 changes: 5 additions & 13 deletions components/com_contact/src/Service/Router.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Joomla\CMS\Component\Router\RouterViewConfiguration;
use Joomla\CMS\Component\Router\Rules\MenuRules;
use Joomla\CMS\Component\Router\Rules\NomenuRules;
use Joomla\CMS\Component\Router\Rules\PreprocessRules;
use Joomla\CMS\Component\Router\Rules\StandardRules;
use Joomla\CMS\Menu\AbstractMenu;
use Joomla\Database\DatabaseInterface;
Expand Down Expand Up @@ -99,6 +100,9 @@ public function __construct(SiteApplication $app, AbstractMenu $menu, CategoryFa

parent::__construct($app, $menu);

$preprocess = new PreprocessRules($contact, '#__contact_details', 'id', 'catid');
$preprocess->setDatabase($this->db);
$this->attachRule($preprocess);
$this->attachRule(new MenuRules($this));
$this->attachRule(new StandardRules($this));
$this->attachRule(new NomenuRules($this));
Expand Down Expand Up @@ -155,19 +159,7 @@ public function getCategoriesSegment($id, $query)
*/
public function getContactSegment($id, $query)
{
if (!strpos($id, ':')) {
$id = (int) $id;
$dbquery = $this->db->getQuery(true);
$dbquery->select($this->db->quoteName('alias'))
->from($this->db->quoteName('#__contact_details'))
->where($this->db->quoteName('id') . ' = :id')
->bind(':id', $id, ParameterType::INTEGER);
$this->db->setQuery($dbquery);

$id .= ':' . $this->db->loadResult();
}

if ($this->noIDs) {
if ($this->noIDs && strpos($id, ':')) {
list($void, $segment) = explode(':', $id, 2);

return [$void => $segment];
Expand Down
18 changes: 5 additions & 13 deletions components/com_content/src/Service/Router.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Joomla\CMS\Component\Router\RouterViewConfiguration;
use Joomla\CMS\Component\Router\Rules\MenuRules;
use Joomla\CMS\Component\Router\Rules\NomenuRules;
use Joomla\CMS\Component\Router\Rules\PreprocessRules;
use Joomla\CMS\Component\Router\Rules\StandardRules;
use Joomla\CMS\Menu\AbstractMenu;
use Joomla\Database\DatabaseInterface;
Expand Down Expand Up @@ -100,6 +101,9 @@ public function __construct(SiteApplication $app, AbstractMenu $menu, CategoryFa

parent::__construct($app, $menu);

$preprocess = new PreprocessRules($article, '#__content', 'id', 'catid');
$preprocess->setDatabase($this->db);
$this->attachRule($preprocess);
$this->attachRule(new MenuRules($this));
$this->attachRule(new StandardRules($this));
$this->attachRule(new NomenuRules($this));
Expand Down Expand Up @@ -156,19 +160,7 @@ public function getCategoriesSegment($id, $query)
*/
public function getArticleSegment($id, $query)
{
if (!strpos($id, ':')) {
$id = (int) $id;
$dbquery = $this->db->getQuery(true);
$dbquery->select($this->db->quoteName('alias'))
->from($this->db->quoteName('#__content'))
->where($this->db->quoteName('id') . ' = :id')
->bind(':id', $id, ParameterType::INTEGER);
$this->db->setQuery($dbquery);

$id .= ':' . $this->db->loadResult();
}

if ($this->noIDs) {
if ($this->noIDs && strpos($id, ':')) {
list($void, $segment) = explode(':', $id, 2);

return [$void => $segment];
Expand Down
18 changes: 5 additions & 13 deletions components/com_newsfeeds/src/Service/Router.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Joomla\CMS\Component\Router\RouterViewConfiguration;
use Joomla\CMS\Component\Router\Rules\MenuRules;
use Joomla\CMS\Component\Router\Rules\NomenuRules;
use Joomla\CMS\Component\Router\Rules\PreprocessRules;
use Joomla\CMS\Component\Router\Rules\StandardRules;
use Joomla\CMS\Menu\AbstractMenu;
use Joomla\Database\DatabaseInterface;
Expand Down Expand Up @@ -95,6 +96,9 @@ public function __construct(SiteApplication $app, AbstractMenu $menu, CategoryFa

parent::__construct($app, $menu);

$preprocess = new PreprocessRules($newsfeed, '#__newsfeeds', 'id', 'catid');
$preprocess->setDatabase($this->db);
$this->attachRule($preprocess);
$this->attachRule(new MenuRules($this));
$this->attachRule(new StandardRules($this));
$this->attachRule(new NomenuRules($this));
Expand Down Expand Up @@ -151,19 +155,7 @@ public function getCategoriesSegment($id, $query)
*/
public function getNewsfeedSegment($id, $query)
{
if (!strpos($id, ':')) {
$id = (int) $id;
$dbquery = $this->db->getQuery(true);
$dbquery->select($this->db->quoteName('alias'))
->from($this->db->quoteName('#__newsfeeds'))
->where($this->db->quoteName('id') . ' = :id')
->bind(':id', $id, ParameterType::INTEGER);
$this->db->setQuery($dbquery);

$id .= ':' . $this->db->loadResult();
}

if ($this->noIDs) {
if ($this->noIDs && strpos($id, ':')) {
list($void, $segment) = explode(':', $id, 2);

return [$void => $segment];
Expand Down
169 changes: 169 additions & 0 deletions libraries/src/Component/Router/Rules/PreprocessRules.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
<?php

/**
* Joomla! Content Management System
*
* @copyright (C) 2024 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/

namespace Joomla\CMS\Component\Router\Rules;

use Joomla\CMS\Component\Router\RouterViewConfiguration;
use Joomla\Database\DatabaseAwareTrait;
use Joomla\Database\ParameterType;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
* Rule to prepare the query and add missing information
*
* This rule adds the alias to an ID query parameter and the
* category ID if either of them is missing. This requires that
* the db table contains an alias column.
* This fixes sloppy URLs in the code, but doesn't mean you can
* simply drop the alias from the &id= in the future. Cleaning up
* every request with this would mean a significant performance impact
*
* @since __DEPLOY_VERSION__
*/
class PreprocessRules implements RulesInterface
{
use DatabaseAwareTrait;

/**
* View to prepare
*
* @var RouterViewConfiguration
* @since __DEPLOY_VERSION__
*/
protected $view;

/**
* DB Table to read the information from
*
* @var string
* @since __DEPLOY_VERSION__
*/
protected $table;

/**
* ID column in the table to read the information from
*
* @var string
* @since __DEPLOY_VERSION__
*/
protected $key;

/**
* Parent ID column in the table to read the information from
*
* @var string
* @since __DEPLOY_VERSION__
*/
protected $parent_key;

/**
* Class constructor.
*
* @param RouterViewConfiguration $view View to act on
* @param string $table Table name for the views information
* @param string $key Key in the table to get the information
* @param string $parent_key Column name of the parent key
*
* @since __DEPLOY_VERSION__
*/
public function __construct(RouterViewConfiguration $view, $table, $key, $parent_key = null)
{
$this->view = $view;
$this->table = $table;
$this->key = $key;
$this->parent_key = $parent_key;
}

/**
* Finds the correct Itemid for this query
*
* @param array &$query The query array to process
*
* @return void
*
* @since __DEPLOY_VERSION__
*/
public function preprocess(&$query)
{
// We only work for URLs with the view we have been setup for
if (!isset($query['view']) || $query['view'] != $this->view->name) {
return;
}

$key = $this->view->key;
$parent_key = $this->view->parent_key;

// We have to have at least the ID or something to repair
if (!isset($query[$key]) || (strpos($query[$key], ':') && isset($query[$parent_key]))) {
return;
}

$dbquery = $this->getDatabase()->getQuery(true);

$dbquery->select($dbquery->quoteName('alias'))
->from($this->table)
->where($dbquery->quoteName($this->key) . ' = :key')
->bind(':key', $query[$key], ParameterType::INTEGER);

// Do we have a parent key?
if ($parent_key && $this->parent_key) {
$dbquery->select($dbquery->quoteName($this->parent_key));
}

$obj = $this->getDatabase()->setQuery($dbquery)->loadObject();

// We haven't found the item in the database. Abort.
if (!$obj) {
return;
}

// Lets fix the slug (id:alias)
if (!strpos($query[$key], ':')) {
$query[$key] .= ':' . $obj->alias;
}

// If we have a parent key and it is missing, lets add it
if ($parent_key && $this->parent_key && !isset($query[$parent_key])) {
$query[$parent_key] = $obj->{$this->parent_key};
}
}

/**
* Dummy method to fulfil the interface requirements
*
* @param array &$segments The URL segments to parse
* @param array &$vars The vars that result from the segments
*
* @return void
*
* @since __DEPLOY_VERSION__
* @codeCoverageIgnore
*/
public function parse(&$segments, &$vars)
{
}

/**
* Dummy method to fulfil the interface requirements
*
* @param array &$query The vars that should be converted
* @param array &$segments The URL segments to create
*
* @return void
*
* @since __DEPLOY_VERSION__
* @codeCoverageIgnore
*/
public function build(&$query, &$segments)
{
}
}