From a8e5e587c6e721dd7f6c7e3ea8f68b53baf0fb5f Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 1 Jul 2010 12:01:46 -0400 Subject: [PATCH] Making RequestHandler able to use the accept header to detect and switch layout/view paths. Fixes #729 --- .../controller/components/request_handler.php | 24 ++++++--- .../components/request_handler.test.php | 52 ++++++++++++++----- 2 files changed, 57 insertions(+), 19 deletions(-) diff --git a/cake/libs/controller/components/request_handler.php b/cake/libs/controller/components/request_handler.php index 669386a72..66e04107d 100644 --- a/cake/libs/controller/components/request_handler.php +++ b/cake/libs/controller/components/request_handler.php @@ -121,9 +121,9 @@ class RequestHandlerComponent extends Object { /** * Initializes the component, gets a reference to Controller::$parameters, and - * checks to see if a file extension has been parsed by the Router. If yes, the - * corresponding content-type is pushed onto the list of accepted content-types - * as the first item. + * checks to see if a file extension has been parsed by the Router. Or if the + * HTTP_ACCEPT_TYPE is set to a single value that is a supported extension and mapped type. + * If yes, RequestHandler::$ext is set to that value * * @param object $controller A reference to the controller * @param array $settings Array of settings to _set(). @@ -131,10 +131,20 @@ class RequestHandlerComponent extends Object { * @see Router::parseExtensions() */ public function initialize(&$controller, $settings = array()) { + $this->request = $controller->request; if (isset($controller->params['url']['ext'])) { $this->ext = $controller->params['url']['ext']; } - $this->request = $controller->request; + if (empty($this->ext)) { + $accepts = $this->request->accepts(); + $extensions = Router::extensions(); + if (count($accepts) == 1) { + $mapped = $this->mapType($accepts[0]); + if (in_array($mapped, $extensions)) { + $this->ext = $mapped; + } + } + } $this->_set($settings); } @@ -144,8 +154,10 @@ class RequestHandlerComponent extends Object { * * - Disabling layout rendering for Ajax requests (based on the HTTP_X_REQUESTED_WITH header) * - If Router::parseExtensions() is enabled, the layout and template type are - * switched based on the parsed extension. For example, if controller/action.xml - * is requested, the view path becomes app/views/controller/xml/action.ctp. + * switched based on the parsed extension or Accept-Type header. For example, if `controller/action.xml` + * is requested, the view path becomes `app/views/controller/xml/action.ctp`. Also if + * `controller/action` is requested with `Accept-Type: application/xml` in the headers + * the view path will become `app/views/controller/xml/action.ctp`. * - If a helper with the same name as the extension exists, it is added to the controller. * - If the extension is of a type that RequestHandler understands, it will set that * Content-type in the response header. diff --git a/cake/tests/cases/libs/controller/components/request_handler.test.php b/cake/tests/cases/libs/controller/components/request_handler.test.php index 37a084ef0..b929793da 100644 --- a/cake/tests/cases/libs/controller/components/request_handler.test.php +++ b/cake/tests/cases/libs/controller/components/request_handler.test.php @@ -202,8 +202,6 @@ class RequestHandlerComponentTest extends CakeTestCase { */ function testInitializeCallback() { $this->assertNull($this->RequestHandler->ext); - - $this->_init(); $this->Controller->request->params['url']['ext'] = 'rss'; $this->RequestHandler->initialize($this->Controller); $this->assertEqual($this->RequestHandler->ext, 'rss'); @@ -215,6 +213,45 @@ class RequestHandlerComponentTest extends CakeTestCase { $this->assertEqual($this->RequestHandler->ajaxLayout, 'test_ajax'); } +/** + * test that a mapped Accept-type header will set $this->ext correctly. + * + * @return void + */ + function testInitializeContentTypeSettingExt() { + $this->assertNull($this->RequestHandler->ext); + $extensions = Router::extensions(); + Router::parseExtensions('json'); + $this->Controller->request = $this->getMock('CakeRequest'); + $this->Controller->request->expects($this->any())->method('accepts') + ->will($this->returnValue(array('application/json'))); + + $this->RequestHandler->initialize($this->Controller); + $this->assertEquals('json', $this->RequestHandler->ext); + + call_user_func_array(array('Router', 'parseExtensions'), $extensions); + } + +/** + * Test that a type mismatch doesn't incorrectly set the ext + * + * @return void + */ + function testInitializeContentTypeAndExtensionMismatch() { + $this->assertNull($this->RequestHandler->ext); + $extensions = Router::extensions(); + Router::parseExtensions('xml'); + + $this->Controller->request = $this->getMock('CakeRequest'); + $this->Controller->request->expects($this->any())->method('accepts') + ->will($this->returnValue(array('application/json'))); + + $this->RequestHandler->initialize($this->Controller); + $this->assertNull($this->RequestHandler->ext); + + call_user_func_array(array('Router', 'parseExtensions'), $extensions); + } + /** * testDisabling method * @@ -414,24 +451,20 @@ class RequestHandlerComponentTest extends CakeTestCase { $this->assertFalse($result); $_SERVER['HTTP_ACCEPT'] = 'text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,*/*'; - $this->_init(); $this->assertTrue($this->RequestHandler->isXml()); $this->assertFalse($this->RequestHandler->isAtom()); $this->assertFalse($this->RequestHandler->isRSS()); $_SERVER['HTTP_ACCEPT'] = 'application/atom+xml,text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,*/*'; - $this->_init(); $this->assertTrue($this->RequestHandler->isAtom()); $this->assertFalse($this->RequestHandler->isRSS()); $_SERVER['HTTP_ACCEPT'] = 'application/rss+xml,text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,*/*'; - $this->_init(); $this->assertFalse($this->RequestHandler->isAtom()); $this->assertTrue($this->RequestHandler->isRSS()); $this->assertFalse($this->RequestHandler->isWap()); $_SERVER['HTTP_ACCEPT'] = 'text/vnd.wap.wml,text/html,text/plain,image/png,*/*'; - $this->_init(); $this->assertTrue($this->RequestHandler->isWap()); $_SERVER['HTTP_ACCEPT'] = 'application/rss+xml,text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,*/*'; @@ -539,12 +572,10 @@ class RequestHandlerComponentTest extends CakeTestCase { */ function testAccepts() { $_SERVER['HTTP_ACCEPT'] = 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'; - $this->_init(); $this->assertEqual($this->RequestHandler->accepts(array('js', 'xml', 'html')), 'xml'); $this->assertFalse($this->RequestHandler->accepts(array('gif', 'jpeg', 'foo'))); $_SERVER['HTTP_ACCEPT'] = '*/*;q=0.5'; - $this->_init(); $this->assertFalse($this->RequestHandler->accepts('rss')); } @@ -556,7 +587,6 @@ class RequestHandlerComponentTest extends CakeTestCase { */ function testPrefers() { $_SERVER['HTTP_ACCEPT'] = 'text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,*/*'; - $this->_init(); $this->assertNotEqual($this->RequestHandler->prefers(), 'rss'); $this->RequestHandler->ext = 'rss'; $this->assertEqual($this->RequestHandler->prefers(), 'rss'); @@ -570,7 +600,6 @@ class RequestHandlerComponentTest extends CakeTestCase { $this->assertEqual($this->RequestHandler->prefers(), 'xml'); $_SERVER['HTTP_ACCEPT'] = '*/*;q=0.5'; - $this->_init(); $this->assertEqual($this->RequestHandler->prefers(), 'html'); $this->assertFalse($this->RequestHandler->prefers('rss')); } @@ -583,7 +612,6 @@ class RequestHandlerComponentTest extends CakeTestCase { */ function testCustomContent() { $_SERVER['HTTP_ACCEPT'] = 'text/x-mobile,text/html;q=0.9,text/plain;q=0.8,*/*;q=0.5'; - $this->_init(); $this->RequestHandler->setContent('mobile', 'text/x-mobile'); $this->RequestHandler->startup($this->Controller); $this->assertEqual($this->RequestHandler->prefers(), 'mobile'); @@ -612,7 +640,6 @@ class RequestHandlerComponentTest extends CakeTestCase { * @return void */ function testAjaxRedirectAsRequestAction() { - $this->_init(); App::build(array( 'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS) ), true); @@ -641,7 +668,6 @@ class RequestHandlerComponentTest extends CakeTestCase { * @return void */ function testAjaxRedirectAsRequestActionStillRenderingLayout() { - $this->_init(); App::build(array( 'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS) ), true);