diff --git a/platform/commonUI/browse/bundle.js b/platform/commonUI/browse/bundle.js index 0998ddf068..10eb6c7004 100644 --- a/platform/commonUI/browse/bundle.js +++ b/platform/commonUI/browse/bundle.js @@ -34,6 +34,7 @@ define([ "./src/windowing/NewTabAction", "./src/windowing/FullscreenAction", "./src/creation/CreateActionProvider", + "./src/creation/AddActionProvider", "./src/creation/CreationService", "./src/windowing/WindowTitler", 'legacyRegistry' @@ -50,6 +51,7 @@ define([ NewTabAction, FullscreenAction, CreateActionProvider, + AddActionProvider, CreationService, WindowTitler, legacyRegistry @@ -272,6 +274,18 @@ define([ "policyService" ] }, + { + "key": "AddActionProvider", + "provides": "actionService", + "type": "provider", + "implementation": AddActionProvider, + "depends": [ + "$q", + "typeService", + "dialogService", + "policyService" + ] + }, { "key": "CreationService", "provides": "creationService", diff --git a/platform/commonUI/browse/src/creation/AddAction.js b/platform/commonUI/browse/src/creation/AddAction.js new file mode 100644 index 0000000000..3832280130 --- /dev/null +++ b/platform/commonUI/browse/src/creation/AddAction.js @@ -0,0 +1,139 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT Web includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ +/*global define,Promise*/ + +/** + * Module defining AddAction. Created by ahenry on 01/21/16. + */ +define( + [ + './CreateWizard' + ], + function (CreateWizard) { + "use strict"; + + /** + * The Add Action is performed to create new instances of + * domain objects of a specific type that are subobjects of an + * object being edited. This is the action that is performed when a + * user uses the Add menu option. + * + * @memberof platform/commonUI/browse + * @implements {Action} + * @constructor + * + * @param {Type} type the type of domain object to create + * @param {DomainObject} parent the domain object that should + * act as a container for the newly-created object + * (note that the user will have an opportunity to + * override this) + * @param {ActionContext} context the context in which the + * action is being performed + * @param {DialogService} dialogService + */ + function AddAction(type, parent, context, $q, dialogService, policyService) { + this.metadata = { + key: 'add', + glyph: type.getGlyph(), + name: type.getName(), + type: type.getKey(), + description: type.getDescription(), + context: context + }; + + this.type = type; + this.parent = parent; + this.$q = $q; + this.dialogService = dialogService; + this.policyService = policyService; + } + + /** + * + * Create a new object of the given type. + * This will prompt for user input first. + * + * @returns {Promise} that will be resolved with the object that the + * action was originally invoked on (ie. the 'parent') + */ + AddAction.prototype.perform = function () { + var newModel = this.type.getInitialModel(), + newObject, + parentObject = this.parent, + wizard; + + newModel.type = this.type.getKey(); + newObject = parentObject.getCapability('instantiation').instantiate(newModel); + newObject.useCapability('mutation', function(model){ + model.location = parentObject.getId(); + }); + + wizard = new CreateWizard(newObject, this.parent, this.policyService); + + function populateObjectFromInput (formValue) { + return wizard.populateObjectFromInput(formValue, newObject); + } + + function addToParent (populatedObject) { + parentObject.getCapability('composition').add(populatedObject); + return parentObject.getCapability('persistence').persist().then(function(){ + return parentObject; + }); + } + + function save(object) { + /* + It's necessary to persist the new sub-object in order + that it can be retrieved for composition in the parent. + Future refactoring that allows temporary objects to be + retrieved from object services will make this unnecessary. + */ + return object.getCapability('editor').save(true); + } + + return this.dialogService + .getUserInput(wizard.getFormStructure(false), wizard.getInitialFormValue()) + .then(populateObjectFromInput) + .then(save) + .then(addToParent); + + }; + + + /** + * Metadata associated with a Add action. + * @typedef {ActionMetadata} AddActionMetadata + * @property {string} type the key for the type of domain object + * to be created + */ + + /** + * Get metadata about this action. + * @returns {AddActionMetadata} metadata about this action + */ + AddAction.prototype.getMetadata = function () { + return this.metadata; + }; + + return AddAction; + } +); diff --git a/platform/commonUI/browse/src/creation/AddActionProvider.js b/platform/commonUI/browse/src/creation/AddActionProvider.js new file mode 100644 index 0000000000..0ac97c0013 --- /dev/null +++ b/platform/commonUI/browse/src/creation/AddActionProvider.js @@ -0,0 +1,87 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT Web includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ +/*global define,Promise*/ + +/** + * Module defining AddActionProvider.js. Created by ahenry on 01/21/16. + */ +define( + ["./AddAction"], + function (AddAction) { + "use strict"; + + /** + * The AddActionProvider is an ActionProvider which introduces + * an Add action for creating sub objects. + * + * @memberof platform/commonUI/browse + * @constructor + * @implements {ActionService} + * + * @param {TypeService} typeService the type service, used to discover + * available types + * @param {DialogService} dialogService the dialog service, used by + * specific Create actions to get user input to populate the + * model of the newly-created domain object. + * @param {CreationService} creationService the creation service (also + * introduced in this bundle), responsible for handling actual + * object creation. + */ + function AddActionProvider($q, typeService, dialogService, policyService) { + this.typeService = typeService; + this.dialogService = dialogService; + this.$q = $q; + this.policyService = policyService; + } + + AddActionProvider.prototype.getActions = function (actionContext) { + var context = actionContext || {}, + key = context.key, + destination = context.domainObject, + self = this; + + // We only provide Add actions, and we need a + // domain object to serve as the container for the + // newly-created object (although the user may later + // make a different selection) + if (key !== 'add' || !destination) { + return []; + } + + // Introduce one create action per type + return this.typeService.listTypes().filter(function (type) { + return self.policyService.allow("creation", type) && self.policyService.allow("composition", destination.getCapability('type'), type); + }).map(function (type) { + return new AddAction( + type, + destination, + context, + self.$q, + self.dialogService, + self.policyService + ); + }); + }; + + return AddActionProvider; + } +); diff --git a/platform/commonUI/browse/src/creation/CreateWizard.js b/platform/commonUI/browse/src/creation/CreateWizard.js index caa60c6150..f4a29d42ac 100644 --- a/platform/commonUI/browse/src/creation/CreateWizard.js +++ b/platform/commonUI/browse/src/creation/CreateWizard.js @@ -26,18 +26,21 @@ define( 'use strict'; /** - * Construct a new CreateWizard. + * A class for capturing user input data from an object creation + * dialog, and populating a domain object with that data. * - * @param {TypeImpl} type the type of domain object to be created + * @param {DomainObject} domainObject the newly created object to + * populate with user input * @param {DomainObject} parent the domain object to serve as * the initial parent for the created object, in the dialog * @memberof platform/commonUI/browse * @constructor */ - function CreateWizard(type, parent, policyService, initialModel) { - this.type = type; - this.model = initialModel || type.getInitialModel(); - this.properties = type.getProperties(); + function CreateWizard(domainObject, parent, policyService) { + this.type = domainObject.getCapability('type'); + this.model = domainObject.getModel(); + this.domainObject = domainObject; + this.properties = this.type.getProperties(); this.parent = parent; this.policyService = policyService; } @@ -46,11 +49,14 @@ define( * Get the form model for this wizard; this is a description * that will be rendered to an HTML form. See the * platform/forms bundle - * + * @param {boolean} includeLocation if true, a 'location' section + * will be included that will allow the user to select the location + * of the newly created object, otherwise the .location property of + * the model will be used. * @return {FormModel} formModel the form model to * show in the create dialog */ - CreateWizard.prototype.getFormStructure = function () { + CreateWizard.prototype.getFormStructure = function (includeLocation) { var sections = [], type = this.type, policyService = this.policyService; @@ -84,12 +90,16 @@ define( }); // Ensure there is always a "save in" section - sections.push({ name: 'Location', rows: [{ - name: "Save In", - control: "locator", - validate: validateLocation, - key: "createParent" - }]}); + if (includeLocation) { + sections.push({ + name: 'Location', rows: [{ + name: "Save In", + control: "locator", + validate: validateLocation, + key: "createParent" + }] + }); + } return { sections: sections, @@ -97,6 +107,23 @@ define( }; }; + /** + * Given some form input values and a domain object, populate the + * domain object used to create this wizard from the given form values. + * @param formValue + * @returns {DomainObject} + */ + CreateWizard.prototype.populateObjectFromInput = function(formValue) { + var parent = this.getLocation(formValue), + formModel = this.createModel(formValue); + + formModel.location = parent.getId(); + this.domainObject.useCapability("mutation", function(){ + return formModel; + }); + return this.domainObject; + }; + /** * Get the initial value for the form being described. * This will include the values for all properties described @@ -120,6 +147,7 @@ define( /** * Based on a populated form, get the domain object which * should be used as a parent for the newly-created object. + * @private * @return {DomainObject} */ CreateWizard.prototype.getLocation = function (formValue) { @@ -129,6 +157,7 @@ define( /** * Create the domain object model for a newly-created object, * based on user input read from a formModel. + * @private * @return {object} the domain object model */ CreateWizard.prototype.createModel = function (formValue) { diff --git a/platform/commonUI/browse/test/creation/AddActionProviderSpec.js b/platform/commonUI/browse/test/creation/AddActionProviderSpec.js new file mode 100644 index 0000000000..aaa83af8e9 --- /dev/null +++ b/platform/commonUI/browse/test/creation/AddActionProviderSpec.js @@ -0,0 +1,137 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT Web includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ +/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine,xit,xdescribe*/ + +/** + * MCTRepresentationSpec. Created by ahenry on 01/21/14. + */ +define( + ["../../src/creation/AddActionProvider"], + function (AddActionProvider) { + "use strict"; + + describe("The add action provider", function () { + var mockTypeService, + mockDialogService, + mockPolicyService, + mockCreationPolicy, + mockCompositionPolicy, + mockPolicyMap = {}, + mockTypes, + mockDomainObject, + mockQ, + provider; + + function createMockType(name) { + var mockType = jasmine.createSpyObj( + "type" + name, + [ + "getKey", + "getGlyph", + "getName", + "getDescription", + "getProperties", + "getInitialModel", + "hasFeature" + ] + ); + mockType.hasFeature.andReturn(true); + mockType.getName.andReturn(name); + return mockType; + } + + beforeEach(function () { + mockTypeService = jasmine.createSpyObj( + "typeService", + [ "listTypes" ] + ); + mockDialogService = jasmine.createSpyObj( + "dialogService", + [ "getUserInput" ] + ); + mockPolicyService = jasmine.createSpyObj( + "policyService", + [ "allow" ] + ); + + mockDomainObject = jasmine.createSpyObj( + "domainObject", + [ "getCapability" ] + ); + + //Mocking getCapability because AddActionProvider uses the + // type capability of the destination object. + mockDomainObject.getCapability.andReturn({}); + + mockTypes = [ "A", "B", "C" ].map(createMockType); + + mockTypes.forEach(function(type){ + mockPolicyMap[type.getName()] = true; + }); + + mockCreationPolicy = function(type){ + return mockPolicyMap[type.getName()]; + }; + + mockCompositionPolicy = function(){ + return true; + }; + + mockPolicyService.allow.andReturn(true); + + mockTypeService.listTypes.andReturn(mockTypes); + + provider = new AddActionProvider( + mockQ, + mockTypeService, + mockDialogService, + mockPolicyService + ); + }); + + it("checks for creatability", function () { + provider.getActions({ + key: "add", + domainObject: mockDomainObject + }); + // Make sure it was creation which was used to check + expect(mockPolicyService.allow) + .toHaveBeenCalledWith("creation", mockTypes[0]); + }); + + it("checks for composability of type", function () { + provider.getActions({ + key: "add", + domainObject: mockDomainObject + }); + + expect(mockPolicyService.allow).toHaveBeenCalledWith( + "composition", + jasmine.any(Object), + jasmine.any(Object) + ); + + expect(mockDomainObject.getCapability).toHaveBeenCalledWith('type'); + }); + }); + } +); \ No newline at end of file diff --git a/platform/commonUI/browse/test/creation/CreateActionProviderSpec.js b/platform/commonUI/browse/test/creation/CreateActionProviderSpec.js index f49c394dc9..857b29fe4e 100644 --- a/platform/commonUI/browse/test/creation/CreateActionProviderSpec.js +++ b/platform/commonUI/browse/test/creation/CreateActionProviderSpec.js @@ -32,11 +32,12 @@ define( describe("The create action provider", function () { var mockTypeService, mockDialogService, - mockCreationService, + mockNavigationService, mockPolicyService, mockCreationPolicy, mockPolicyMap = {}, mockTypes, + mockQ, provider; function createMockType(name) { @@ -66,9 +67,9 @@ define( "dialogService", [ "getUserInput" ] ); - mockCreationService = jasmine.createSpyObj( - "creationService", - [ "createObject" ] + mockNavigationService = jasmine.createSpyObj( + "navigationService", + [ "setNavigation" ] ); mockPolicyService = jasmine.createSpyObj( "policyService", @@ -92,15 +93,14 @@ define( mockTypeService.listTypes.andReturn(mockTypes); provider = new CreateActionProvider( + mockQ, mockTypeService, - mockDialogService, - mockCreationService, + mockNavigationService, mockPolicyService ); }); - //TODO: Disabled for NEM Beta - xit("exposes one create action per type", function () { + it("exposes one create action per type", function () { expect(provider.getActions({ key: "create", domainObject: {} @@ -114,8 +114,7 @@ define( }).length).toEqual(0); }); - //TODO: Disabled for NEM Beta - xit("does not expose non-creatable types", function () { + it("does not expose non-creatable types", function () { // One of the types won't have the creation feature... mockPolicyMap[mockTypes[0].getName()] = false; // ...so it should have been filtered out. diff --git a/platform/commonUI/browse/test/creation/CreateWizardSpec.js b/platform/commonUI/browse/test/creation/CreateWizardSpec.js index c4453e7432..fbb2a735e6 100644 --- a/platform/commonUI/browse/test/creation/CreateWizardSpec.js +++ b/platform/commonUI/browse/test/creation/CreateWizardSpec.js @@ -35,6 +35,7 @@ define( mockProperties, mockPolicyService, testModel, + mockDomainObject, wizard; function createMockProperty(name) { @@ -81,8 +82,18 @@ define( mockType.getInitialModel.andReturn(testModel); mockType.getProperties.andReturn(mockProperties); + mockDomainObject = jasmine.createSpyObj( + 'domainObject', + ['getCapability', 'useCapability', 'getModel'] + ); + + //Mocking the getCapability('type') call + mockDomainObject.getCapability.andReturn(mockType); + mockDomainObject.useCapability.andReturn(); + mockDomainObject.getModel.andReturn(testModel); + wizard = new CreateWizard( - mockType, + mockDomainObject, mockParent, mockPolicyService ); @@ -130,6 +141,18 @@ define( }); }); + it("populates the model on the associated object", function () { + var formValue = { + "A": "ValueA", + "B": "ValueB", + "C": "ValueC" + }, + compareModel = wizard.createModel(formValue); + wizard.populateObjectFromInput(formValue); + expect(mockDomainObject.useCapability).toHaveBeenCalledWith('mutation', jasmine.any(Function)); + expect(mockDomainObject.useCapability.mostRecentCall.args[1]()).toEqual(compareModel); + }); + it("validates selection types using policy", function () { var mockDomainObject = jasmine.createSpyObj( 'domainObject', @@ -139,7 +162,8 @@ define( 'otherType', ['getKey'] ), - structure = wizard.getFormStructure(), + //Create a form structure with location + structure = wizard.getFormStructure(true), sections = structure.sections, rows = structure.sections[sections.length - 1].rows, locationRow = rows[rows.length - 1]; @@ -156,6 +180,12 @@ define( ); }); + it("creates a form model without a location if not requested", function () { + expect(wizard.getFormStructure(false).sections.some(function(section){ + return section.name === 'Location'; + })).toEqual(false); + }); + }); } diff --git a/platform/commonUI/edit/src/actions/SaveAction.js b/platform/commonUI/edit/src/actions/SaveAction.js index 13fde582d7..073beeb4df 100644 --- a/platform/commonUI/edit/src/actions/SaveAction.js +++ b/platform/commonUI/edit/src/actions/SaveAction.js @@ -79,65 +79,23 @@ define( function doWizardSave(parent) { var context = domainObject.getCapability("context"), - wizard = new CreateWizard(domainObject.useCapability('type'), parent, self.policyService, domainObject.getModel()); - - function mergeObjects(fromObject, toObject){ - Object.keys(fromObject).forEach(function(key) { - toObject[key] = fromObject[key]; - }); - } - - // Create and persist the new object, based on user - // input. - function buildObjectFromInput(formValue) { - var parent = wizard.getLocation(formValue), - formModel = wizard.createModel(formValue); - - formModel.location = parent.getId(); - //Replace domain object model with model collected - // from user form. - domainObject.useCapability("mutation", function(){ - //Replace object model with the model from the form - return formModel; - }); - return domainObject; - } - - function getAllComposees(domainObject){ - return domainObject.useCapability('composition'); - } - - function addComposeesToObject(object){ - return function(composees){ - return self.$q.all(composees.map(function (composee) { - return object.getCapability('composition').add(composee); - })).then(resolveWith(object)); - }; - } - - /** - * Add the composees of the 'virtual' object to the - * persisted object - * @param object - * @returns {*} - */ - function composeNewObject(object){ - if (self.$q.when(object.hasCapability('composition') && domainObject.hasCapability('composition'))) { - return getAllComposees(domainObject) - .then(addComposeesToObject(object)); - } - } + wizard = new CreateWizard(domainObject, parent, self.policyService); return self.dialogService - .getUserInput(wizard.getFormStructure(), wizard.getInitialFormValue()) - .then(buildObjectFromInput); + .getUserInput(wizard.getFormStructure(true), wizard.getInitialFormValue()) + .then(function(formValue){ + return wizard.populateObjectFromInput(formValue, domainObject); + }); } function persistObject(object){ - return ((object.hasCapability('editor') && object.getCapability('editor').save(true)) || - object.getCapability('persistence').persist()) - .then(resolveWith(object)); + + //Persist first to mark dirty + return object.getCapability('persistence').persist().then(function(){ + //then save permanently + return object.getCapability('editor').save(); + }); } function fetchObject(objectId){ @@ -152,7 +110,9 @@ define( function locateObjectInParent(parent){ parent.getCapability('composition').add(domainObject.getId()); - return parent; + return parent.getCapability('persistence').persist().then(function() { + return parent; + }); } function doNothing() { @@ -174,7 +134,6 @@ define( .then(getParent)//Parent may have changed based // on user selection .then(locateObjectInParent) - .then(persistObject) .then(function(){ return fetchObject(domainObject.getId()); }) diff --git a/platform/commonUI/edit/src/capabilities/EditableInstantiationCapability.js b/platform/commonUI/edit/src/capabilities/EditableInstantiationCapability.js new file mode 100644 index 0000000000..6a0392476b --- /dev/null +++ b/platform/commonUI/edit/src/capabilities/EditableInstantiationCapability.js @@ -0,0 +1,60 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT Web includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ +/*global define*/ + + +define( + ['./EditableLookupCapability'], + function (EditableLookupCapability) { + 'use strict'; + + /** + * Wrapper for the "instantiation" capability; + * ensures that any domain objects instantiated in Edit mode + * are also wrapped as EditableDomainObjects. + * + * Meant specifically for use by EditableDomainObject and the + * associated cache; the constructor signature is particular + * to a pattern used there and may contain unused arguments. + * @constructor + * @memberof platform/commonUI/edit + * @implements {CompositionCapability} + */ + return function EditableInstantiationCapability( + contextCapability, + editableObject, + domainObject, + cache + ) { + // This is a "lookup" style capability (it looks up other + // domain objects), but we do not want to return the same + // specific value every time (composition may change) + return new EditableLookupCapability( + contextCapability, + editableObject, + domainObject, + cache, + false // Not idempotent + ); + }; + } +); diff --git a/platform/commonUI/edit/src/capabilities/EditableLookupCapability.js b/platform/commonUI/edit/src/capabilities/EditableLookupCapability.js index c92495dc3f..dae2df3d83 100644 --- a/platform/commonUI/edit/src/capabilities/EditableLookupCapability.js +++ b/platform/commonUI/edit/src/capabilities/EditableLookupCapability.js @@ -45,7 +45,8 @@ define( cache, idempotent ) { - var capability = Object.create(contextCapability); + var capability = Object.create(contextCapability), + method; // Check for domain object interface. If something has these // three methods, we assume it's a domain object. @@ -114,7 +115,9 @@ define( } // Wrap all methods; return only editable domain objects. - Object.keys(contextCapability).forEach(wrapMethod); + for (method in contextCapability) { + wrapMethod(method); + } return capability; }; diff --git a/platform/commonUI/edit/src/capabilities/EditorCapability.js b/platform/commonUI/edit/src/capabilities/EditorCapability.js index ec43bf298a..0818eebf6d 100644 --- a/platform/commonUI/edit/src/capabilities/EditorCapability.js +++ b/platform/commonUI/edit/src/capabilities/EditorCapability.js @@ -81,7 +81,8 @@ define( var domainObject = this.domainObject, editableObject = this.editableObject, self = this, - cache = this.cache; + cache = this.cache, + returnPromise; // Update the underlying, "real" domain object's model // with changes made to the copy used for editing. @@ -99,14 +100,18 @@ define( editableObject.getCapability("status").set("editing", false); if (nonrecursive) { - return resolvePromise(doMutate()) + returnPromise = resolvePromise(doMutate()) .then(doPersist) .then(function(){ self.cancel(); }); } else { - return resolvePromise(cache.saveAll()); + returnPromise = resolvePromise(cache.saveAll()); } + //Return the original (non-editable) object + return returnPromise.then(function() { + return domainObject.getOriginalObject ? domainObject.getOriginalObject() : domainObject; + }); }; /** diff --git a/platform/commonUI/edit/src/objects/EditableDomainObject.js b/platform/commonUI/edit/src/objects/EditableDomainObject.js index 2b764f7de7..253947181d 100644 --- a/platform/commonUI/edit/src/objects/EditableDomainObject.js +++ b/platform/commonUI/edit/src/objects/EditableDomainObject.js @@ -36,6 +36,7 @@ define( '../capabilities/EditableContextCapability', '../capabilities/EditableCompositionCapability', '../capabilities/EditableRelationshipCapability', + '../capabilities/EditableInstantiationCapability', '../capabilities/EditorCapability', '../capabilities/EditableActionCapability', './EditableDomainObjectCache' @@ -45,6 +46,7 @@ define( EditableContextCapability, EditableCompositionCapability, EditableRelationshipCapability, + EditableInstantiationCapability, EditorCapability, EditableActionCapability, EditableDomainObjectCache @@ -56,6 +58,7 @@ define( context: EditableContextCapability, composition: EditableCompositionCapability, relationship: EditableRelationshipCapability, + instantiation: EditableInstantiationCapability, editor: EditorCapability }; diff --git a/platform/commonUI/edit/test/capabilities/EditableLookupCapabilitySpec.js b/platform/commonUI/edit/test/capabilities/EditableLookupCapabilitySpec.js index 24b68b6fd9..16bcee88b1 100644 --- a/platform/commonUI/edit/test/capabilities/EditableLookupCapabilitySpec.js +++ b/platform/commonUI/edit/test/capabilities/EditableLookupCapabilitySpec.js @@ -118,6 +118,29 @@ define( expect(mockContext.getDomainObject.calls.length).toEqual(2); }); + it("wraps inherited methods", function () { + var CapabilityClass = function(){ + }; + CapabilityClass.prototype.inheritedMethod=function () { + return "an inherited method"; + }; + + mockContext = new CapabilityClass(); + + capability = new EditableLookupCapability( + mockContext, + mockEditableObject, + mockDomainObject, + factory, + false + ); + expect(capability.inheritedMethod()).toEqual("an inherited method"); + expect(capability.hasOwnProperty('inheritedMethod')).toBe(true); + // The presence of an own property indicates that the method + // has been wrapped on the object itself and this is a valid + // test that the inherited method has been wrapped. + }); + }); } ); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/swimlane/TimelineProxy.js b/platform/features/timeline/src/controllers/swimlane/TimelineProxy.js index 3174659a63..7f74f59de0 100644 --- a/platform/features/timeline/src/controllers/swimlane/TimelineProxy.js +++ b/platform/features/timeline/src/controllers/swimlane/TimelineProxy.js @@ -39,7 +39,7 @@ define( function populateActionMap(domainObject) { var actionCapability = domainObject.getCapability('action'), actions = actionCapability ? - actionCapability.getActions('create') : []; + actionCapability.getActions('add') : []; actions.forEach(function (action) { actionMap[action.getMetadata().type] = action; }); diff --git a/platform/features/timeline/test/controllers/swimlane/TimelineProxySpec.js b/platform/features/timeline/test/controllers/swimlane/TimelineProxySpec.js index 0ecd073219..7d137fa6e4 100644 --- a/platform/features/timeline/test/controllers/swimlane/TimelineProxySpec.js +++ b/platform/features/timeline/test/controllers/swimlane/TimelineProxySpec.js @@ -74,7 +74,7 @@ define( expect(mockDomainObject.getCapability) .toHaveBeenCalledWith('action'); expect(mockActionCapability.getActions) - .toHaveBeenCalledWith('create'); + .toHaveBeenCalledWith('add'); }); it("invokes the action on the selection, if any", function () {