2012-03-24 03:19:02 +01:00
|
|
|
<?php
|
2016-08-19 00:51:35 +02:00
|
|
|
|
|
|
|
namespace SilverStripe\Control;
|
|
|
|
|
|
|
|
use SilverStripe\Core\Convert;
|
|
|
|
use InvalidArgumentException;
|
|
|
|
|
2012-03-24 03:19:02 +01:00
|
|
|
/**
|
2014-08-15 08:53:05 +02:00
|
|
|
* Handle the X-Pjax header that AJAX responses may provide, returning the
|
|
|
|
* fragment, or, in the case of non-AJAX form submissions, redirecting back
|
2013-05-20 12:18:07 +02:00
|
|
|
* to the submitter.
|
|
|
|
*
|
2014-08-15 08:53:05 +02:00
|
|
|
* X-Pjax ensures that users won't end up seeing the unstyled form HTML in
|
2013-05-20 12:18:07 +02:00
|
|
|
* their browser.
|
|
|
|
*
|
|
|
|
* If a JS error prevents the Ajax overriding of form submissions from happening.
|
2012-03-24 03:19:02 +01:00
|
|
|
*
|
|
|
|
* It also provides better non-JS operation.
|
2014-08-15 08:53:05 +02:00
|
|
|
*
|
2012-03-24 03:19:02 +01:00
|
|
|
* Caution: This API is volatile, and might eventually be replaced by a generic
|
|
|
|
* action helper system for controllers.
|
|
|
|
*/
|
|
|
|
class PjaxResponseNegotiator {
|
|
|
|
|
|
|
|
/**
|
2016-08-19 00:51:35 +02:00
|
|
|
* See {@link respond()}
|
|
|
|
*
|
|
|
|
* @var array
|
2012-03-24 03:19:02 +01:00
|
|
|
*/
|
2016-08-19 00:51:35 +02:00
|
|
|
protected $callbacks = array();
|
2012-03-24 03:19:02 +01:00
|
|
|
|
2012-06-13 00:27:03 +02:00
|
|
|
protected $response = null;
|
|
|
|
|
2012-06-07 00:26:27 +02:00
|
|
|
/**
|
|
|
|
* Overriden fragments (if any). Otherwise uses fragments from the request.
|
|
|
|
*/
|
|
|
|
protected $fragmentOverride = null;
|
|
|
|
|
2012-03-24 03:19:02 +01:00
|
|
|
/**
|
2016-08-19 00:51:35 +02:00
|
|
|
* @param array $callbacks
|
2016-09-09 08:43:05 +02:00
|
|
|
* @param HTTPResponse $response An existing response to reuse (optional)
|
2012-03-24 03:19:02 +01:00
|
|
|
*/
|
2012-09-19 12:07:39 +02:00
|
|
|
public function __construct($callbacks = array(), $response = null) {
|
2014-08-15 08:53:05 +02:00
|
|
|
$this->callbacks = $callbacks;
|
2012-06-13 00:27:03 +02:00
|
|
|
$this->response = $response;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getResponse() {
|
2016-08-19 00:51:35 +02:00
|
|
|
if(!$this->response) {
|
2016-09-09 08:43:05 +02:00
|
|
|
$this->response = new HTTPResponse();
|
2016-08-19 00:51:35 +02:00
|
|
|
}
|
2012-06-13 00:27:03 +02:00
|
|
|
return $this->response;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setResponse($response) {
|
|
|
|
$this->response = $response;
|
2012-03-24 03:19:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-08-15 08:53:05 +02:00
|
|
|
* Out of the box, the handler "CurrentForm" value, which will return the rendered form.
|
2012-03-24 03:19:02 +01:00
|
|
|
* Non-Ajax calls will redirect back.
|
2014-08-15 08:53:05 +02:00
|
|
|
*
|
2016-09-09 08:43:05 +02:00
|
|
|
* @param HTTPRequest $request
|
2012-03-24 03:19:02 +01:00
|
|
|
* @param array $extraCallbacks List of anonymous functions or callables returning either a string
|
2016-09-09 08:43:05 +02:00
|
|
|
* or HTTPResponse, keyed by their fragment identifier. The 'default' key can
|
2012-03-24 03:19:02 +01:00
|
|
|
* be used as a fallback for non-ajax responses.
|
2016-09-09 08:43:05 +02:00
|
|
|
* @return HTTPResponse
|
|
|
|
* @throws HTTPResponse_Exception
|
2012-03-24 03:19:02 +01:00
|
|
|
*/
|
2016-09-09 08:43:05 +02:00
|
|
|
public function respond(HTTPRequest $request, $extraCallbacks = array()) {
|
2012-03-24 03:19:02 +01:00
|
|
|
// Prepare the default options and combine with the others
|
2012-05-30 11:53:54 +02:00
|
|
|
$callbacks = array_merge($this->callbacks, $extraCallbacks);
|
2012-06-13 00:27:03 +02:00
|
|
|
$response = $this->getResponse();
|
2014-08-15 08:53:05 +02:00
|
|
|
|
2012-04-18 21:15:31 +02:00
|
|
|
$responseParts = array();
|
2012-06-07 00:26:27 +02:00
|
|
|
|
|
|
|
if (isset($this->fragmentOverride)) {
|
|
|
|
$fragments = $this->fragmentOverride;
|
|
|
|
} elseif ($fragmentStr = $request->getHeader('X-Pjax')) {
|
2012-05-30 11:53:54 +02:00
|
|
|
$fragments = explode(',', $fragmentStr);
|
2012-03-24 03:19:02 +01:00
|
|
|
} else {
|
2012-09-26 23:34:00 +02:00
|
|
|
if($request->isAjax()) {
|
2016-09-09 08:43:05 +02:00
|
|
|
throw new HTTPResponse_Exception("Ajax requests to this URL require an X-Pjax header.", 400);
|
2016-08-19 00:51:35 +02:00
|
|
|
} elseif (empty($callbacks['default'])) {
|
2016-09-09 08:43:05 +02:00
|
|
|
throw new HTTPResponse_Exception("Missing default response handler for this URL", 400);
|
2012-09-26 23:34:00 +02:00
|
|
|
}
|
2012-04-18 21:15:31 +02:00
|
|
|
$response->setBody(call_user_func($callbacks['default']));
|
2012-06-07 00:26:27 +02:00
|
|
|
return $response;
|
2012-03-24 03:19:02 +01:00
|
|
|
}
|
2012-06-07 00:26:27 +02:00
|
|
|
|
|
|
|
// Execute the fragment callbacks and build the response.
|
|
|
|
foreach($fragments as $fragment) {
|
|
|
|
if(isset($callbacks[$fragment])) {
|
2013-03-12 00:17:20 +01:00
|
|
|
$res = call_user_func($callbacks[$fragment]);
|
2015-10-06 07:28:08 +02:00
|
|
|
$responseParts[$fragment] = $res ? (string) $res : $res;
|
2012-06-07 00:26:27 +02:00
|
|
|
} else {
|
2016-09-09 08:43:05 +02:00
|
|
|
throw new HTTPResponse_Exception("X-Pjax = '$fragment' not supported for this URL.", 400);
|
2012-06-07 00:26:27 +02:00
|
|
|
}
|
2012-03-24 03:19:02 +01:00
|
|
|
}
|
2012-06-07 00:26:27 +02:00
|
|
|
$response->setBody(Convert::raw2json($responseParts));
|
|
|
|
$response->addHeader('Content-Type', 'text/json');
|
|
|
|
|
2012-04-18 21:15:31 +02:00
|
|
|
return $response;
|
2012-03-24 03:19:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-10-06 07:28:04 +02:00
|
|
|
* @param string $fragment
|
2012-03-24 03:19:02 +01:00
|
|
|
* @param Callable $callback
|
|
|
|
*/
|
|
|
|
public function setCallback($fragment, $callback) {
|
|
|
|
$this->callbacks[$fragment] = $callback;
|
|
|
|
}
|
2012-06-07 00:26:27 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Set up fragment overriding - will completely replace the incoming fragments.
|
|
|
|
*
|
|
|
|
* @param array $fragments Fragments to insert.
|
2016-08-19 00:51:35 +02:00
|
|
|
* @return $this
|
2012-06-07 00:26:27 +02:00
|
|
|
*/
|
2012-06-13 10:28:25 +02:00
|
|
|
public function setFragmentOverride($fragments) {
|
2016-08-19 00:51:35 +02:00
|
|
|
if (!is_array($fragments)) {
|
|
|
|
throw new InvalidArgumentException("fragments must be an array");
|
|
|
|
}
|
2012-06-07 00:26:27 +02:00
|
|
|
|
|
|
|
$this->fragmentOverride = $fragments;
|
|
|
|
return $this;
|
|
|
|
}
|
2012-06-13 10:28:25 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getFragmentOverride() {
|
|
|
|
return $this->fragmentOverride;
|
|
|
|
}
|
2012-03-24 04:04:52 +01:00
|
|
|
}
|