Skip to content

Commit 188f40c

Browse files
authored
[5.3] SEF: Fix URLs when preprocessing (joomla#43992)
1 parent b472042 commit 188f40c

File tree

4 files changed

+184
-39
lines changed

4 files changed

+184
-39
lines changed

components/com_contact/src/Service/Router.php

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Joomla\CMS\Component\Router\RouterViewConfiguration;
1919
use Joomla\CMS\Component\Router\Rules\MenuRules;
2020
use Joomla\CMS\Component\Router\Rules\NomenuRules;
21+
use Joomla\CMS\Component\Router\Rules\PreprocessRules;
2122
use Joomla\CMS\Component\Router\Rules\StandardRules;
2223
use Joomla\CMS\Menu\AbstractMenu;
2324
use Joomla\Database\DatabaseInterface;
@@ -99,6 +100,9 @@ public function __construct(SiteApplication $app, AbstractMenu $menu, CategoryFa
99100

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

103+
$preprocess = new PreprocessRules($contact, '#__contact_details', 'id', 'catid');
104+
$preprocess->setDatabase($this->db);
105+
$this->attachRule($preprocess);
102106
$this->attachRule(new MenuRules($this));
103107
$this->attachRule(new StandardRules($this));
104108
$this->attachRule(new NomenuRules($this));
@@ -155,19 +159,7 @@ public function getCategoriesSegment($id, $query)
155159
*/
156160
public function getContactSegment($id, $query)
157161
{
158-
if (!strpos($id, ':')) {
159-
$id = (int) $id;
160-
$dbquery = $this->db->getQuery(true);
161-
$dbquery->select($this->db->quoteName('alias'))
162-
->from($this->db->quoteName('#__contact_details'))
163-
->where($this->db->quoteName('id') . ' = :id')
164-
->bind(':id', $id, ParameterType::INTEGER);
165-
$this->db->setQuery($dbquery);
166-
167-
$id .= ':' . $this->db->loadResult();
168-
}
169-
170-
if ($this->noIDs) {
162+
if ($this->noIDs && strpos($id, ':')) {
171163
list($void, $segment) = explode(':', $id, 2);
172164

173165
return [$void => $segment];

components/com_content/src/Service/Router.php

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Joomla\CMS\Component\Router\RouterViewConfiguration;
1919
use Joomla\CMS\Component\Router\Rules\MenuRules;
2020
use Joomla\CMS\Component\Router\Rules\NomenuRules;
21+
use Joomla\CMS\Component\Router\Rules\PreprocessRules;
2122
use Joomla\CMS\Component\Router\Rules\StandardRules;
2223
use Joomla\CMS\Menu\AbstractMenu;
2324
use Joomla\Database\DatabaseInterface;
@@ -100,6 +101,9 @@ public function __construct(SiteApplication $app, AbstractMenu $menu, CategoryFa
100101

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

104+
$preprocess = new PreprocessRules($article, '#__content', 'id', 'catid');
105+
$preprocess->setDatabase($this->db);
106+
$this->attachRule($preprocess);
103107
$this->attachRule(new MenuRules($this));
104108
$this->attachRule(new StandardRules($this));
105109
$this->attachRule(new NomenuRules($this));
@@ -156,19 +160,7 @@ public function getCategoriesSegment($id, $query)
156160
*/
157161
public function getArticleSegment($id, $query)
158162
{
159-
if (!strpos($id, ':')) {
160-
$id = (int) $id;
161-
$dbquery = $this->db->getQuery(true);
162-
$dbquery->select($this->db->quoteName('alias'))
163-
->from($this->db->quoteName('#__content'))
164-
->where($this->db->quoteName('id') . ' = :id')
165-
->bind(':id', $id, ParameterType::INTEGER);
166-
$this->db->setQuery($dbquery);
167-
168-
$id .= ':' . $this->db->loadResult();
169-
}
170-
171-
if ($this->noIDs) {
163+
if ($this->noIDs && strpos($id, ':')) {
172164
list($void, $segment) = explode(':', $id, 2);
173165

174166
return [$void => $segment];

components/com_newsfeeds/src/Service/Router.php

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Joomla\CMS\Component\Router\RouterViewConfiguration;
1919
use Joomla\CMS\Component\Router\Rules\MenuRules;
2020
use Joomla\CMS\Component\Router\Rules\NomenuRules;
21+
use Joomla\CMS\Component\Router\Rules\PreprocessRules;
2122
use Joomla\CMS\Component\Router\Rules\StandardRules;
2223
use Joomla\CMS\Menu\AbstractMenu;
2324
use Joomla\Database\DatabaseInterface;
@@ -95,6 +96,9 @@ public function __construct(SiteApplication $app, AbstractMenu $menu, CategoryFa
9596

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

99+
$preprocess = new PreprocessRules($newsfeed, '#__newsfeeds', 'id', 'catid');
100+
$preprocess->setDatabase($this->db);
101+
$this->attachRule($preprocess);
98102
$this->attachRule(new MenuRules($this));
99103
$this->attachRule(new StandardRules($this));
100104
$this->attachRule(new NomenuRules($this));
@@ -151,19 +155,7 @@ public function getCategoriesSegment($id, $query)
151155
*/
152156
public function getNewsfeedSegment($id, $query)
153157
{
154-
if (!strpos($id, ':')) {
155-
$id = (int) $id;
156-
$dbquery = $this->db->getQuery(true);
157-
$dbquery->select($this->db->quoteName('alias'))
158-
->from($this->db->quoteName('#__newsfeeds'))
159-
->where($this->db->quoteName('id') . ' = :id')
160-
->bind(':id', $id, ParameterType::INTEGER);
161-
$this->db->setQuery($dbquery);
162-
163-
$id .= ':' . $this->db->loadResult();
164-
}
165-
166-
if ($this->noIDs) {
158+
if ($this->noIDs && strpos($id, ':')) {
167159
list($void, $segment) = explode(':', $id, 2);
168160

169161
return [$void => $segment];
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
<?php
2+
3+
/**
4+
* Joomla! Content Management System
5+
*
6+
* @copyright (C) 2024 Open Source Matters, Inc. <https://www.joomla.org>
7+
* @license GNU General Public License version 2 or later; see LICENSE.txt
8+
*/
9+
10+
namespace Joomla\CMS\Component\Router\Rules;
11+
12+
use Joomla\CMS\Component\Router\RouterViewConfiguration;
13+
use Joomla\Database\DatabaseAwareTrait;
14+
use Joomla\Database\ParameterType;
15+
16+
// phpcs:disable PSR1.Files.SideEffects
17+
\defined('_JEXEC') or die;
18+
// phpcs:enable PSR1.Files.SideEffects
19+
20+
/**
21+
* Rule to prepare the query and add missing information
22+
*
23+
* This rule adds the alias to an ID query parameter and the
24+
* category ID if either of them is missing. This requires that
25+
* the db table contains an alias column.
26+
* This fixes sloppy URLs in the code, but doesn't mean you can
27+
* simply drop the alias from the &id= in the future. Cleaning up
28+
* every request with this would mean a significant performance impact
29+
*
30+
* @since __DEPLOY_VERSION__
31+
*/
32+
class PreprocessRules implements RulesInterface
33+
{
34+
use DatabaseAwareTrait;
35+
36+
/**
37+
* View to prepare
38+
*
39+
* @var RouterViewConfiguration
40+
* @since __DEPLOY_VERSION__
41+
*/
42+
protected $view;
43+
44+
/**
45+
* DB Table to read the information from
46+
*
47+
* @var string
48+
* @since __DEPLOY_VERSION__
49+
*/
50+
protected $table;
51+
52+
/**
53+
* ID column in the table to read the information from
54+
*
55+
* @var string
56+
* @since __DEPLOY_VERSION__
57+
*/
58+
protected $key;
59+
60+
/**
61+
* Parent ID column in the table to read the information from
62+
*
63+
* @var string
64+
* @since __DEPLOY_VERSION__
65+
*/
66+
protected $parent_key;
67+
68+
/**
69+
* Class constructor.
70+
*
71+
* @param RouterViewConfiguration $view View to act on
72+
* @param string $table Table name for the views information
73+
* @param string $key Key in the table to get the information
74+
* @param string $parent_key Column name of the parent key
75+
*
76+
* @since __DEPLOY_VERSION__
77+
*/
78+
public function __construct(RouterViewConfiguration $view, $table, $key, $parent_key = null)
79+
{
80+
$this->view = $view;
81+
$this->table = $table;
82+
$this->key = $key;
83+
$this->parent_key = $parent_key;
84+
}
85+
86+
/**
87+
* Finds the correct Itemid for this query
88+
*
89+
* @param array &$query The query array to process
90+
*
91+
* @return void
92+
*
93+
* @since __DEPLOY_VERSION__
94+
*/
95+
public function preprocess(&$query)
96+
{
97+
// We only work for URLs with the view we have been setup for
98+
if (!isset($query['view']) || $query['view'] != $this->view->name) {
99+
return;
100+
}
101+
102+
$key = $this->view->key;
103+
$parent_key = $this->view->parent_key;
104+
105+
// We have to have at least the ID or something to repair
106+
if (!isset($query[$key]) || (strpos($query[$key], ':') && isset($query[$parent_key]))) {
107+
return;
108+
}
109+
110+
$dbquery = $this->getDatabase()->getQuery(true);
111+
112+
$dbquery->select($dbquery->quoteName('alias'))
113+
->from($this->table)
114+
->where($dbquery->quoteName($this->key) . ' = :key')
115+
->bind(':key', $query[$key], ParameterType::INTEGER);
116+
117+
// Do we have a parent key?
118+
if ($parent_key && $this->parent_key) {
119+
$dbquery->select($dbquery->quoteName($this->parent_key));
120+
}
121+
122+
$obj = $this->getDatabase()->setQuery($dbquery)->loadObject();
123+
124+
// We haven't found the item in the database. Abort.
125+
if (!$obj) {
126+
return;
127+
}
128+
129+
// Lets fix the slug (id:alias)
130+
if (!strpos($query[$key], ':')) {
131+
$query[$key] .= ':' . $obj->alias;
132+
}
133+
134+
// If we have a parent key and it is missing, lets add it
135+
if ($parent_key && $this->parent_key && !isset($query[$parent_key])) {
136+
$query[$parent_key] = $obj->{$this->parent_key};
137+
}
138+
}
139+
140+
/**
141+
* Dummy method to fulfil the interface requirements
142+
*
143+
* @param array &$segments The URL segments to parse
144+
* @param array &$vars The vars that result from the segments
145+
*
146+
* @return void
147+
*
148+
* @since __DEPLOY_VERSION__
149+
* @codeCoverageIgnore
150+
*/
151+
public function parse(&$segments, &$vars)
152+
{
153+
}
154+
155+
/**
156+
* Dummy method to fulfil the interface requirements
157+
*
158+
* @param array &$query The vars that should be converted
159+
* @param array &$segments The URL segments to create
160+
*
161+
* @return void
162+
*
163+
* @since __DEPLOY_VERSION__
164+
* @codeCoverageIgnore
165+
*/
166+
public function build(&$query, &$segments)
167+
{
168+
}
169+
}

0 commit comments

Comments
 (0)