Blame view

framework/control/PjaxResponseNegotiator.php 3.71 KB
0084d336   Administrator   Importers CRUD
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
  <?php
  /**
   * Handle the X-Pjax header that AJAX responses may provide, returning the 
   * fragment, or, in the case of non-AJAX form submissions, redirecting back 
   * to the submitter.
   *
   * X-Pjax ensures that users won't end up seeing the unstyled form HTML in 
   * their browser.
   *
   * If a JS error prevents the Ajax overriding of form submissions from happening.
   *
   * It also provides better non-JS operation.
   * 
   * Caution: This API is volatile, and might eventually be replaced by a generic
   * action helper system for controllers.
   *
   * @package framework 
   * @subpackage control
   */
  class PjaxResponseNegotiator {
  
  	/**
  	 * @var Array See {@link respond()}
  	 */
  	protected $callbacks = array(
  		// TODO Using deprecated functionality, but don't want to duplicate Controller->redirectBack()
  		'default' => array('Director', 'redirectBack'),
  	);
  
  	protected $response = null;
  
  	/**
  	 * Overriden fragments (if any). Otherwise uses fragments from the request.
  	 */
  	protected $fragmentOverride = null;
  
  	/**
  	 * @param RequestHandler $controller
  	 * @param SS_HTTPResponse An existing response to reuse (optional)
  	 * @param Array $callbacks
  	 */
  	public function __construct($callbacks = array(), $response = null) {
  		$this->callbacks = $callbacks; 
  		$this->response = $response;
  	}
  
  	public function getResponse() {
  		if(!$this->response) $this->response = new SS_HTTPResponse();
  		return $this->response;
  	}
  
  	public function setResponse($response) {
  		$this->response = $response;
  	}
  
  	/**
  	 * Out of the box, the handler "CurrentForm" value, which will return the rendered form.  
  	 * Non-Ajax calls will redirect back.
  	 * 
  	 * @param SS_HTTPRequest $request 
  	 * @param array $extraCallbacks List of anonymous functions or callables returning either a string
  	 * or SS_HTTPResponse, keyed by their fragment identifier. The 'default' key can
  	 * be used as a fallback for non-ajax responses.
  	 * @param array $fragmentOverride Change the response fragments.
  	 * @return SS_HTTPResponse
  	 */
  	public function respond(SS_HTTPRequest $request, $extraCallbacks = array()) {
  		// Prepare the default options and combine with the others
  		$callbacks = array_merge($this->callbacks, $extraCallbacks);
  		$response = $this->getResponse();
  		
  		$responseParts = array();
  
  		if (isset($this->fragmentOverride)) {
  			$fragments = $this->fragmentOverride;
  		} elseif ($fragmentStr = $request->getHeader('X-Pjax')) {
  			$fragments = explode(',', $fragmentStr);
  		} else {
  			if($request->isAjax()) {
  				throw new SS_HTTPResponse_Exception("Ajax requests to this URL require an X-Pjax header.", 400);
  			}
  			$response->setBody(call_user_func($callbacks['default']));
  			return $response;
  		}
  
  		// Execute the fragment callbacks and build the response.
  		foreach($fragments as $fragment) {
  			if(isset($callbacks[$fragment])) {
  				$res = call_user_func($callbacks[$fragment]);
  				$responseParts[$fragment] = $res ? (string)$res : $res;
  			} else {
  				throw new SS_HTTPResponse_Exception("X-Pjax = '$fragment' not supported for this URL.", 400);
  			}
  		}
  		$response->setBody(Convert::raw2json($responseParts));
  		$response->addHeader('Content-Type', 'text/json');
  
  		return $response;
  	}
  
  	/**
  	 * @param String   $fragment
  	 * @param Callable $callback
  	 */
  	public function setCallback($fragment, $callback) {
  		$this->callbacks[$fragment] = $callback;
  	}
  
  	/**
  	 * Set up fragment overriding - will completely replace the incoming fragments.
  	 *
  	 * @param array $fragments Fragments to insert.
  	 */
  	public function setFragmentOverride($fragments) {
  		if (!is_array($fragments)) throw new InvalidArgumentException();
  
  		$this->fragmentOverride = $fragments;
  
  		return $this;
  	}
  
  	/**
  	 * @return array
  	 */
  	public function getFragmentOverride() {
  		return $this->fragmentOverride;
  	}
  }