Rework pretty URLs

This commit is contained in:
grandeljay 2022-11-11 13:40:40 +01:00
parent 29c9c3c472
commit aa1c391166
6 changed files with 106 additions and 78 deletions

View file

@ -10,11 +10,11 @@
RewriteRule ^([a-z\-]+)$ /?page=$1 [QSA,L] RewriteRule ^([a-z\-]+)$ /?page=$1 [QSA,L]
# Wishlists (My lists) # Wishlists (My lists)
RewriteRule ^(wishlists)/([0-9]+)$ /?page=$1&id=$2 [QSA,L] RewriteRule ^wishlists/([0-9]+)$ /?page=wishlists&id=$1 [QSA,L]
RewriteRule ^(wishlists)/([0-9]+)/add$ /?page=$1&id=$2&wish_add=true [QSA,L] RewriteRule ^wishlists/([0-9]+)/add$ /?page=wishlists&id=$1&wish_add=true [QSA,L]
# Wishlist # Wishlist
RewriteRule ^(wishlist)/([0-9a-f]{40})$ /?page=$1&hash=$2 [QSA,L] RewriteRule ^wishlist/([0-9a-f]{40})$ /?page=wishlist&hash=$1 [QSA,L]
# Blog Post # Blog Post
RewriteRule ^blog/([a-z\-0-9]+)$ /?page=post&slug=$1 [QSA,L] RewriteRule ^blog/([a-z\-0-9]+)$ /?page=post&slug=$1 [QSA,L]

View file

@ -18,12 +18,13 @@ if (!isset($page)) {
switch ($_SERVER['REQUEST_METHOD']) { switch ($_SERVER['REQUEST_METHOD']) {
case 'GET': case 'GET':
if (isset($_GET['url'])) { if (isset($_GET['url'])) {
$url = new URL(base64_decode($_GET['url'])); $url_old = base64_decode($_GET['url']);
$url = new URL($url_old);
$response['data'] = array( $response['data'] = array(
'url' => $url->getPretty(), 'url' => $url_old,
'url_old' => $url->url, 'url_pretty' => $url->getPretty(),
'url_old_pretty' => $url->isPretty(), 'url_is_pretty' => $url->isPretty(),
); );
} }
break; break;

View file

@ -81,7 +81,7 @@ $(function () {
.then(handleFetchError) .then(handleFetchError)
.then(handleFetchResponse) .then(handleFetchResponse)
.then(function(response) { .then(function(response) {
window.history.pushState(null, document.title, response.data.url); window.history.pushState(null, document.title, response.data.url_pretty);
$('.ui.dropdown.filter.priority') $('.ui.dropdown.filter.priority')
.dropdown('restore default value') .dropdown('restore default value')

View file

@ -149,35 +149,26 @@ class Page
/** /**
* Session * Session
*/ */
$user = isset($_SESSION['user']->id) ? $_SESSION['user'] : new User();
$user = isset($_SESSION['user']->id) ? $_SESSION['user'] : new User();
$ignorePower = array(
'blog',
'changelog',
'home',
'install',
'login',
'maintenance',
'post',
'register',
'wishlist',
);
if (
false === $user->isLoggedIn()
&& isset($_GET['page'])
&& false === in_array($_GET['page'], $ignorePower)
) {
redirect(Page::PAGE_LOGIN);
}
/** /**
* Power * Power
*/ */
if (isset($user->power) && $user->power < $this->power) { if (isset($user->power) && $user->power < $this->power && 0 !== $this->power) {
redirect(Page::PAGE_POWER . '&required=' . $this->power); redirect(Page::PAGE_POWER . '&required=' . $this->power);
} }
/**
* Login
*/
if (
false === $user->isLoggedIn()
&& isset($_GET['page'])
&& 0 !== $this->power
) {
redirect(Page::PAGE_LOGIN);
}
/** /**
* Update * Update
*/ */
@ -200,7 +191,7 @@ class Page
* Redirect * Redirect
*/ */
if ($options && $options->getOption('isInstalled') && isset($_GET)) { if ($options && $options->getOption('isInstalled') && isset($_GET)) {
$url = new URL(http_build_query($_GET)); $url = new URL($_SERVER['REQUEST_URI']);
if ($url->url && false === $url->isPretty()) { if ($url->url && false === $url->isPretty()) {
redirect($url->getPretty()); redirect($url->getPretty());

View file

@ -11,7 +11,11 @@ namespace wishthis;
class URL class URL
{ {
/** /**
* Static * Returns the HTTP status code of a URL.
*
* @param string $url
*
* @return integer
*/ */
public static function getResponseCode(string $url): int public static function getResponseCode(string $url): int
{ {
@ -43,24 +47,41 @@ class URL
} }
/** /**
* Non-Static * The current URL. Can be pretty or not.
*
* @var string
*/ */
public string $url; public string $url;
/**
* Constructor
*
* @param string $url
*/
public function __construct(string $url) public function __construct(string $url)
{ {
$this->url = urldecode($url); $this->url = urldecode($url);
$_GET = $this->get_GET(); $_GET = $this->getGET();
} }
/**
* Returns whether the current URL is pretty.
*
* @return boolean
*/
public function isPretty(): bool public function isPretty(): bool
{ {
$isPretty = 1 === preg_match('/^\/[a-z0-9\/]+/', $this->url); $isPretty = 1 === preg_match('/^\/[a-z0-9\/\-]+$/', $this->url);
return $isPretty; return $isPretty;
} }
/**
* Returns the original, un-pretty URL or an empty string on failure.
*
* @return string
*/
public function getPermalink(): string public function getPermalink(): string
{ {
$htaccess = preg_split('/\r\n|\r|\n/', file_get_contents(ROOT . '/.htaccess')); $htaccess = preg_split('/\r\n|\r|\n/', file_get_contents(ROOT . '/.htaccess'));
@ -95,10 +116,14 @@ class URL
return $permalink; return $permalink;
} }
/**
* Returns a pretty version of the current URL.
*
* @return string
*/
public function getPretty(): string public function getPretty(): string
{ {
$htaccess = preg_split('/\r\n|\r|\n/', file_get_contents(ROOT . '/.htaccess')); $htaccess = preg_split('/\r\n|\r|\n/', file_get_contents(ROOT . '/.htaccess'));
$pretty_url = '';
if (!$this->url) { if (!$this->url) {
return ''; return '';
@ -121,63 +146,74 @@ class URL
explode('&', parse_url($target, PHP_URL_QUERY)) explode('&', parse_url($target, PHP_URL_QUERY))
); );
$flags = explode(',', substr($parts[3], 1, -1)) ?? array(); $flags = explode(',', substr($parts[3], 1, -1)) ?? array();
parse_str($this->url, $getParameters);
uasort( parse_str(ltrim($target, '/?'), $parameters);
$getParameters, /** */
function ($a, $b) {
return strlen($a) <=> strlen($b); /** Determine a potential URL. */
$potential_url = $rewriteRule;
preg_match_all('/\(.+?\)/', $rewriteRule, $groups);
$groups = $groups[0];
for ($i = 0; $i < count($groups); $i++) {
foreach ($parameters as $key => $value) {
$replacement = '$' . $i + 1;
if ($replacement === $value && isset($_GET[$key])) {
$potential_url = str_replace(
$groups[$i],
$_GET[$key],
$potential_url
);
}
} }
}
$match = preg_match(
'/^' . str_replace(array('/'), array('\/'), $rewriteRule) . '$/',
$potential_url
); );
$getParameters = array_reverse($getParameters, true);
preg_match_all('/\(.+?\)/', $rewriteRule, $regexes); if (1 === $match && count($_GET) === count(explode('/', $rewriteRule))) {
return '/' . $potential_url;
$countMatches = 0;
foreach ($regexes as $matches) {
foreach ($matches as $match) {
foreach ($getParameters as $key => $value) {
if (
preg_match('/^' . $match . '$/', $value)
&& in_array($key, $keys)
&& count($getParameters) === count($keys)
) {
$rewriteRule = str_replace($match, $value, $rewriteRule);
$countMatches++;
break;
}
}
}
if ($countMatches > 0 && $countMatches === count($matches)) {
$pretty_url = '/' . $rewriteRule;
if (in_array('L', $flags)) {
break 3;
}
}
} }
break; break;
} }
} }
} }
return $pretty_url ?: '/?' . $this->url; if ('/?' === substr($this->url, 0, 2)) {
return $this->url;
}
return '/?' . $this->url;
} }
public function get_GET(): array /**
* Returns the current URL parameters, even for pretty URLs.
*
* @return array
*/
public function getGET(): array
{ {
$queryString = parse_url($this->getPermalink(), PHP_URL_QUERY); $queryString = $this->url;
$GET = array(); $GET = array();
if ($this->isPretty() && $queryString) { if ($this->isPretty()) {
parse_str($queryString, $GET); $queryString = parse_url($this->getPermalink(), PHP_URL_QUERY);
} else {
$GET = $_GET;
} }
if (null === $queryString) {
return array();
}
if ('/?' === substr($queryString, 0, 2)) {
$queryString = substr($queryString, 2);
}
parse_str($queryString, $GET);
return $GET; return $GET;
} }
} }

View file

@ -43,7 +43,7 @@ $page->navigation();
<li> <li>
<?php <?php
/** TRANSLATORS: Changelog: Improved */ /** TRANSLATORS: Changelog: Improved */
echo __('Completely reworked the API'); echo __('Large parts of wishthis have been completely rewritten, such as the API and how pretty URLs work. The aim was to increase security and maintenability.');
?> ?>
</li> </li>
</ul> </ul>