ソースを参照

AjaxComponent init

euromark 12 年 前
コミット
ad0c0b83ae

+ 116 - 0
Controller/Component/AjaxComponent.php

@@ -0,0 +1,116 @@
+<?php
+App::uses('Component', 'Controller');
+
+/**
+ * Ajax Component to respond to AJAX requests.
+ *
+ * Works together with the AjaxView to easily switch
+ * output type from HTML to JSON format.
+ *
+ * It will also avoid redirects and pass those down as content
+ * of the JSON response object.
+ *
+ * @author Mark Scherer
+ * @license MIT
+ */
+class AjaxComponent extends Component {
+
+	public $Controller;
+
+	public $components = array('Session');
+
+	public $respondAsAjax = false;
+
+	protected $_defaults = array(
+		'autoDetect' => true,
+		'resolveRedirect' => true,
+		'flashKey' => 'Message.flash' // Use "messages" for Tools plugin, set to false to disable
+	);
+
+/**
+ * Constructor.
+ *
+ * @param ComponentCollection $collection
+ * @param array $settings
+ */
+	public function __construct(ComponentCollection $collection, $settings = array()) {
+		$settings = array_merge($this->_defaults, (array)Configure::read('Ajax'), $settings);
+		parent::__construct($collection, $settings);
+	}
+
+	public function initialize(Controller $Controller) {
+		$this->Controller = $Controller;
+
+		if (!$this->settings['autoDetect']) {
+			return;
+		}
+		$this->respondAsAjax = $this->Controller->request->is('ajax');
+	}
+
+	/**
+	 * Called before the Controller::beforeRender(), and before
+	 * the view class is loaded, and before Controller::render()
+	 *
+	 * @param Controller $controller Controller with components to beforeRender
+	 * @return void
+	 */
+	public function beforeRender(Controller $controller) {
+		if (!$this->respondAsAjax) {
+			return;
+		}
+		$this->_respondAsAjax();
+	}
+
+	/**
+	 * AjaxComponent::respondAsAjax()
+	 *
+	 * @return void
+	 */
+	protected function _respondAsAjax() {
+		$this->Controller->viewClass = 'Tools.Ajax';
+
+		// Set flash messages to the view
+		if ($this->settings['flashKey']) {
+	    $_message = $this->Session->read($this->settings['flashKey']);
+	    $this->Session->delete($this->settings['flashKey']);
+	    $this->Controller->set(compact('_message'));
+		}
+	}
+
+	/**
+	 * Called before Controller::redirect(). Allows you to replace the URL that will
+	 * be redirected to with a new URL. The return of this method can either be an array or a string.
+	 *
+	 * If the return is an array and contains a 'url' key. You may also supply the following:
+	 *
+	 * - `status` The status code for the redirect
+	 * - `exit` Whether or not the redirect should exit.
+	 *
+	 * If your response is a string or an array that does not contain a 'url' key it will
+	 * be used as the new URL to redirect to.
+	 *
+	 * @param Controller $controller Controller with components to beforeRedirect
+	 * @param string|array $url Either the string or URL array that is being redirected to.
+	 * @param integer $status The status code of the redirect
+	 * @param boolean $exit Will the script exit.
+	 * @return array|void Either an array or null.
+	 */
+	public function beforeRedirect(Controller $controller, $url, $status = null, $exit = true) {
+		if (!$this->respondAsAjax || !$this->settings['resolveRedirect']) {
+			return parent::beforeRedirect($controller, $url, $status, $exit);
+		}
+
+		$url = Router::url($url, true);
+
+		if (is_string($status)) {
+			$codes = array_flip($this->response->httpCodes());
+			if (isset($codes[$status])) {
+				$status = $codes[$status];
+			}
+		}
+
+		$this->Controller->set('_redirect', compact('url', 'status', 'exit'));
+		return false;
+	}
+
+}

+ 140 - 0
Test/Case/Controller/Component/AjaxComponentTest.php

@@ -0,0 +1,140 @@
+<?php
+
+App::uses('AjaxComponent', 'Tools.Controller/Component');
+App::uses('Component', 'Controller');
+App::uses('Controller', 'Controller');
+App::uses('AppModel', 'Model');
+
+/**
+ */
+class AjaxComponentTest extends CakeTestCase {
+
+	public $fixtures = array('core.cake_session', 'plugin.tools.tools_user', 'plugin.tools.role');
+
+	public function setUp() {
+		parent::setUp();
+		Configure::delete('Ajax');
+
+		$this->Controller = new AjaxComponentTestController(new CakeRequest, new CakeResponse);
+		$this->Controller->constructClasses();
+	}
+
+	/**
+	 * AjaxComponentTest::testNonAjax()
+	 *
+	 * @return void
+	 */
+	public function testNonAjax() {
+		$this->Controller->startupProcess();
+		$this->assertFalse($this->Controller->Components->Ajax->respondAsAjax);
+	}
+
+	/**
+	 * AjaxComponentTest::testDefaults()
+	 *
+	 * @return void
+	 */
+	public function testDefaults() {
+		$_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest';
+
+		$this->Controller->startupProcess();
+		$this->assertTrue($this->Controller->Components->Ajax->respondAsAjax);
+
+		$this->Controller->Session->setFlash('A message', 'custom');
+		$session = $this->Controller->Session->read('Message.flash');
+		$expected = array(
+			'message' => 'A message',
+			'element' => 'custom',
+			'params' => array()
+		);
+		$this->assertEquals($expected, $session);
+
+		$this->Controller->Components->Ajax->beforeRender($this->Controller);
+		$this->assertEqual('Tools.Ajax', $this->Controller->viewClass);
+
+		$this->assertEquals($expected, $this->Controller->viewVars['_message']);
+
+		$session = $this->Controller->Session->read('Message.flash');
+		$this->assertNull($session);
+
+		$this->Controller->redirect('/');
+		$this->assertSame(array(), $this->Controller->response->header());
+
+		$expected = array(
+			'url' => Router::url('/', true),
+			'status' => null,
+			'exit' => true
+		);
+		$this->assertEquals($expected, $this->Controller->viewVars['_redirect']);
+	}
+
+	/**
+	 * AjaxComponentTest::testAutoDetectOnFalse()
+	 *
+	 * @return void
+	 */
+	public function testAutoDetectOnFalse() {
+		$_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest';
+
+		$this->Controller->Components->unload('Ajax');
+		$this->Controller->Components->load('Tools.Ajax', array('autoDetect' => false));
+
+		$this->Controller->startupProcess();
+		$this->assertFalse($this->Controller->Components->Ajax->respondAsAjax);
+	}
+
+	/**
+	 * AjaxComponentTest::testAutoDetectOnFalseViaConfig()
+	 *
+	 * @return void
+	 */
+	public function testAutoDetectOnFalseViaConfig() {
+		$_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest';
+		Configure::write('Ajax.autoDetect', false);
+
+		$this->Controller->Components->unload('Ajax');
+		$this->Controller->Components->load('Tools.Ajax');
+
+		$this->Controller->startupProcess();
+		$this->assertFalse($this->Controller->Components->Ajax->respondAsAjax);
+	}
+
+	/**
+	 * AjaxComponentTest::testToolsMultiMessages()
+	 *
+	 * @return void
+	 */
+	public function testToolsMultiMessages() {
+		$_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest';
+		Configure::write('Ajax.flashKey', 'messages');
+
+		$this->Controller->Components->unload('Ajax');
+		$this->Controller->Components->load('Tools.Ajax');
+
+		$this->Controller->startupProcess();
+		$this->assertTrue($this->Controller->Components->Ajax->respondAsAjax);
+
+		$this->Controller->Common->flashMessage('A message', 'success');
+		$session = $this->Controller->Session->read('messages');
+		$expected = array(
+			'success' => array('A message')
+		);
+		$this->assertEquals($expected, $session);
+
+		$this->Controller->Components->Ajax->beforeRender($this->Controller);
+		$this->assertEquals('Tools.Ajax', $this->Controller->viewClass);
+
+		$this->assertEquals($expected, $this->Controller->viewVars['_message']);
+
+		$session = $this->Controller->Session->read('messages');
+		$this->assertNull($session);
+	}
+
+}
+
+// Use Controller instead of AppController to avoid conflicts
+class AjaxComponentTestController extends Controller {
+
+	public $components = array('Session', 'Tools.Ajax', 'Tools.Common');
+
+}