Commit 2b212a93 authored by Sean Madsen's avatar Sean Madsen

Implement system for internal page redirects

parent 6d25beab
......@@ -42,3 +42,10 @@ services:
class: Monolog\Handler\StreamHandler
arguments:
- %kernel.logs_dir%/publish.log
app.exception_listener:
class: AppBundle\EventListener\ExceptionListener
arguments:
- %publish_path_root%
tags:
- { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
<?php
namespace AppBundle\EventListener;
use AppBundle\Utils\StringTools;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use \AppBundle\Model\Library;
class ExceptionListener {
/**
* @var string Filesystem path to the directory where all published books go
*/
public $publishPathRoot;
/**
* ExceptionListener constructor.
* @param string $publishPathRoot
*/
public function __construct($publishPathRoot) {
$this->publishPathRoot = $publishPathRoot;
}
/**
* This method is called by some sort of Symfony magic for every exception
* @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
*/
public function onKernelException(GetResponseForExceptionEvent $event) {
$exception = $event->getException();
if ($exception instanceof NotFoundHttpException) {
$requestUri = $event->getRequest()->getRequestUri();
$redirect = $this->lookupRedirect($requestUri);
if ($redirect) {
$response = new RedirectResponse($redirect);
$event->setResponse($response);
}
}
}
/**
* See if we have a redirect stored for the given URI. If so, return the full
* path to it as a string (which begins with a slash). If not, return NULL
* @param string $requestUri
* @return null|string
*/
private function lookupRedirect($requestUri) {
// Give up right away if the request contains two dots (for security)
if (strstr($requestUri, '..')) {
return NULL;
}
$requestParts = Library::parseIdentifier($requestUri);
$edition = $requestParts['editionIdentifier'];
$path = $requestParts['path'];
$fragment = $requestParts['fragment'] ? "#${requestParts['fragment']}" : '';
$redirectsFile = $this->publishPathRoot . '/' . $edition . '/redirects.txt';
// If we don't have all the info we need, then give up
if ($edition === NULL || $path === NULL || !file_exists($redirectsFile)) {
return NULL;
}
// Look for a redirect
$redirects = file($redirectsFile);
foreach ($redirects as $redirect) {
$rule = StringTools::parseRedirectRule($redirect);
if ($rule && $rule['from'] == $path) {
return "/$edition/${rule['to']}$fragment";
}
}
return NULL;
}
}
......@@ -20,4 +20,36 @@ class StringTools {
return $clean;
}
/**
* Look at one redirect rule, as it is written in a text file, and determine
* the "from" and "to" elements.
*
* See StringToolsTest::redirectRuleProvider() for examples
*
* @param string $rule
*
* @return array
*/
public static function parseRedirectRule($rule) {
$rule = trim($rule);
// ignore comments (lines beginning with #)
if (preg_match('_^#_', $rule)) {
return NULL;
}
// Split by spaces
$redirectParts = array_values(array_filter(explode(' ', $rule)));
// Trim slashes from results
$redirectParts = array_map(function($v) {
return trim($v,'/');
}, $redirectParts);
$from = $redirectParts[0] ?? NULL;
$to = $redirectParts[1] ?? NULL;
return ($from && $to) ? ['from' => $from, 'to' => $to] : NULL;
}
}
......@@ -40,4 +40,82 @@ class StringToolsTest extends \PHPUnit_Framework_TestCase
],
];
}
/**
* @param $rule
* @param $expected
* @dataProvider redirectRuleProvider
*/
public function testParseRedirectRule($rule, $expected) {
$this->assertEquals($expected, StringTools::parseRedirectRule($rule));
}
/**
* @return array
*/
public function redirectRuleProvider() {
return [
[
'foo/bar baz/bat',
[
'from' => 'foo/bar',
'to' => 'baz/bat',
]
],
[
'/foo/bar/ /baz/bat/',
[
'from' => 'foo/bar',
'to' => 'baz/bat',
]
],
[
'foo///bar baz///bat',
[
'from' => 'foo///bar',
'to' => 'baz///bat',
]
],
[
" /foo/bar /baz/bat \n",
[
'from' => 'foo/bar',
'to' => 'baz/bat',
]
],
[
'#foo/bar baz/bat',
NULL,
],
[
'foo/bar/baz/bat',
NULL,
],
[
' ',
NULL,
],
[
'foo/bar baz/bat spam/eggs',
[
'from' => 'foo/bar',
'to' => 'baz/bat',
]
],
[
NULL,
NULL,
],
];
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment