diff --git a/cake/libs/route/redirect_route.php b/cake/libs/route/redirect_route.php new file mode 100644 index 000000000..148ce358f --- /dev/null +++ b/cake/libs/route/redirect_route.php @@ -0,0 +1,74 @@ +response) { + $this->response = new CakeResponse(); + } + $redirect = $this->defaults; + if (count($this->defaults) == 1 && !isset($this->defaults['controller'])) { + $redirect = $this->defaults[0]; + } + if (isset($this->options['persist']) && is_array($redirect)) { + $argOptions['context'] = array('action' => $redirect['action'], 'controller' => $redirect['controller']); + $args = Router::getArgs($params['_args_'], $argOptions); + $redirect += $args['pass']; + $redirect += $args['named']; + } + $status = 301; + if (isset($this->options['status']) && ($this->options['status'] >= 300 && $this->options['status'] < 400)) { + $status = $this->options['status']; + } + $this->response->header(array('Location' => Router::url($redirect, true))); + $this->response->statusCode($status); + $this->response->send(); + } + +/** + * There is no reverse routing redirection routes + * + * @param array $url Array of parameters to convert to a string. + * @return mixed either false or a string url. + */ + public function match($url) { + return false; + } +} \ No newline at end of file diff --git a/cake/libs/router.php b/cake/libs/router.php index d3a467281..f3a0b9a74 100644 --- a/cake/libs/router.php +++ b/cake/libs/router.php @@ -253,6 +253,41 @@ class Router { return self::$routes; } +/** + * Connects a new redirection Route in the router. + * + * Redirection routes are different from normal routes as they perform an actual + * header redirection if a match is found. The redirection can occur within your + * application or redirect to an outside location. + * + * Examples: + * + * `Router::redirect('/home/*', array('controller' => 'posts', 'action' => 'view', array('persist' => true));` + * + * Redirects /home/* to /posts/view and passes the parameters to /posts/view + * + * `Router::redirect('/posts/*', 'http://google.com', array('status' => 302));` + * + * Redirects /posts/* to http://google.com with a HTTP status of 302 + * + * ### Options: + * - `status` Sets the HTTP status (default 301) + * - `persist` Passes the params to the redirected route, if it can + * + * @param string $route A string describing the template of the route + * @param array $url A url to redirect to. Can be a string or a Cake array-based url + * @param array $options An array matching the named elements in the route to regular expressions which that + * element should match. Also contains additional parameters such as which routed parameters should be + * shifted into the passed arguments. As well as supplying patterns for routing parameters. + * @see routes + * @return array Array of routes + */ + public static function redirect($route, $url, $options) { + App::import('Core', 'route/RedirectRoute'); + $options['routeClass'] = 'RedirectRoute'; + return self::connect($route, $url, $options); + } + /** * Specifies what named parameters CakePHP should be parsing. The most common setups are: * diff --git a/cake/tests/cases/libs/route/redirect_route.test.php b/cake/tests/cases/libs/route/redirect_route.test.php new file mode 100644 index 000000000..657cd28e2 --- /dev/null +++ b/cake/tests/cases/libs/route/redirect_route.test.php @@ -0,0 +1,71 @@ + null, 'prefixes' => array())); + Router::reload(); + } + +/** + * test the parsing of routes. + * + * @return void + */ + function testParsing() { + $route = new RedirectRoute('/home', array('controller' => 'posts')); + $route->response = $this->getMock('CakeResponse', array('_sendHeader')); + $result = $route->parse('/home'); + $this->assertEqual($route->response->header(), array('Location' => Router::url('/posts', true))); + + $route = new RedirectRoute('/home', array('controller' => 'posts', 'action' => 'index')); + $route->response = $this->getMock('CakeResponse', array('_sendHeader')); + $result = $route->parse('/home'); + $this->assertEqual($route->response->header(), array('Location' => Router::url('/posts', true))); + $this->assertEqual($route->response->statusCode(), 301); + + $route = new RedirectRoute('/google', 'http://google.com'); + $route->response = $this->getMock('CakeResponse', array('_sendHeader')); + $result = $route->parse('/google'); + $this->assertEqual($route->response->header(), array('Location' => 'http://google.com')); + + $route = new RedirectRoute('/posts/*', array('controller' => 'posts', 'action' => 'view'), array('status' => 302)); + $route->response = $this->getMock('CakeResponse', array('_sendHeader')); + $result = $route->parse('/posts/2'); + $this->assertEqual($route->response->header(), array('Location' => Router::url('/posts/view', true))); + $this->assertEqual($route->response->statusCode(), 302); + + $route = new RedirectRoute('/posts/*', array('controller' => 'posts', 'action' => 'view'), array('persist' => true)); + $route->response = $this->getMock('CakeResponse', array('_sendHeader')); + $result = $route->parse('/posts/2'); + $this->assertEqual($route->response->header(), array('Location' => Router::url('/posts/view/2', true))); + + $route = new RedirectRoute('/posts/*', '/test', array('persist' => true)); + $route->response = $this->getMock('CakeResponse', array('_sendHeader')); + $result = $route->parse('/posts/2'); + $this->assertEqual($route->response->header(), array('Location' => Router::url('/test', true))); + + $route = new RedirectRoute('/my_controllers/:action/*', array('controller' => 'tags', 'action' => 'add'), array('persist' => true)); + $route->response = $this->getMock('CakeResponse', array('_sendHeader')); + $result = $route->parse('/my_controllers/do_something/passme/named:param'); + $this->assertEqual($route->response->header(), array('Location' => Router::url('/tags/add/passme/named:param', true))); + + $route = new RedirectRoute('/my_controllers/:action/*', array('controller' => 'tags', 'action' => 'add')); + $route->response = $this->getMock('CakeResponse', array('_sendHeader')); + $result = $route->parse('/my_controllers/do_something/passme/named:param'); + $this->assertEqual($route->response->header(), array('Location' => Router::url('/tags/add', true))); + } + +} diff --git a/cake/tests/cases/libs/router.test.php b/cake/tests/cases/libs/router.test.php index 0178174bb..4637c234e 100644 --- a/cake/tests/cases/libs/router.test.php +++ b/cake/tests/cases/libs/router.test.php @@ -18,6 +18,7 @@ * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ App::import('Core', array('Router')); +App::import('Core', 'CakeResponse'); if (!defined('FULL_BASE_URL')) { define('FULL_BASE_URL', 'http://cakephp.org'); @@ -2191,4 +2192,24 @@ class RouterTest extends CakeTestCase { $this->assertEqual($result->here, '/protected/images/index'); $this->assertEqual($result->webroot, '/'); } + +/** + * test setting redirect routes + * + * @return void + */ + function testRouteRedirection() { + Router::redirect('/blog', array('controller' => 'posts'), array('status' => 302)); + $this->assertEqual(count(Router::$routes), 1); + Router::$routes[0]->response = $this->getMock('CakeResponse', array('_sendHeader')); + $this->assertEqual(Router::$routes[0]->options['status'], 302); + + Router::parse('/blog'); + $this->assertEqual(Router::$routes[0]->response->header(), array('Location' => Router::url('/posts', true))); + $this->assertEqual(Router::$routes[0]->response->statusCode(), 302); + + Router::$routes[0]->response = $this->getMock('CakeResponse', array('_sendHeader')); + Router::parse('/not-a-match'); + $this->assertEqual(Router::$routes[0]->response->header(), array()); + } }