diff --git a/admin/code/CMSForm.php b/admin/code/CMSForm.php deleted file mode 100644 index 563a2d821..000000000 --- a/admin/code/CMSForm.php +++ /dev/null @@ -1,52 +0,0 @@ -getRequest(); - $negotiator = $this->getResponseNegotiator(); - - if($request->isAjax() && $negotiator) { - $this->setupFormErrors(); - $result = $this->forTemplate(); - - return $negotiator->respond($request, array( - 'CurrentForm' => function() use($result) { - return $result; - } - )); - } else { - return parent::getValidationErrorResponse(); - } - } - - /** - * Sets the response negotiator - * @param ResponseNegotiator $negotiator The response negotiator to use - * @return Form The current form - */ - public function setResponseNegotiator($negotiator) { - $this->responseNegotiator = $negotiator; - return $this; - } - - /** - * Gets the current response negotiator - * @return ResponseNegotiator|null - */ - public function getResponseNegotiator() { - return $this->responseNegotiator; - } - -} diff --git a/admin/code/LeftAndMain.php b/admin/code/LeftAndMain.php index a64dac490..f20e5aceb 100644 --- a/admin/code/LeftAndMain.php +++ b/admin/code/LeftAndMain.php @@ -13,6 +13,8 @@ use SilverStripe\Forms\Schema\FormSchema; * * This is essentially an abstract class which should be subclassed. * See {@link CMSMain} for a good example. + * + * @property FormSchema $schema */ class LeftAndMain extends Controller implements PermissionProvider { @@ -197,7 +199,7 @@ class LeftAndMain extends Controller implements PermissionProvider { } // Make sure it's an AJAX GET request with a valid "X-Formschema-Request" header value. - if (!$req->isAjax() || !$req->isGET() || !count($schemaParts)) { + if (!$req->isGET() || !count($schemaParts)) { throw new SS_HTTPResponse_Exception( 'Invalid request. Check you\'ve set a "X-Formschema-Request" header with "schema" or "state" values.', 400 @@ -1312,14 +1314,27 @@ class LeftAndMain extends Controller implements PermissionProvider { $actionsFlattened = $actions->dataFields(); if($actionsFlattened) foreach($actionsFlattened as $action) $action->setUseButtonTag(true); - $form = CMSForm::create( + $negotiator = $this->getResponseNegotiator(); + $form = Form::create( $this, "EditForm", $fields, $actions )->setHTMLID('Form_EditForm'); - $form->setResponseNegotiator($this->getResponseNegotiator()); $form->addExtraClass('cms-edit-form'); $form->loadDataFrom($record); $form->setTemplate($this->getTemplatesWithSuffix('_EditForm')); $form->setAttribute('data-pjax-fragment', 'CurrentForm'); + $form->setValidationResponseCallback(function() use ($negotiator, $form) { + $request = $this->getRequest(); + if($request->isAjax() && $negotiator) { + $form->setupFormErrors(); + $result = $form->forTemplate(); + + return $negotiator->respond($request, array( + 'CurrentForm' => function() use($result) { + return $result; + } + )); + } + }); // Announce the capability so the frontend can decide whether to allow preview or not. if(in_array('CMSPreviewable', class_implements($record))) { @@ -1368,7 +1383,7 @@ class LeftAndMain extends Controller implements PermissionProvider { * @return Form */ public function EmptyForm() { - $form = CMSForm::create( + $form = Form::create( $this, "EditForm", new FieldList( @@ -1387,7 +1402,6 @@ class LeftAndMain extends Controller implements PermissionProvider { ), new FieldList() )->setHTMLID('Form_EditForm'); - $form->setResponseNegotiator($this->getResponseNegotiator()); $form->unsetValidator(); $form->addExtraClass('cms-edit-form'); $form->addExtraClass('root-form'); diff --git a/admin/code/ModelAdmin.php b/admin/code/ModelAdmin.php index cc41d7bce..3ff8ef89c 100644 --- a/admin/code/ModelAdmin.php +++ b/admin/code/ModelAdmin.php @@ -138,13 +138,12 @@ abstract class ModelAdmin extends LeftAndMain { $listField->getConfig()->getComponentByType('GridFieldDetailForm')->setValidator($detailValidator); } - $form = CMSForm::create( + $form = Form::create( $this, 'EditForm', new FieldList($listField), new FieldList() )->setHTMLID('Form_EditForm'); - $form->setResponseNegotiator($this->getResponseNegotiator()); $form->addExtraClass('cms-edit-form cms-panel-padded center'); $form->setTemplate($this->getTemplatesWithSuffix('_EditForm')); $editFormAction = Controller::join_links($this->Link($this->sanitiseClassName($this->modelClass)), 'EditForm'); diff --git a/admin/code/SecurityAdmin.php b/admin/code/SecurityAdmin.php index cfbf4fe3d..923f9d4f9 100755 --- a/admin/code/SecurityAdmin.php +++ b/admin/code/SecurityAdmin.php @@ -177,13 +177,12 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider { $actions = new FieldList(); - $form = CMSForm::create( + $form = Form::create( $this, 'EditForm', $fields, $actions )->setHTMLID('Form_EditForm'); - $form->setResponseNegotiator($this->getResponseNegotiator()); $form->addExtraClass('cms-edit-form'); $form->setTemplate($this->getTemplatesWithSuffix('_EditForm')); // Tab nav in CMS is rendered through separate template diff --git a/forms/Form.php b/forms/Form.php index 112d8b3b7..0523c14a4 100644 --- a/forms/Form.php +++ b/forms/Form.php @@ -79,6 +79,11 @@ class Form extends RequestHandler { */ protected $validator; + /** + * @var callable {@see setValidationResponseCallback()} + */ + protected $validationResponseCallback; + /** * @var string */ @@ -479,16 +484,45 @@ class Form extends RequestHandler { ); } + /** + * @return callable + */ + public function getValidationResponseCallback() { + return $this->validationResponseCallback; + } + + /** + * Overrules validation error behaviour in {@link httpSubmission()} + * when validation has failed. Useful for optional handling of a certain accepted content type. + * + * The callback can opt out of handling specific responses by returning NULL, + * in which case the default form behaviour will kick in. + * + * @param $callback + * @return self + */ + public function setValidationResponseCallback($callback) { + $this->validationResponseCallback = $callback; + + return $this; + } + /** * Returns the appropriate response up the controller chain * if {@link validate()} fails (which is checked prior to executing any form actions). * By default, returns different views for ajax/non-ajax request, and * handles 'application/json' requests with a JSON object containing the error messages. - * Behaviour can be influenced by setting {@link $redirectToFormOnValidationError}. + * Behaviour can be influenced by setting {@link $redirectToFormOnValidationError}, + * and can be overruled by setting {@link $validationResponseCallback}. * * @return SS_HTTPResponse|string */ protected function getValidationErrorResponse() { + $callback = $this->getValidationResponseCallback(); + if($callback && $callbackResponse = $callback()) { + return $callbackResponse; + } + $request = $this->getRequest(); if($request->isAjax()) { // Special case for legacy Validator.js implementation