Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

API New CMSForm class to allow validation responses in CMS (fixes #1777)

Thanks to @willmorgan for getting this discussion started
(see #1814).
  • Loading branch information...
commit bfff11eb9c45d6ceed8a399b82a10449df9bc748 1 parent 6ca27cd
@chillu chillu authored
View
40 admin/code/CMSForm.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * Deals with special form handling in CMS, mainly around {@link PjaxResponseNegotiator}
+ */
+class CMSForm extends Form {
+
+ /**
+ * Route validation error responses through response negotiator,
+ * so they return the correct markup as expected by the requesting client.
+ */
+ protected function getValidationErrorResponse() {
+ $request = $this->getRequest();
+ $negotiator = $this->getResponseNegotiator();
+ if($request->isAjax() && $negotiator) {
+ $negotiator->setResponse(new SS_HTTPResponse($this));
+ return $negotiator->respond($request);
+ } 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;
+ }
+
+}
View
10 admin/code/LeftAndMain.php
@@ -1211,7 +1211,10 @@ public function getEditForm($id = null, $fields = null) {
$actionsFlattened = $actions->dataFields();
if($actionsFlattened) foreach($actionsFlattened as $action) $action->setUseButtonTag(true);
- $form = new Form($this, "EditForm", $fields, $actions);
+ $form = CMSForm::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'));
@@ -1264,7 +1267,7 @@ public function getEditForm($id = null, $fields = null) {
* @return Form
*/
public function EmptyForm() {
- $form = new Form(
+ $form = CMSForm::create(
$this,
"EditForm",
new FieldList(
@@ -1282,7 +1285,8 @@ public function EmptyForm() {
// )
),
new FieldList()
- );
+ )->setHTMLID('Form_EditForm');
+ $form->setResponseNegotiator($this->getResponseNegotiator());
$form->unsetValidator();
$form->addExtraClass('cms-edit-form');
$form->addExtraClass('root-form');
View
5 admin/code/ModelAdmin.php
@@ -155,12 +155,13 @@ public function getEditForm($id = null, $fields = null) {
$listField->getConfig()->getComponentByType('GridFieldDetailForm')->setValidator($detailValidator);
}
- $form = new Form(
+ $form = CMSForm::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');
View
5 admin/code/SecurityAdmin.php
@@ -154,12 +154,13 @@ public function getEditForm($id = null, $fields = null) {
$actions = new FieldList();
- $form = new Form(
+ $form = CMSForm::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
View
3  docs/en/changelogs/3.1.0.md
@@ -475,3 +475,6 @@ you can enable those warnings and future-proof your code already.
by `updateCMSFields`. See the [DataExtension Reference](/reference/dataextension) for more information.
* Magic quotes is now deprecated. Will trigger user_error on live sites, as well as an error on new installs
* Support for Apache 1.x is removed.
+ * Forms created in the CMS should now be instances of a new `CMSForm` class,
+ and have the CMS controller's response negotiator passed into them.
+ Example: `$form = new CMSForm(...); $form->setResponseNegotiator($this->getResponseNegotiator());`
View
18 docs/en/reference/cms-architecture.md
@@ -106,12 +106,15 @@ In order to set the correct layout classes, we also need a custom template.
To obey the inheritance chain, we use `$this->getTemplatesWithSuffix('_EditForm')` for
selecting the most specific template (so `MyAdmin_EditForm.ss`, if it exists).
+The form should be of type `CMSForm` rather than `Form`, since it allows the use
+of a `PjaxResponseNegotiator` to handle its display.
+
Basic example form in a CMS controller subclass:
:::php
class MyAdmin extends LeftAndMain {
function getEditForm() {
- $form = new Form(
+ return CMSForm::create(
$this,
'EditForm',
new FieldSet(
@@ -125,11 +128,14 @@ Basic example form in a CMS controller subclass:
new FieldSet(
FormAction::create('doSubmit')
)
- );
- // Required for correct CMS layout
- $form->addExtraClass('cms-edit-form');
- $form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
- return $form;
+ )
+ // JS and CSS use this identifier
+ ->setHTMLID('Form_EditForm')
+ // Render correct responses on validation errors
+ ->setResponseNegotiator($this->getResponseNegotiator());
+ // Required for correct CMS layout
+ ->addExtraClass('cms-edit-form')
+ ->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
}
}
View
46 forms/Form.php
@@ -344,9 +344,36 @@ public function httpSubmission($request) {
// Validate the form
if(!$this->validate()) {
- if(Director::is_ajax()) {
- // Special case for legacy Validator.js implementation (assumes eval'ed javascript collected through
- // FormResponse)
+ return $this->getValidationErrorResponse();
+ }
+
+ // First, try a handler method on the controller (has been checked for allowed_actions above already)
+ if($this->controller->hasMethod($funcName)) {
+ return $this->controller->$funcName($vars, $this, $request);
+ // Otherwise, try a handler method on the form object.
+ } elseif($this->hasMethod($funcName)) {
+ return $this->$funcName($vars, $this, $request);
+ } elseif($field = $this->checkFieldsForAction($this->Fields(), $funcName)) {
+ return $field->$funcName($vars, $this, $request);
+ }
+
+ return $this->httpError(404);
+ }
+
+ /**
+ * 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 'appliction/json' requests with a JSON object containing the error messages.
+ * Behaviour can be influenced by setting {@link $redirectToFormOnValidationError}.
+ *
+ * @return SS_HTTPResponse|string
+ */
+ protected function getValidationErrorResponse() {
+ $request = $this->getRequest();
+ if($request->isAjax()) {
+ // Special case for legacy Validator.js implementation
+ // (assumes eval'ed javascript collected through FormResponse)
$acceptType = $request->getHeader('Accept');
if(strpos($acceptType, 'application/json') !== FALSE) {
// Send validation errors back as JSON with a flag at the start
@@ -372,19 +399,6 @@ public function httpSubmission($request) {
}
return $this->controller->redirectBack();
}
- }
-
- // First, try a handler method on the controller (has been checked for allowed_actions above already)
- if($this->controller->hasMethod($funcName)) {
- return $this->controller->$funcName($vars, $this, $request);
- // Otherwise, try a handler method on the form object.
- } elseif($this->hasMethod($funcName)) {
- return $this->$funcName($vars, $this, $request);
- } elseif($field = $this->checkFieldsForAction($this->Fields(), $funcName)) {
- return $field->$funcName($vars, $this, $request);
- }
-
- return $this->httpError(404);
}
/**
Please sign in to comment.
Something went wrong with that request. Please try again.