From 4630e5b521a6c240b99c8de39128fbedf05f1d05 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Thu, 25 Jul 2013 16:45:30 -0700 Subject: [PATCH] CRM-12943 - crm.backbone.js - Allow saving of CRM collections Calling "collection.save()" will lead to "CRM.api(crmEntityName, "replace", ...) ---------------------------------------- * CRM-12943: Make HTML prototype of job UI functional http://issues.civicrm.org/jira/browse/CRM-12943 --- js/crm.backbone.js | 73 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 7 deletions(-) diff --git a/js/crm.backbone.js b/js/crm.backbone.js index 9d5e2cba7e..01ec9e847a 100644 --- a/js/crm.backbone.js +++ b/js/crm.backbone.js @@ -8,7 +8,7 @@ * To load collections using API queries, set the "crmCriteria" property or override the * method "toCrmCriteria". * - * @param method + * @param method Accepts normal Backbone.sync methods; also accepts "crm-replace" * @param model * @param options * @see tests/qunit/crm-backbone @@ -34,6 +34,13 @@ case 'read': CRM.api(model.crmEntityName, 'get', model.toCrmCriteria(), apiOptions); break; + // replace all entities matching "x.crmCriteria" with new entities in "x.models" + case 'crm-replace': + var params = this.toCrmCriteria(); + params.version = 3; + params.values = this.toJSON(); + CRM.api(model.crmEntityName, 'replace', params, apiOptions); + break; default: apiOptions.error({is_error: 1, error_message: "CRM.Backbone.sync(" + method + ") not implemented for collections"}); break; @@ -115,9 +122,11 @@ /** * Configure a model class to track whether a model has unsaved changes. * - * The ModelClass will be extended with: - * - Method: isSaved() - true if there have been no changes to the data since the last fetch or save - * - Event: saved(object model, bool is_saved) - triggered whenever isSaved() value would change + * Methods: + * - setModified() - flag the model as modified/dirty + * - isSaved() - return true if there have been no changes to the data since the last fetch or save + * Events: + * - saved(object model, bool is_saved) - triggered whenever isSaved() value would change * * Note: You should not directly call isSaved() within the context of the success/error/sync callback; * I haven't found a way to make isSaved() behave correctly within these callbacks without patching @@ -193,8 +202,11 @@ * deletion (or not) -- however, deletion will be deferred until save() * is called. * + * Methods: + * setSoftDeleted(boolean) - flag the model as deleted (or not-deleted) + * isSoftDeleted() - determine whether model has been soft-deleted * Events: - * softDelete: function(model, is_deleted) -- change value of is_deleted + * softDelete(model, is_deleted) -- change value of is_deleted * * @param ModelClass */ @@ -233,7 +245,7 @@ * Connect a "collection" class to CiviCRM's APIv3 * * Note: the collection supports a special property, crmCriteria, which is an array of - * query options to send to the API + * query options to send to the API. * * @code * // Setup class @@ -249,6 +261,9 @@ * crmCriteria: {contact_type: 'Organization'} * }); * c.fetch(); + * c.get(123).set('property', 'value'); + * c.get(456).setDeleted(true); + * c.save(); * @endcode * * @param Class CollectionClass @@ -260,7 +275,30 @@ _.defaults(CollectionClass.prototype, { crmEntityName: CollectionClass.prototype.model.prototype.crmEntityName, toCrmCriteria: function() { - return this.crmCriteria || {}; + return (this.crmCriteria) ? _.extend({}, this.crmCriteria) : {}; + }, + + /** + * Reconcile the server's collection with the client's collection. + * New/modified items from the client will be saved/updated on the + * server. Deleted items from the client will be deleted on the + * server. + * + * @param Object options - accepts "success" and "error" callbacks + */ + save: function(options) { + options || (options = {}); + var collection = this; + var success = options.success; + options.success = function(resp) { + // Ensure attributes are restored during synchronous saves. + collection.reset(resp, options); + if (success) success(collection, resp, options); + // collection.trigger('sync', collection, resp, options); + }; + wrapError(collection, options); + + return this.sync('crm-replace', this, options) } }); // Overrides - if specified in CollectionClass, replace @@ -274,6 +312,18 @@ if (origInit) { return origInit.apply(this, arguments); } + }, + toJSON: function() { + var result = []; + // filter models list, excluding any soft-deleted items + this.each(function(model) { + // if model doesn't track soft-deletes + // or if model tracks soft-deletes and wasn't soft-deleted + if (!model.isSoftDeleted || !model.isSoftDeleted()) { + result.push(model.toJSON()); + } + }); + return result; } }); }; @@ -432,4 +482,13 @@ } }); */ + + // Wrap an optional error callback with a fallback error event. + var wrapError = function (model, options) { + var error = options.error; + options.error = function(resp) { + if (error) error(model, resp, options); + model.trigger('error', model, resp, options); + }; + }; })(cj); -- 2.25.1