Skip to content

Commit

Permalink
BUGFIX Respecting server-overrides on X-Pjax responses during ajax re…
Browse files Browse the repository at this point in the history
…directs. Fixes GridFieldDetailForm redirect after delete, e.g. in ModelAdmin. Partially reverts 8b4b896. Closes pull request #488
  • Loading branch information
chillu committed May 29, 2012
1 parent 909c5bd commit 5b03f49
Show file tree
Hide file tree
Showing 5 changed files with 21 additions and 17 deletions.
2 changes: 2 additions & 0 deletions admin/code/LeftAndMain.php
Expand Up @@ -351,6 +351,8 @@ function handleRequest(SS_HTTPRequest $request, DataModel $model = null) {
function redirect($url, $code=302) { function redirect($url, $code=302) {
if($this->request->isAjax()) { if($this->request->isAjax()) {
$this->response->addHeader('X-ControllerURL', $url); $this->response->addHeader('X-ControllerURL', $url);
if($header = $this->request->getHeader('X-Pjax')) $this->response->addHeader('X-Pjax', $header);
if($header = $this->request->getHeader('X-Pjax-Selector')) $this->response->addHeader('X-Pjax-Selector', $header);
return ''; // Actual response will be re-requested by client return ''; // Actual response will be re-requested by client
} else { } else {
parent::redirect($url, $code); parent::redirect($url, $code);
Expand Down
16 changes: 6 additions & 10 deletions admin/javascript/LeftAndMain.Content.js
Expand Up @@ -80,18 +80,14 @@
// as automatic browser ajax response redirects seem to discard the hash/fragment. // as automatic browser ajax response redirects seem to discard the hash/fragment.
formData.push({name: 'BackURL', value:History.getPageUrl()}); formData.push({name: 'BackURL', value:History.getPageUrl()});


// Some action buttons are redirecting to content areas as oposed to reloading the form. // Standard Pjax behaviour is to replace the submitted form with new content.
// They will have pjax-content class on them. // The returned view isn't always decided upon when the request
var pjaxType = 'CurrentForm', pjaxSelector = '.cms-edit-form'; // is fired, so the server might decide to change it based on its own logic,
if ($(button).hasClass('pjax-content')) { // sending back different `X-Pjax` headers and content
pjaxType = 'Content';
pjaxSelector = '.cms-content';
}

jQuery.ajax(jQuery.extend({ jQuery.ajax(jQuery.extend({
headers: { headers: {
"X-Pjax" : pjaxType, "X-Pjax" : "CurrentForm",
'X-Pjax-Selector': pjaxSelector 'X-Pjax-Selector': '.cms-edit-form'
}, },
url: form.attr('action'), url: form.attr('action'),
data: formData, data: formData,
Expand Down
13 changes: 8 additions & 5 deletions admin/javascript/LeftAndMain.js
Expand Up @@ -36,13 +36,13 @@ jQuery.noConflict();
$(document).ajaxComplete(function(e, xhr, settings) { $(document).ajaxComplete(function(e, xhr, settings) {
// Simulates a redirect on an ajax response. // Simulates a redirect on an ajax response.
if(window.History.enabled) { if(window.History.enabled) {
var url = xhr.getResponseHeader('X-ControllerURL'); var url = xhr.getResponseHeader('X-ControllerURL'), opts, requestHeaders = settings.headers;
// Normalize trailing slashes in URL to work around routing weirdnesses in SS_HTTPRequest. // Normalize trailing slashes in URL to work around routing weirdnesses in SS_HTTPRequest.
var isSame = (url && History.getPageUrl().replace(/\/+$/, '') == url.replace(/\/+$/, '')); var isSame = (url && History.getPageUrl().replace(/\/+$/, '') == url.replace(/\/+$/, ''));
if(url && !isSame) { if(url && !isSame) {
var opts = { opts = {
pjax: settings.headers ? settings.headers['X-Pjax'] : null, pjax: xhr.getResponseHeader('X-Pjax') ? xhr.getResponseHeader('X-Pjax') : settings.headers['X-Pjax'],
selector: settings.headers ? settings.headers['X-Pjax-Selector'] : null selector: xhr.getResponseHeader('X-Pjax-Selector') ? xhr.getResponseHeader('X-Pjax-Selector') : settings.headers['X-Pjax-Selector']
}; };
window.History.pushState(opts, '', url); window.History.pushState(opts, '', url);
} }
Expand Down Expand Up @@ -216,11 +216,14 @@ jQuery.noConflict();
state: state, element: contentEl state: state, element: contentEl
}); });


// Set Pjax headers, which can declare a preference for the returned view.
// The actually returned view isn't always decided upon when the request
// is fired, so the server might decide to change it based on its own logic.
var headers = {}; var headers = {};
if(state.data.pjax) { if(state.data.pjax) {
headers['X-Pjax'] = state.data.pjax; headers['X-Pjax'] = state.data.pjax;
} else { } else {
// Replace full RHS content area // Standard Pjax behaviour is to replace right content area
headers["X-Pjax"] = 'Content'; headers["X-Pjax"] = 'Content';
} }
headers['X-Pjax-Selector'] = selector; headers['X-Pjax-Selector'] = selector;
Expand Down
3 changes: 3 additions & 0 deletions docs/en/reference/cms-architecture.md
Expand Up @@ -183,6 +183,9 @@ Within the PHP logic, the `[api:PjaxResponseNegotiator]` class determines which
Through a custom `X-Pjax` HTTP header, the client can declare which view he's expecting, Through a custom `X-Pjax` HTTP header, the client can declare which view he's expecting,
through identifiers like `CurrentForm` or `Content` (see `[api:LeftAndMain->getResponseNegotiator()]`). through identifiers like `CurrentForm` or `Content` (see `[api:LeftAndMain->getResponseNegotiator()]`).
These identifiers are passed to `loadPanel()` via the `pjax` data option. These identifiers are passed to `loadPanel()` via the `pjax` data option.
Keep in mind that the returned view isn't always decided upon when the Ajax request
is fired, so the server might decide to change it based on its own logic,
sending back different `X-Pjax` headers and content.


## Ajax Redirects ## Ajax Redirects


Expand Down
4 changes: 2 additions & 2 deletions forms/gridfield/GridFieldDetailForm.php
Expand Up @@ -291,9 +291,8 @@ function ItemEditForm() {
if($this->record->ID !== 0) { if($this->record->ID !== 0) {
$actions->push(FormAction::create('doSave', _t('GridFieldDetailForm.Save', 'Save')) $actions->push(FormAction::create('doSave', _t('GridFieldDetailForm.Save', 'Save'))
->setUseButtonTag(true)->addExtraClass('ss-ui-action-constructive')->setAttribute('data-icon', 'accept')); ->setUseButtonTag(true)->addExtraClass('ss-ui-action-constructive')->setAttribute('data-icon', 'accept'));
// The delete action will redirect, hence pjax-content class.
$actions->push(FormAction::create('doDelete', _t('GridFieldDetailForm.Delete', 'Delete')) $actions->push(FormAction::create('doDelete', _t('GridFieldDetailForm.Delete', 'Delete'))
->addExtraClass('ss-ui-action-destructive')->addExtraClass('pjax-content')); ->addExtraClass('ss-ui-action-destructive'));
}else{ // adding new record }else{ // adding new record
//Change the Save label to 'Create' //Change the Save label to 'Create'
$actions->push(FormAction::create('doSave', _t('GridFieldDetailForm.Create', 'Create')) $actions->push(FormAction::create('doSave', _t('GridFieldDetailForm.Create', 'Create'))
Expand Down Expand Up @@ -405,6 +404,7 @@ function doDelete($data, $form) {
//when an item is deleted, redirect to the revelant admin section without the action parameter //when an item is deleted, redirect to the revelant admin section without the action parameter
$controller = Controller::curr(); $controller = Controller::curr();
$noActionURL = $controller->removeAction($data['url']); $noActionURL = $controller->removeAction($data['url']);
$controller->getRequest()->addHeader('X-Pjax', 'Content'); // Force a content refresh


return $controller->redirect($noActionURL, 302); //redirect back to admin section return $controller->redirect($noActionURL, 302); //redirect back to admin section
} }
Expand Down

0 comments on commit 5b03f49

Please sign in to comment.