Merge pull request #6119 from open-sausages/pulls/4.0/assets-custom-actions

API Update form schema to be more responsive to dynamic form changes
This commit is contained in:
Damian Mooyman 2016-10-07 17:08:58 +13:00 committed by GitHub
commit 89c88f0f60
13 changed files with 2139 additions and 2050 deletions

View File

@ -695,12 +695,15 @@ class Controller extends RequestHandler implements TemplateGlobalProvider {
* *
* Caution: All parameters are expected to be URI-encoded already. * Caution: All parameters are expected to be URI-encoded already.
* *
* @param string * @param string|array $arg,.. One or more link segments, or list of link segments as an array
*
* @return string * @return string
*/ */
public static function join_links() { public static function join_links($arg = null) {
if (func_num_args() === 1 && is_array($arg)) {
$args = $arg;
} else {
$args = func_get_args(); $args = func_get_args();
}
$result = ""; $result = "";
$queryargs = array(); $queryargs = array();
$fragmentIdentifier = null; $fragmentIdentifier = null;

View File

@ -1424,7 +1424,7 @@ class FormField extends RequestHandler {
* @return array * @return array
*/ */
public function getSchemaData() { public function getSchemaData() {
return array_merge($this->getSchemaDataDefaults(), $this->schemaData); return array_replace_recursive($this->getSchemaDataDefaults(), $this->schemaData);
} }
/** /**

View File

@ -17,18 +17,16 @@ class FormSchema {
* Gets the schema for this form as a nested array. * Gets the schema for this form as a nested array.
* *
* @param Form $form * @param Form $form
* @param string $schemaLink Link to get this schema
* @return array * @return array
*/ */
public function getSchema(Form $form) { public function getSchema(Form $form, $schemaLink) {
$request = $form->getController()->getRequest();
$schema = [ $schema = [
'name' => $form->getName(), 'name' => $form->getName(),
'id' => $form->FormName(), 'id' => $form->FormName(),
'action' => $form->FormAction(), 'action' => $form->FormAction(),
'method' => $form->FormMethod(), 'method' => $form->FormMethod(),
// @todo Not really reliable. Refactor into action on $this->Link('schema') 'schema_url' => $schemaLink,
'schema_url' => $request->getURL(),
'attributes' => $form->getAttributes(), 'attributes' => $form->getAttributes(),
'data' => [], 'data' => [],
'fields' => [], 'fields' => [],
@ -62,7 +60,10 @@ class FormSchema {
]; ];
// flattened nested fields are returned, rather than only top level fields. // flattened nested fields are returned, rather than only top level fields.
$state['fields'] = $this->getFieldStates($form->Fields()); $state['fields'] = array_merge(
$this->getFieldStates($form->Fields()),
$this->getFieldStates($form->Actions())
);
if($form->Message()) { if($form->Message()) {
$state['messages'][] = [ $state['messages'][] = [
@ -76,6 +77,7 @@ class FormSchema {
protected function getFieldStates($fields) { protected function getFieldStates($fields) {
$states = []; $states = [];
/** @var FormField $field */
foreach ($fields as $field) { foreach ($fields as $field) {
$states[] = $field->getSchemaState(); $states[] = $field->getSchemaState();

View File

@ -5,6 +5,7 @@ namespace SilverStripe\ORM\Versioning;
use SilverStripe\Admin\CMSPreviewable; use SilverStripe\Admin\CMSPreviewable;
use SilverStripe\Assets\Thumbnail; use SilverStripe\Assets\Thumbnail;
use SilverStripe\Control\Controller; use SilverStripe\Control\Controller;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataList; use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\ManyManyList; use SilverStripe\ORM\ManyManyList;
@ -109,8 +110,8 @@ class ChangeSetItem extends DataObject implements Thumbnail {
/** /**
* Get the type of change: none, created, deleted, modified, manymany * Get the type of change: none, created, deleted, modified, manymany
*
* @return string * @return string
* @throws UnexpectedDataException
*/ */
public function getChangeType() { public function getChangeType() {
if(!class_exists($this->ObjectClass)) { if(!class_exists($this->ObjectClass)) {
@ -146,7 +147,8 @@ class ChangeSetItem extends DataObject implements Thumbnail {
* Find version of this object in the given stage * Find version of this object in the given stage
* *
* @param string $stage * @param string $stage
* @return Versioned|DataObject * @return DataObject|Versioned
* @throws UnexpectedDataException
*/ */
protected function getObjectInStage($stage) { protected function getObjectInStage($stage) {
if(!class_exists($this->ObjectClass)) { if(!class_exists($this->ObjectClass)) {
@ -158,8 +160,8 @@ class ChangeSetItem extends DataObject implements Thumbnail {
/** /**
* Find latest version of this object * Find latest version of this object
* * @return DataObject|Versioned
* @return Versioned|DataObject * @throws UnexpectedDataException
*/ */
protected function getObjectLatestVersion() { protected function getObjectLatestVersion() {
if(!class_exists($this->ObjectClass)) { if(!class_exists($this->ObjectClass)) {
@ -177,15 +179,23 @@ class ChangeSetItem extends DataObject implements Thumbnail {
public function findReferenced() { public function findReferenced() {
if($this->getChangeType() === ChangeSetItem::CHANGE_DELETED) { if($this->getChangeType() === ChangeSetItem::CHANGE_DELETED) {
// If deleted from stage, need to look at live record // If deleted from stage, need to look at live record
return $this->getObjectInStage(Versioned::LIVE)->findOwners(false); $record = $this->getObjectInStage(Versioned::LIVE);
if ($record) {
return $record->findOwners(false);
}
} else { } else {
// If changed on stage, look at owned objects there // If changed on stage, look at owned objects there
return $this->getObjectInStage(Versioned::DRAFT)->findOwned()->filterByCallback(function ($owned) { $record = $this->getObjectInStage(Versioned::DRAFT);
if ($record) {
return $record->findOwned()->filterByCallback(function ($owned) {
/** @var Versioned|DataObject $owned */ /** @var Versioned|DataObject $owned */
return $owned->stagesDiffer(Versioned::DRAFT, Versioned::LIVE); return $owned->stagesDiffer(Versioned::DRAFT, Versioned::LIVE);
}); });
} }
} }
// Empty set
return new ArrayList();
}
/** /**
* Publish this item, then close it. * Publish this item, then close it.

View File

@ -331,24 +331,25 @@ return e?e.id:null}},{key:"componentDidMount",value:function r(){this.fetch()}},
var e=this,t=arguments.length<=0||void 0===arguments[0]||arguments[0],n=arguments.length<=1||void 0===arguments[1]||arguments[1],i=[] var e=this,t=arguments.length<=0||void 0===arguments[0]||arguments[0],n=arguments.length<=1||void 0===arguments[1]||arguments[1],i=[]
return this.state.isFetching===!0?this.formSchemaPromise:(t===!0&&i.push("schema"),n===!0&&i.push("state"),this.formSchemaPromise=(0,O["default"])(this.props.schemaUrl,{headers:{"X-FormSchema-Request":i.join() return this.state.isFetching===!0?this.formSchemaPromise:(t===!0&&i.push("schema"),n===!0&&i.push("state"),this.formSchemaPromise=(0,O["default"])(this.props.schemaUrl,{headers:{"X-FormSchema-Request":i.join()
},credentials:"same-origin"}).then(function(e){return e.json()}).then(function(t){var n=c({},{id:t.id,schema:t.schema}),i=c({},t.state) },credentials:"same-origin"}).then(function(e){return e.json()}).then(function(t){var n=c({},{id:t.id,schema:t.schema}),i=c({},t.state)
if("undefined"!=typeof n.id){var r={SecurityID:e.props.config.SecurityID} "undefined"!=typeof n.id&&!function(){var t={SecurityID:e.props.config.SecurityID}
n.schema.actions.length>0&&(r[n.schema.actions[0].name]=1),e.submitApi=k["default"].createEndpointFetcher({url:n.schema.attributes.action,method:n.schema.attributes.method,defaultData:r}),e.props.schemaActions.setSchema(n) e.submitApi=function(){var i=k["default"].createEndpointFetcher({url:n.schema.attributes.action,method:n.schema.attributes.method,defaultData:t})
return i.apply(void 0,arguments).then(function(t){if(t.schema){var n=c({},{id:t.id,schema:t.schema})
e.props.schemaActions.setSchema(n)}return t})},e.props.schemaActions.setSchema(n)}(),"undefined"!=typeof i.id&&e.props.formActions.addForm(i)}),this.formSchemaPromise)}},{key:"handleFieldUpdate",value:function p(e,t,n){
"function"==typeof n?n(this.getFormId(),this.props.formActions.updateField):this.props.formActions.updateField(this.getFormId(),t)}},{key:"handleAction",value:function m(e,t){this.props.formActions.setSubmitAction(this.getFormId(),t),
"function"==typeof this.props.handleAction&&this.props.handleAction(e,t,this.getFieldValues())}},{key:"handleSubmit",value:function g(e){var t=this,n=this.getFieldValues(),i=function r(){return t.props.formActions.submitForm(t.submitApi,t.getFormId(),n)
}"undefined"!=typeof i.id&&e.props.formActions.addForm(i)}),this.formSchemaPromise)}},{key:"handleFieldUpdate",value:function p(e,t,n){"function"==typeof n?n(this.getFormId(),this.props.formActions.updateField):this.props.formActions.updateField(this.getFormId(),t) }
return"undefined"!=typeof this.props.handleSubmit?this.props.handleSubmit(e,n,i):(e.preventDefault(),i())}},{key:"getFieldValues",value:function v(){var e=this,t=this.props.schemas[this.props.schemaUrl],n=t.state?t.state.fields:t.schema.fields,i=this.getSubmitAction(),r={}
}},{key:"handleAction",value:function m(e,t){this.props.formActions.setSubmitAction(this.getFormId(),t),"function"==typeof this.props.handleAction&&this.props.handleAction(e,t,this.getFieldValues())}},{
key:"handleSubmit",value:function g(e){var t=this,n=this.getFieldValues(),i=function r(){return t.props.formActions.submitForm(t.submitApi,t.getFormId(),n)}
return"undefined"!=typeof this.props.handleSubmit?this.props.handleSubmit(e,n,i):(e.preventDefault(),i())}},{key:"getFieldValues",value:function v(){var e=this,t=this.props.schemas[this.props.schemaUrl],n=t.state?t.state.fields:t.schema.fields
return this.props.form[this.getFormId()].fields.reduce(function(t,i){var r=e.findField(n,i.id) return i&&(r[i]=1),this.props.form[this.getFormId()].fields.reduce(function(t,i){var r=e.findField(n,i.id)
return r?c({},t,o({},r.name,i.value)):t},{})}},{key:"findField",value:function y(e,t){var n=null return r?c({},t,o({},r.name,i.value)):t},r)}},{key:"getSubmitAction",value:function y(){return this.props.form[this.getFormId()].submitAction}},{key:"findField",value:function b(e,t){var n=null
if(!e)return n if(!e)return n
n=e.find(function(e){return e.id===t}) n=e.find(function(e){return e.id===t})
var i=!0,r=!1,o=void 0 var i=!0,r=!1,o=void 0
try{for(var a=e[Symbol.iterator](),s;!(i=(s=a.next()).done);i=!0){var l=s.value try{for(var a=e[Symbol.iterator](),s;!(i=(s=a.next()).done);i=!0){var l=s.value
if(n)break if(n)break
n=this.findField(l.children,t)}}catch(u){r=!0,o=u}finally{try{!i&&a["return"]&&a["return"]()}finally{if(r)throw o}}return n}},{key:"buildComponent",value:function b(e){var t=arguments.length<=1||void 0===arguments[1]?{}:arguments[1],n=null!==e.component?x["default"].getComponentByName(e.component):x["default"].getComponentByDataType(e.type) n=this.findField(l.children,t)}}catch(u){r=!0,o=u}finally{try{!i&&a["return"]&&a["return"]()}finally{if(r)throw o}}return n}},{key:"buildComponent",value:function w(e){var t=arguments.length<=1||void 0===arguments[1]?{}:arguments[1],n=null!==e.component?x["default"].getComponentByName(e.component):x["default"].getComponentByDataType(e.type)
if(null===n)return null if(null===n)return null
@ -356,27 +357,27 @@ if(null!==e.component&&void 0===n)throw Error("Component not found in injector:
var i=c({},e,t) var i=c({},e,t)
null===i.value&&delete i.value null===i.value&&delete i.value
var r=this.props.createFn var r=this.props.createFn
return"function"==typeof r?r(n,i):h["default"].createElement(n,c({key:i.id},i))}},{key:"mapFieldsToComponents",value:function w(e){var t=this return"function"==typeof r?r(n,i):h["default"].createElement(n,c({key:i.id},i))}},{key:"mapFieldsToComponents",value:function _(e){var t=this
return e.map(function(e){var n={onChange:t.handleFieldUpdate} return e.map(function(e){var n={onChange:t.handleFieldUpdate}
return e.children&&(n.children=t.mapFieldsToComponents(e.children)),t.buildComponent(e,n)})}},{key:"mapActionsToComponents",value:function _(e){var t=this,n=this.props.form[this.getFormId()] return e.children&&(n.children=t.mapFieldsToComponents(e.children)),t.buildComponent(e,n)})}},{key:"mapActionsToComponents",value:function C(e){var t=this,n=this.props.form[this.getFormId()]
return e.map(function(e){var i=n&&n.submitting&&n.submitAction===e.name,r={handleClick:t.handleAction,loading:i,disabled:i||e.disabled} return e.map(function(e){var i=n&&n.submitting&&n.submitAction===e.name,r={handleClick:t.handleAction,loading:i,disabled:i||e.disabled}
return e.children&&(r.children=t.mapActionsToComponents(e.children)),t.buildComponent(e,r)})}},{key:"mergeFieldData",value:function C(e,t){return"undefined"==typeof t?e:I["default"].recursive(!0,e,{data:t.data, return e.children&&(r.children=t.mapActionsToComponents(e.children)),t.buildComponent(e,r)})}},{key:"mergeFieldData",value:function T(e,t){return"undefined"==typeof t?e:I["default"].recursive(!0,e,{data:t.data,
source:t.source,messages:t.messages,valid:t.valid,value:t.value})}},{key:"removeForm",value:function T(e){this.props.formActions.removeForm(e)}},{key:"getFieldData",value:function P(e,t){var n=this source:t.source,messages:t.messages,valid:t.valid,value:t.value})}},{key:"removeForm",value:function P(e){this.props.formActions.removeForm(e)}},{key:"getFieldData",value:function S(e,t){var n=this
return e&&t&&t.fields?e.map(function(e){var i=t.fields.find(function(t){return t.id===e.id}),r=n.mergeFieldData(e,i) return e&&t&&t.fields?e.map(function(e){var i=t.fields.find(function(t){return t.id===e.id}),r=n.mergeFieldData(e,i)
return e.children&&(r.children=n.getFieldData(e.children,t)),r}):e}},{key:"render",value:function S(){var e=this.getFormId() return e.children?c({},r,{children:n.getFieldData(e.children,t)}):r}):e}},{key:"render",value:function j(){var e=this.getFormId()
if(!e)return null if(!e)return null
var t=this.getFormSchema(),n=this.props.form[e] var t=this.getFormSchema(),n=this.props.form[e]
if(!t||!t.schema)return null if(!t||!t.schema)return null
var i=c({},t.schema.attributes,{className:t.schema.attributes["class"],encType:t.schema.attributes.enctype}) var i=c({},t.schema.attributes,{className:t.schema.attributes["class"],encType:t.schema.attributes.enctype})
delete i["class"],delete i.enctype delete i["class"],delete i.enctype
var r=this.getFieldData(t.schema.fields,n),o={actions:t.schema.actions,attributes:i,componentWillUnmount:this.removeForm,data:t.schema.data,fields:r,formId:e,handleSubmit:this.handleSubmit,mapActionsToComponents:this.mapActionsToComponents, var r=this.getFieldData(t.schema.fields,n),o=this.getFieldData(t.schema.actions,n),a={actions:o,attributes:i,componentWillUnmount:this.removeForm,data:t.schema.data,fields:r,formId:e,handleSubmit:this.handleSubmit,
mapFieldsToComponents:this.mapFieldsToComponents} mapActionsToComponents:this.mapActionsToComponents,mapFieldsToComponents:this.mapFieldsToComponents}
return h["default"].createElement(E["default"],o)}}]),t}(C["default"]) return h["default"].createElement(E["default"],a)}}]),t}(C["default"])
D.propTypes={config:h["default"].PropTypes.object,createFn:h["default"].PropTypes.func,form:h["default"].PropTypes.object.isRequired,formActions:h["default"].PropTypes.object.isRequired,handleSubmit:h["default"].PropTypes.func, D.propTypes={config:h["default"].PropTypes.object,createFn:h["default"].PropTypes.func,form:h["default"].PropTypes.object.isRequired,formActions:h["default"].PropTypes.object.isRequired,handleSubmit:h["default"].PropTypes.func,
handleAction:h["default"].PropTypes.func,schemas:h["default"].PropTypes.object.isRequired,schemaActions:h["default"].PropTypes.object.isRequired,schemaUrl:h["default"].PropTypes.string.isRequired},t["default"]=(0, handleAction:h["default"].PropTypes.func,schemas:h["default"].PropTypes.object.isRequired,schemaActions:h["default"].PropTypes.object.isRequired,schemaUrl:h["default"].PropTypes.string.isRequired},t["default"]=(0,
m.connect)(u,d)(D)},function(e,t){e.exports=ReactRedux},,function(e,t,n){"use strict" m.connect)(u,d)(D)},function(e,t){e.exports=ReactRedux},,function(e,t,n){"use strict"
function i(e){return function(t){t({type:u.ACTION_TYPES.REMOVE_FORM,payload:{formId:e}})}}function r(e,t){return function(n){n({type:u.ACTION_TYPES.UPDATE_FIELD,payload:{formId:e,updates:t}})}}function o(e){ function i(e){return function(t){t({type:u.ACTION_TYPES.REMOVE_FORM,payload:{formId:e}})}}function r(e,t){return function(n){n({type:u.ACTION_TYPES.UPDATE_FIELD,payload:{formId:e,updates:t}})}}function o(e){
return function(t){t({type:u.ACTION_TYPES.ADD_FORM,payload:{formState:e}})}}function a(e,t,n){return function(i){var r={"X-Formschema-Request":"state","X-Requested-With":"XMLHttpRequest"} return function(t){t({type:u.ACTION_TYPES.ADD_FORM,payload:{formState:e}})}}function a(e,t,n){return function(i){var r={"X-Formschema-Request":"schema,state","X-Requested-With":"XMLHttpRequest"}
return i({type:u.ACTION_TYPES.SUBMIT_FORM_REQUEST,payload:{formId:t}}),e(l({ID:t},n),r).then(function(e){return i({type:u.ACTION_TYPES.SUBMIT_FORM_SUCCESS,payload:{response:e}}),e})["catch"](function(e){ return i({type:u.ACTION_TYPES.SUBMIT_FORM_REQUEST,payload:{formId:t}}),e(l({ID:t},n),r).then(function(e){return i({type:u.ACTION_TYPES.SUBMIT_FORM_SUCCESS,payload:{response:e}}),e})["catch"](function(e){
throw e.response.text().then(function(e){return i({type:u.ACTION_TYPES.SUBMIT_FORM_FAILURE,payload:{formId:t,error:e}}),e})})}}function s(e,t){return function(n){n({type:u.ACTION_TYPES.SET_SUBMIT_ACTION, throw e.response.text().then(function(e){return i({type:u.ACTION_TYPES.SUBMIT_FORM_FAILURE,payload:{formId:t,error:e}}),e})})}}function s(e,t){return function(n){n({type:u.ACTION_TYPES.SET_SUBMIT_ACTION,
payload:{formId:e,submitAction:t}})}}Object.defineProperty(t,"__esModule",{value:!0}) payload:{formId:e,submitAction:t}})}}Object.defineProperty(t,"__esModule",{value:!0})
@ -2408,15 +2409,17 @@ multiline:!0,crumbs:this.props.breadcrumbs})),p["default"].createElement("div",{
}},{key:"renderItemListView",value:function h(){var e={sectionConfig:this.props.sectionConfig,campaignId:this.props.params.id,itemListViewEndpoint:this.props.sectionConfig.itemListViewEndpoint,publishApi:this.publishApi, }},{key:"renderItemListView",value:function h(){var e={sectionConfig:this.props.sectionConfig,campaignId:this.props.params.id,itemListViewEndpoint:this.props.sectionConfig.itemListViewEndpoint,publishApi:this.publishApi,
handleBackButtonClick:this.handleBackButtonClick.bind(this)} handleBackButtonClick:this.handleBackButtonClick.bind(this)}
return p["default"].createElement(A["default"],e)}},{key:"renderDetailEditView",value:function m(){var e=this.props.sectionConfig.form.DetailEditForm.schemaUrl,t={createFn:this.campaignEditCreateFn.bind(this), return p["default"].createElement(A["default"],e)}},{key:"renderDetailEditView",value:function m(){var e=this.props.sectionConfig.form.DetailEditForm.schemaUrl,t=e
schemaUrl:e+"/"+this.props.params.id} this.props.params.id>0&&(t=e+"/"+this.props.params.id)
var n={createFn:this.campaignEditCreateFn.bind(this),schemaUrl:t}
return p["default"].createElement("div",{className:"cms-content__inner"},p["default"].createElement(x["default"],{showBackButton:!0,handleBackButtonClick:this.handleBackButtonClick},p["default"].createElement(C["default"],{ return p["default"].createElement("div",{className:"cms-content__inner"},p["default"].createElement(x["default"],{showBackButton:!0,handleBackButtonClick:this.handleBackButtonClick},p["default"].createElement(C["default"],{
multiline:!0,crumbs:this.props.breadcrumbs})),p["default"].createElement("div",{className:"panel panel--padded panel--scrollable panel--single-toolbar"},p["default"].createElement("div",{className:"form--inline" multiline:!0,crumbs:this.props.breadcrumbs})),p["default"].createElement("div",{className:"panel panel--padded panel--scrollable panel--single-toolbar"},p["default"].createElement("div",{className:"form--inline"
},p["default"].createElement(I["default"],t))))}},{key:"renderCreateView",value:function g(){var e=this.props.sectionConfig.form.DetailEditForm.schemaUrl,t={createFn:this.campaignAddCreateFn.bind(this), },p["default"].createElement(I["default"],n))))}},{key:"renderCreateView",value:function g(){var e=this.props.sectionConfig.form.DetailEditForm.schemaUrl,t=e
schemaUrl:e+"/"+this.props.params.id} this.props.params.id>0&&(t=e+"/"+this.props.params.id)
var n={createFn:this.campaignAddCreateFn.bind(this),schemaUrl:t}
return p["default"].createElement("div",{className:"cms-content__inner"},p["default"].createElement(x["default"],{showBackButton:!0,handleBackButtonClick:this.handleBackButtonClick},p["default"].createElement(C["default"],{ return p["default"].createElement("div",{className:"cms-content__inner"},p["default"].createElement(x["default"],{showBackButton:!0,handleBackButtonClick:this.handleBackButtonClick},p["default"].createElement(C["default"],{
multiline:!0,crumbs:this.props.breadcrumbs})),p["default"].createElement("div",{className:"panel panel--padded panel--scrollable panel--single-toolbar"},p["default"].createElement("div",{className:"form--inline" multiline:!0,crumbs:this.props.breadcrumbs})),p["default"].createElement("div",{className:"panel panel--padded panel--scrollable panel--single-toolbar"},p["default"].createElement("div",{className:"form--inline"
},p["default"].createElement(I["default"],t))))}},{key:"campaignEditCreateFn",value:function v(e,t){var n=this,i=this.props.sectionConfig.url },p["default"].createElement(I["default"],n))))}},{key:"campaignEditCreateFn",value:function v(e,t){var n=this,i=this.props.sectionConfig.url
if("action_cancel"===t.name){var r=d({},t,{handleClick:function o(e){e.preventDefault(),n.props.router.push(i)}}) if("action_cancel"===t.name){var r=d({},t,{handleClick:function o(e){e.preventDefault(),n.props.router.push(i)}})
return p["default"].createElement(e,d({key:t.id},r))}return p["default"].createElement(e,d({key:t.id},t))}},{key:"campaignAddCreateFn",value:function b(e,t){var n=this,i=this.props.sectionConfig.url return p["default"].createElement(e,d({key:t.id},r))}return p["default"].createElement(e,d({key:t.id},t))}},{key:"campaignAddCreateFn",value:function b(e,t){var n=this,i=this.props.sectionConfig.url
if("action_cancel"===t.name){var r=d({},t,{handleClick:function o(e){e.preventDefault(),n.props.router.push(i)}}) if("action_cancel"===t.name){var r=d({},t,{handleClick:function o(e){e.preventDefault(),n.props.router.push(i)}})

View File

@ -105,16 +105,24 @@ export class FormBuilderComponent extends SilverStripeComponent {
SecurityID: this.props.config.SecurityID, SecurityID: this.props.config.SecurityID,
}; };
if (formSchema.schema.actions.length > 0) { this.submitApi = (...args) => {
defaultData[formSchema.schema.actions[0].name] = 1; const endPoint = backend.createEndpointFetcher({
}
this.submitApi = backend.createEndpointFetcher({
url: formSchema.schema.attributes.action, url: formSchema.schema.attributes.action,
method: formSchema.schema.attributes.method, method: formSchema.schema.attributes.method,
defaultData, defaultData,
}); });
// Ensure that schema changes are handled prior to updating state
return endPoint(...args)
.then((response) => {
if (response.schema) {
const newSchema = Object.assign({}, { id: response.id, schema: response.schema });
this.props.schemaActions.setSchema(newSchema);
}
return response;
});
};
this.props.schemaActions.setSchema(formSchema); this.props.schemaActions.setSchema(formSchema);
} }
@ -248,6 +256,14 @@ export class FormBuilderComponent extends SilverStripeComponent {
? schema.state.fields ? schema.state.fields
: schema.schema.fields; : schema.schema.fields;
// Set action
const action = this.getSubmitAction();
const values = {};
if (action) {
values[action] = 1;
}
// Reduce all other fields
return this.props.form[this.getFormId()].fields return this.props.form[this.getFormId()].fields
.reduce((prev, curr) => { .reduce((prev, curr) => {
const match = this.findField(fields, curr.id); const match = this.findField(fields, curr.id);
@ -258,7 +274,11 @@ export class FormBuilderComponent extends SilverStripeComponent {
return Object.assign({}, prev, { return Object.assign({}, prev, {
[match.name]: curr.value, [match.name]: curr.value,
}); });
}, {}); }, values);
}
getSubmitAction() {
return this.props.form[this.getFormId()].submitAction;
} }
/** /**
@ -419,7 +439,9 @@ export class FormBuilderComponent extends SilverStripeComponent {
const data = this.mergeFieldData(field, state); const data = this.mergeFieldData(field, state);
if (field.children) { if (field.children) {
data.children = this.getFieldData(field.children, formState); return Object.assign({}, data, {
children: this.getFieldData(field.children, formState),
});
} }
return data; return data;
@ -451,9 +473,10 @@ export class FormBuilderComponent extends SilverStripeComponent {
delete attributes.enctype; delete attributes.enctype;
const fieldData = this.getFieldData(formSchema.schema.fields, formState); const fieldData = this.getFieldData(formSchema.schema.fields, formState);
const actionData = this.getFieldData(formSchema.schema.actions, formState);
const formProps = { const formProps = {
actions: formSchema.schema.actions, actions: actionData,
attributes, attributes,
componentWillUnmount: this.removeForm, componentWillUnmount: this.removeForm,
data: formSchema.schema.data, data: formSchema.schema.data,

View File

@ -63,6 +63,7 @@ describe('FormBuilderComponent', () => {
props = { props = {
form: { form: {
MyForm: { MyForm: {
submitAction: 'action_save',
fields: [ fields: [
{ id: 'fieldOne', value: 'valOne' }, { id: 'fieldOne', value: 'valOne' },
{ id: 'fieldTwo', value: null }, { id: 'fieldTwo', value: null },
@ -89,6 +90,7 @@ describe('FormBuilderComponent', () => {
fieldValues = formBuilder.getFieldValues(); fieldValues = formBuilder.getFieldValues();
expect(fieldValues).toEqual({ expect(fieldValues).toEqual({
action_save: 1,
fieldOne: 'valOne', fieldOne: 'valOne',
fieldTwo: null, fieldTwo: null,
}); });

View File

@ -170,9 +170,13 @@ class CampaignAdmin extends SilverStripeComponent {
*/ */
renderDetailEditView() { renderDetailEditView() {
const baseSchemaUrl = this.props.sectionConfig.form.DetailEditForm.schemaUrl; const baseSchemaUrl = this.props.sectionConfig.form.DetailEditForm.schemaUrl;
let schemaUrl = baseSchemaUrl;
if (this.props.params.id > 0) {
schemaUrl = `${baseSchemaUrl}/${this.props.params.id}`;
}
const formBuilderProps = { const formBuilderProps = {
createFn: this.campaignEditCreateFn.bind(this), createFn: this.campaignEditCreateFn.bind(this),
schemaUrl: `${baseSchemaUrl}/${this.props.params.id}`, schemaUrl,
}; };
return ( return (
@ -195,9 +199,13 @@ class CampaignAdmin extends SilverStripeComponent {
*/ */
renderCreateView() { renderCreateView() {
const baseSchemaUrl = this.props.sectionConfig.form.DetailEditForm.schemaUrl; const baseSchemaUrl = this.props.sectionConfig.form.DetailEditForm.schemaUrl;
let schemaUrl = baseSchemaUrl;
if (this.props.params.id > 0) {
schemaUrl = `${baseSchemaUrl}/${this.props.params.id}`;
}
const formBuilderProps = { const formBuilderProps = {
createFn: this.campaignAddCreateFn.bind(this), createFn: this.campaignAddCreateFn.bind(this),
schemaUrl: `${baseSchemaUrl}/${this.props.params.id}`, schemaUrl,
}; };
return ( return (

View File

@ -58,7 +58,7 @@ export function addForm(formState) {
export function submitForm(submitApi, formId, fieldValues) { export function submitForm(submitApi, formId, fieldValues) {
return (dispatch) => { return (dispatch) => {
const headers = { const headers = {
'X-Formschema-Request': 'state', 'X-Formschema-Request': 'schema,state',
'X-Requested-With': 'XMLHttpRequest', 'X-Requested-With': 'XMLHttpRequest',
}; };
dispatch({ dispatch({

View File

@ -372,7 +372,8 @@ class LeftAndMain extends Controller implements PermissionProvider {
$return = ['id' => $form->FormName()]; $return = ['id' => $form->FormName()];
if (in_array('schema', $schemaParts)) { if (in_array('schema', $schemaParts)) {
$return['schema'] = $this->schema->getSchema($form); $schemaLink = $this->getSchemaLinkForForm($form);
$return['schema'] = $this->schema->getSchema($form, $schemaLink);
} }
if (in_array('state', $schemaParts)) { if (in_array('state', $schemaParts)) {
@ -382,6 +383,22 @@ class LeftAndMain extends Controller implements PermissionProvider {
return $return; return $return;
} }
/**
* Get link to schema url for a given form
*
* @param Form $form
* @return string
*/
protected function getSchemaLinkForForm(Form $form) {
$parts = [$this->Link('schema'), $form->getName()];
if (($record = $form->getRecord()) && $record->isInDB()) {
$parts[] = $record->ID;
} elseif (($data = $form->getData()) && !empty($data['ID'])) {
$parts[] = $data['ID'];
}
return Controller::join_links($parts);
}
/** /**
* @param Member $member * @param Member $member
* @return boolean * @return boolean

View File

@ -792,6 +792,7 @@ specific functions.
Run `composer require --dev 'phpunit/phpunit:~4.8'` on existing projects to pull in the new dependency. Run `composer require --dev 'phpunit/phpunit:~4.8'` on existing projects to pull in the new dependency.
* Admin URL can now be configured via custom Director routing rule * Admin URL can now be configured via custom Director routing rule
* `Controller::init` visibility changed to protected. Use `Controller::doInit()` instead. * `Controller::init` visibility changed to protected. Use `Controller::doInit()` instead.
* `Controller::join_links` supports an array of link sections.
* `Object::useCustomClass` has been removed. You should use the config API with Injector instead. * `Object::useCustomClass` has been removed. You should use the config API with Injector instead.
* `Object::invokeWithExtensions` now has the same method signature as `Object::extend` and behaves the same way. * `Object::invokeWithExtensions` now has the same method signature as `Object::extend` and behaves the same way.
* `ServiceConfigurationLocator` is now an interface not a class. * `ServiceConfigurationLocator` is now an interface not a class.

View File

@ -283,6 +283,11 @@ class ControllerTest extends FunctionalTest {
/* Does type-safe checks for zero value */ /* Does type-safe checks for zero value */
$this->assertEquals("my-page/0", Controller::join_links("my-page", 0)); $this->assertEquals("my-page/0", Controller::join_links("my-page", 0));
// Test array args
$this->assertEquals("admin/crm/MyForm?a=1&b=2&c=3",
Controller::join_links(["?a=1", "admin/crm", "?b=2", "MyForm?c=3"])
);
} }
public function testLink() { public function testLink() {

View File

@ -10,9 +10,6 @@ use SilverStripe\Forms\RequiredFields;
use SilverStripe\Forms\FormAction; use SilverStripe\Forms\FormAction;
use SilverStripe\Forms\PopoverField; use SilverStripe\Forms\PopoverField;
class FormSchemaTest extends SapphireTest { class FormSchemaTest extends SapphireTest {
public function testGetSchema() { public function testGetSchema() {
@ -23,7 +20,7 @@ class FormSchemaTest extends SapphireTest {
'id' => 'Form_TestForm', 'id' => 'Form_TestForm',
'action' => 'Controller/TestForm', 'action' => 'Controller/TestForm',
'method' => 'POST', 'method' => 'POST',
'schema_url' => '', 'schema_url' => 'admin/mysection/schema',
'attributes' => [ 'attributes' => [
'id' => 'Form_TestForm', 'id' => 'Form_TestForm',
'action' => 'Controller/TestForm', 'action' => 'Controller/TestForm',
@ -56,7 +53,7 @@ class FormSchemaTest extends SapphireTest {
'actions' => [] 'actions' => []
]; ];
$schema = $formSchema->getSchema($form); $schema = $formSchema->getSchema($form, 'admin/mysection/schema');
$this->assertInternalType('array', $schema); $this->assertInternalType('array', $schema);
$this->assertJsonStringEqualsJsonString(json_encode($expected), json_encode($schema)); $this->assertJsonStringEqualsJsonString(json_encode($expected), json_encode($schema));
} }
@ -174,7 +171,7 @@ class FormSchemaTest extends SapphireTest {
'id' => 'Form_TestForm', 'id' => 'Form_TestForm',
'action' => 'Controller/TestForm', 'action' => 'Controller/TestForm',
'method' => 'POST', 'method' => 'POST',
'schema_url' => '', 'schema_url' => 'admin/mysection/schema',
'attributes' => [ 'attributes' => [
'id' => 'Form_TestForm', 'id' => 'Form_TestForm',
'action' => 'Controller/TestForm', 'action' => 'Controller/TestForm',
@ -339,9 +336,27 @@ class FormSchemaTest extends SapphireTest {
] ]
]; ];
$schema = $formSchema->getSchema($form); $schema = $formSchema->getSchema($form, 'admin/mysection/schema');
$this->assertInternalType('array', $schema); $this->assertInternalType('array', $schema);
$this->assertJsonStringEqualsJsonString(json_encode($expected), json_encode($schema)); $this->assertJsonStringEqualsJsonString(json_encode($expected), json_encode($schema));
} }
/**
* Test that schema is merged correctly
*/
public function testMergeSchema() {
$publishAction = FormAction::create('publish', 'Publish');
$publishAction->setIcon('save');
$publishAction->setSchemaData(['data' => ['buttonStyle' => 'primary']]);
$schema = $publishAction->getSchemaData();
$this->assertEquals(
[
'icon' => 'save',
'buttonStyle' => 'primary',
],
$schema['data']
);
}
} }