1 // ==========================================================================
  2 // Project:   The M-Project - Mobile HTML5 Application Framework
  3 // Copyright: (c) 2010 M-Way Solutions GmbH. All rights reserved.
  4 //            (c) 2011 panacoda GmbH. All rights reserved.
  5 // Creator:   Sebastian
  6 // Date:      02.12.2010
  7 // License:   Dual licensed under the MIT or GPL Version 2 licenses.
  8 //            http://github.com/mwaylabs/The-M-Project/blob/master/MIT-LICENSE
  9 //            http://github.com/mwaylabs/The-M-Project/blob/master/GPL-LICENSE
 10 // ==========================================================================
 11 
 12 m_require('core/datastore/data_provider.js');
 13 
 14 /**
 15  * @class
 16  *
 17  * Encapsulates access to a remote storage, a json based web service.
 18  *
 19  * @extends M.DataProvider
 20  */
 21 M.DataProviderRemoteStorage = M.DataProvider.extend(
 22 /** @scope M.RemoteStorageProvider.prototype */ {
 23 
 24     /**
 25      * The type of this object.
 26      * @type String
 27      */
 28     type: 'M.DataProviderRemoteStorage',
 29 
 30     /**
 31      * The type of this object.
 32      * @type Object
 33      */
 34     config: null,
 35 
 36     /* CRUD methods */
 37 
 38     save: function(obj) {
 39 
 40         var config = this.config[obj.model.name];
 41         var result = null;
 42         var dataResult = null;
 43 
 44         if(obj.model.state === M.STATE_NEW) {   /* if the model is new we need to make a create request, if not new then we make an update request */
 45 
 46             dataResult = config.create.map(obj.model.record);
 47 
 48             this.remoteQuery('create', config.url + config.create.url(obj.model.get('ID')), config.create.httpMethod, dataResult, obj, null);
 49 
 50         } else { // make an update request
 51 
 52             dataResult = config.update.map(obj.model.record);
 53 
 54             var updateUrl = config.url + config.update.url(obj.model.get('ID'));
 55 
 56             this.remoteQuery('update', updateUrl, config.update.httpMethod, dataResult, obj, function(xhr) {
 57                   xhr.setRequestHeader("X-Http-Method-Override", config.update.httpMethod);
 58             });
 59         }
 60 
 61     },
 62 
 63     del: function(obj) {
 64         var config = this.config[obj.model.name];
 65         var delUrl = config.del.url(obj.model.get('ID'));
 66         delUrl = config.url + delUrl;
 67 
 68         this.remoteQuery('delete', delUrl, config.del.httpMethod, null, obj,  function(xhr) {
 69             xhr.setRequestHeader("X-Http-Method-Override", config.del.httpMethod);
 70         });
 71     },
 72 
 73     find: function(obj) {
 74         var config = this.config[obj.model.name];
 75 
 76         var readUrl = obj.ID ? config.read.url.one(obj.ID) : config.read.url.all();
 77         readUrl = config.url + readUrl;
 78 
 79         this.remoteQuery('read', readUrl, config.read.httpMethod, null, obj);
 80 
 81     },
 82 
 83     createModelsFromResult: function(data, callback, obj) {
 84         var result = [];
 85         var config = this.config[obj.model.name];
 86         if(_.isArray(data)) {
 87             for(var i in data) {
 88                 var res = data[i];
 89                 /* create model  record from result by first map with given mapper function before passing
 90                  * to createRecord
 91                  */
 92                 result.push(obj.model.createRecord($.extend(config.read.map(res), {state: M.STATE_VALID})));
 93             }
 94         } else if(typeof(data) === 'object') {
 95             result.push(obj.model.createRecord($.extend(config.read.map(data), {state: M.STATE_VALID})));
 96         }
 97         callback(result);
 98     },
 99 
100     remoteQuery: function(opType, url, type, data, obj, beforeSend) {
101         var that = this;
102         var config = this.config[obj.model.name];
103 
104         M.Request.init({
105             url: url,
106             method: type,
107             isJSON: YES,
108             contentType: 'application/JSON',
109             data: data ? data : null,
110             onSuccess: function(data, msg, xhr) {
111 
112                 /*
113                 * delete from record manager if delete request was made.
114                 */
115                 if(opType === 'delete') {
116                     obj.model.recordManager.remove(obj.model.m_id);
117                 }
118 
119                 /*
120                 * call the receiveIdentifier method if provided, that sets the ID for the newly created model
121                 */
122                 if(opType === 'create') {
123                     if(config.create.receiveIdentifier) {
124                         config.create.receiveIdentifier(data, obj.model);
125                     } else {
126                         M.Logger.log('No ID receiving operation defined.');
127                     }
128                 }
129 
130                 /*
131                 * call callback
132                 */
133                 if(obj.onSuccess) {
134                     if(obj.onSuccess.target && obj.onSuccess.action) {
135                         obj.onSuccess = that.bindToCaller(obj.onSuccess.target, obj.onSuccess.target[obj.onSuccess.action], [data]);
136                         if(opType === 'read') {
137                             that.createModelsFromResult(data, obj.onSuccess, obj);
138                         } else {
139                             obj.onSuccess();
140                         }
141                     } else if(typeof(obj.onSuccess) === 'function') {
142                         that.createModelsFromResult(data, obj.onSuccess, obj);
143                     }
144 
145                 }else {
146                     M.Logger.log('No success callback given.', M.WARN);
147                 }
148             },
149             onError: function(xhr, msg) {
150 
151                 var err = M.Error.extend({
152                     code: M.ERR_CONNECTION,
153                     msg: msg
154                 });
155 
156                 if(obj.onError && typeof(obj.onError) === 'function') {
157                     obj.onError(err);
158                 }
159                 if(obj.onError && obj.onError.target && obj.onError.action) {
160                     obj.onError = this.bindToCaller(obj.onError.target, obj.onError.target[obj.onError.action], err);
161                     obj.onError();
162                 } else if (typeof(obj.onError) !== 'function') {
163                     M.Logger.log('No error callback given.', M.WARN);
164                 }
165             },
166             beforeSend: beforeSend ? beforeSend : null
167         }).send();
168     },
169 
170     /**
171      * creates a new data provider instance with the passed configuration parameters
172      * @param obj
173      */
174     configure: function(obj) {
175         console.log('configure() called.');
176         // maybe some value checking
177         return this.extend({
178             config:obj
179         });
180     }
181 
182 });