[483] | 1 | define(["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/connect", "dojox/rpc/Rest", |
---|
| 2 | "dojox/rpc/JsonRest", "dojox/json/schema", "dojox/data/ServiceStore"], |
---|
| 3 | function(lang, declare, connect, rpcRest, rpcJsonRest, jsonSchema, ServiceStore) { |
---|
| 4 | |
---|
| 5 | var rpc = lang.getObject("dojox.rpc", true); |
---|
| 6 | |
---|
| 7 | var JsonRestStore = declare("dojox.data.JsonRestStore", ServiceStore, |
---|
| 8 | { |
---|
| 9 | constructor: function(options){ |
---|
| 10 | // summary: |
---|
| 11 | // JsonRestStore is a Dojo Data store interface to JSON HTTP/REST web |
---|
| 12 | // storage services that support read and write through GET, PUT, POST, and DELETE. |
---|
| 13 | // options: |
---|
| 14 | // Keyword arguments |
---|
| 15 | // |
---|
| 16 | // ####The *schema* parameter |
---|
| 17 | // |
---|
| 18 | // This is a schema object for this store. This should be JSON Schema format. |
---|
| 19 | // |
---|
| 20 | // ####The *service* parameter |
---|
| 21 | // |
---|
| 22 | // This is the service object that is used to retrieve lazy data and save results |
---|
| 23 | // The function should be directly callable with a single parameter of an object id to be loaded |
---|
| 24 | // The function should also have the following methods: |
---|
| 25 | // |
---|
| 26 | // - put(id,value) - puts the value at the given id |
---|
| 27 | // - post(id,value) - posts (appends) the value at the given id |
---|
| 28 | // - delete(id) - deletes the value corresponding to the given id |
---|
| 29 | // |
---|
| 30 | // Note that it is critical that the service parses responses as JSON. |
---|
| 31 | // If you are using dojox.rpc.Service, the easiest way to make sure this |
---|
| 32 | // happens is to make the responses have a content type of |
---|
| 33 | // application/json. If you are creating your own service, make sure you |
---|
| 34 | // use handleAs: "json" with your XHR requests. |
---|
| 35 | // |
---|
| 36 | // ####The *target* parameter |
---|
| 37 | // |
---|
| 38 | // This is the target URL for this Service store. This may be used in place |
---|
| 39 | // of a service parameter to connect directly to RESTful URL without |
---|
| 40 | // using a dojox.rpc.Service object. |
---|
| 41 | // |
---|
| 42 | // ####The *idAttribute* parameter |
---|
| 43 | // |
---|
| 44 | // Defaults to 'id'. The name of the attribute that holds an objects id. |
---|
| 45 | // This can be a preexisting id provided by the server. |
---|
| 46 | // If an ID isn't already provided when an object |
---|
| 47 | // is fetched or added to the store, the autoIdentity system |
---|
| 48 | // will generate an id for it and add it to the index. |
---|
| 49 | // |
---|
| 50 | // ####The *syncMode* parameter |
---|
| 51 | // |
---|
| 52 | // Setting this to true will set the store to using synchronous calls by default. |
---|
| 53 | // Sync calls return their data immediately from the calling function, so |
---|
| 54 | // callbacks are unnecessary |
---|
| 55 | // description: |
---|
| 56 | // The JsonRestStore will cause all saved modifications to be sent to the server using Rest commands (PUT, POST, or DELETE). |
---|
| 57 | // When using a Rest store on a public network, it is important to implement proper security measures to |
---|
| 58 | // control access to resources. |
---|
| 59 | // |
---|
| 60 | // On the server side implementing a REST interface means providing GET, PUT, POST, and DELETE handlers. |
---|
| 61 | // |
---|
| 62 | // - GET - Retrieve an object or array/result set, this can be by id (like /table/1) or with a |
---|
| 63 | // query (like /table/?name=foo). |
---|
| 64 | // - PUT - This should modify a object, the URL will correspond to the id (like /table/1), and the body will |
---|
| 65 | // provide the modified object |
---|
| 66 | // - POST - This should create a new object. The URL will correspond to the target store (like /table/) |
---|
| 67 | // and the body should be the properties of the new object. The server's response should include a |
---|
| 68 | // Location header that indicates the id of the newly created object. This id will be used for subsequent |
---|
| 69 | // PUT and DELETE requests. JsonRestStore also includes a Content-Location header that indicates |
---|
| 70 | // the temporary randomly generated id used by client, and this location is used for subsequent |
---|
| 71 | // PUT/DELETEs if no Location header is provided by the server or if a modification is sent prior |
---|
| 72 | // to receiving a response from the server. |
---|
| 73 | // - DELETE - This should delete an object by id. |
---|
| 74 | // |
---|
| 75 | // These articles include more detailed information on using the JsonRestStore: |
---|
| 76 | // |
---|
| 77 | // - http://www.sitepen.com/blog/2008/06/13/restful-json-dojo-data/ |
---|
| 78 | // - http://blog.medryx.org/2008/07/24/jsonreststore-overview/ |
---|
| 79 | // example: |
---|
| 80 | // A JsonRestStore takes a REST service or a URL and uses it the remote communication for a |
---|
| 81 | // read/write dojo.data implementation. A JsonRestStore can be created with a simple URL like: |
---|
| 82 | // | new JsonRestStore({target:"/MyData/"}); |
---|
| 83 | // example: |
---|
| 84 | // To use a JsonRestStore with a service, you should create a |
---|
| 85 | // service with a REST transport. This can be configured with an SMD: |
---|
| 86 | // | { |
---|
| 87 | // | services: { |
---|
| 88 | // | jsonRestStore: { |
---|
| 89 | // | transport: "REST", |
---|
| 90 | // | envelope: "URL", |
---|
| 91 | // | target: "store.php", |
---|
| 92 | // | contentType:"application/json", |
---|
| 93 | // | parameters: [ |
---|
| 94 | // | {name: "location", type: "string", optional: true} |
---|
| 95 | // | ] |
---|
| 96 | // | } |
---|
| 97 | // | } |
---|
| 98 | // | } |
---|
| 99 | // The SMD can then be used to create service, and the service can be passed to a JsonRestStore. For example: |
---|
| 100 | // | var myServices = new dojox.rpc.Service(dojo.moduleUrl("dojox.rpc.tests.resources", "test.smd")); |
---|
| 101 | // | var jsonStore = new dojox.data.JsonRestStore({service:myServices.jsonRestStore}); |
---|
| 102 | // example: |
---|
| 103 | // The JsonRestStore also supports lazy loading. References can be made to objects that have not been loaded. |
---|
| 104 | // For example if a service returned: |
---|
| 105 | // | {"name":"Example","lazyLoadedObject":{"$ref":"obj2"}} |
---|
| 106 | // And this object has accessed using the dojo.data API: |
---|
| 107 | // | var obj = jsonStore.getValue(myObject,"lazyLoadedObject"); |
---|
| 108 | // The object would automatically be requested from the server (with an object id of "obj2"). |
---|
| 109 | |
---|
| 110 | connect.connect(rpcRest._index,"onUpdate",this,function(obj,attrName,oldValue,newValue){ |
---|
| 111 | var prefix = this.service.servicePath; |
---|
| 112 | if(!obj.__id){ |
---|
| 113 | console.log("no id on updated object ", obj); |
---|
| 114 | }else if(obj.__id.substring(0,prefix.length) == prefix){ |
---|
| 115 | this.onSet(obj,attrName,oldValue,newValue); |
---|
| 116 | } |
---|
| 117 | }); |
---|
| 118 | this.idAttribute = this.idAttribute || 'id';// no options about it, we have to have identity |
---|
| 119 | |
---|
| 120 | if(typeof options.target == 'string'){ |
---|
| 121 | options.target = options.target.match(/\/$/) || this.allowNoTrailingSlash ? options.target : (options.target + '/'); |
---|
| 122 | if(!this.service){ |
---|
| 123 | this.service = rpcJsonRest.services[options.target] || |
---|
| 124 | rpcRest(options.target, true); |
---|
| 125 | // create a default Rest service |
---|
| 126 | } |
---|
| 127 | } |
---|
| 128 | |
---|
| 129 | rpcJsonRest.registerService(this.service, options.target, this.schema); |
---|
| 130 | this.schema = this.service._schema = this.schema || this.service._schema || {}; |
---|
| 131 | // wrap the service with so it goes through JsonRest manager |
---|
| 132 | this.service._store = this; |
---|
| 133 | this.service.idAsRef = this.idAsRef; |
---|
| 134 | this.schema._idAttr = this.idAttribute; |
---|
| 135 | var constructor = rpcJsonRest.getConstructor(this.service); |
---|
| 136 | var self = this; |
---|
| 137 | this._constructor = function(data){ |
---|
| 138 | constructor.call(this, data); |
---|
| 139 | self.onNew(this); |
---|
| 140 | } |
---|
| 141 | this._constructor.prototype = constructor.prototype; |
---|
| 142 | this._index = rpcRest._index; |
---|
| 143 | }, |
---|
| 144 | |
---|
| 145 | // loadReferencedSchema: Boolean |
---|
| 146 | // Will load any schemas referenced content-type header or in Link headers |
---|
| 147 | loadReferencedSchema: true, |
---|
| 148 | |
---|
| 149 | // idAsRef: Boolean |
---|
| 150 | // Treat objects in queries as partially loaded objects |
---|
| 151 | idAsRef: false, |
---|
| 152 | |
---|
| 153 | referenceIntegrity: true, |
---|
| 154 | target:"", |
---|
| 155 | |
---|
| 156 | // allowNoTrailingSlash: Boolean |
---|
| 157 | // Allow no trailing slash on target paths. This is generally discouraged since |
---|
| 158 | // it creates prevents simple scalar values from being used a relative URLs. |
---|
| 159 | // Disabled by default. |
---|
| 160 | allowNoTrailingSlash: false, |
---|
| 161 | |
---|
| 162 | //Write API Support |
---|
| 163 | newItem: function(data, parentInfo){ |
---|
| 164 | // summary: |
---|
| 165 | // adds a new item to the store at the specified point. |
---|
| 166 | // Takes two parameters, data, and options. |
---|
| 167 | // data: Object |
---|
| 168 | // The data to be added in as an item. |
---|
| 169 | data = new this._constructor(data); |
---|
| 170 | if(parentInfo){ |
---|
| 171 | // get the previous value or any empty array |
---|
| 172 | var values = this.getValue(parentInfo.parent,parentInfo.attribute,[]); |
---|
| 173 | // set the new value |
---|
| 174 | values = values.concat([data]); |
---|
| 175 | data.__parent = values; |
---|
| 176 | this.setValue(parentInfo.parent, parentInfo.attribute, values); |
---|
| 177 | } |
---|
| 178 | return data; |
---|
| 179 | }, |
---|
| 180 | deleteItem: function(item){ |
---|
| 181 | // summary: |
---|
| 182 | // deletes item and any references to that item from the store. |
---|
| 183 | // item: |
---|
| 184 | // item to delete |
---|
| 185 | |
---|
| 186 | // If the desire is to delete only one reference, unsetAttribute or |
---|
| 187 | // setValue is the way to go. |
---|
| 188 | var checked = []; |
---|
| 189 | var store = dataExtCfg._getStoreForItem(item) || this; |
---|
| 190 | if(this.referenceIntegrity){ |
---|
| 191 | // cleanup all references |
---|
| 192 | rpcJsonRest._saveNotNeeded = true; |
---|
| 193 | var index = rpcRest._index; |
---|
| 194 | var fixReferences = function(parent){ |
---|
| 195 | var toSplice; |
---|
| 196 | // keep track of the checked ones |
---|
| 197 | checked.push(parent); |
---|
| 198 | // mark it checked so we don't run into circular loops when encountering cycles |
---|
| 199 | parent.__checked = 1; |
---|
| 200 | for(var i in parent){ |
---|
| 201 | if(i.substring(0,2) != "__"){ |
---|
| 202 | var value = parent[i]; |
---|
| 203 | if(value == item){ |
---|
| 204 | if(parent != index){ // make sure we are just operating on real objects |
---|
| 205 | if(parent instanceof Array){ |
---|
| 206 | // mark it as needing to be spliced, don't do it now or it will mess up the index into the array |
---|
| 207 | (toSplice = toSplice || []).push(i); |
---|
| 208 | }else{ |
---|
| 209 | // property, just delete it. |
---|
| 210 | (dataExtCfg._getStoreForItem(parent) || store).unsetAttribute(parent, i); |
---|
| 211 | } |
---|
| 212 | } |
---|
| 213 | }else{ |
---|
| 214 | if((typeof value == 'object') && value){ |
---|
| 215 | if(!value.__checked){ |
---|
| 216 | // recursively search |
---|
| 217 | fixReferences(value); |
---|
| 218 | } |
---|
| 219 | if(typeof value.__checked == 'object' && parent != index){ |
---|
| 220 | // if it is a modified array, we will replace it |
---|
| 221 | (dataExtCfg._getStoreForItem(parent) || store).setValue(parent, i, value.__checked); |
---|
| 222 | } |
---|
| 223 | } |
---|
| 224 | } |
---|
| 225 | } |
---|
| 226 | } |
---|
| 227 | if(toSplice){ |
---|
| 228 | // we need to splice the deleted item out of these arrays |
---|
| 229 | i = toSplice.length; |
---|
| 230 | parent = parent.__checked = parent.concat(); // indicates that the array is modified |
---|
| 231 | while(i--){ |
---|
| 232 | parent.splice(toSplice[i], 1); |
---|
| 233 | } |
---|
| 234 | return parent; |
---|
| 235 | } |
---|
| 236 | return null; |
---|
| 237 | }; |
---|
| 238 | // start with the index |
---|
| 239 | fixReferences(index); |
---|
| 240 | rpcJsonRest._saveNotNeeded = false; |
---|
| 241 | var i = 0; |
---|
| 242 | while(checked[i]){ |
---|
| 243 | // remove the checked marker |
---|
| 244 | delete checked[i++].__checked; |
---|
| 245 | } |
---|
| 246 | } |
---|
| 247 | rpcJsonRest.deleteObject(item); |
---|
| 248 | |
---|
| 249 | store.onDelete(item); |
---|
| 250 | }, |
---|
| 251 | changing: function(item,_deleting){ |
---|
| 252 | // summary: |
---|
| 253 | // adds an item to the list of dirty items. This item |
---|
| 254 | // contains a reference to the item itself as well as a |
---|
| 255 | // cloned and trimmed version of old item for use with |
---|
| 256 | // revert. |
---|
| 257 | rpcJsonRest.changing(item,_deleting); |
---|
| 258 | }, |
---|
| 259 | cancelChanging : function(object){ |
---|
| 260 | // summary: |
---|
| 261 | // Removes an object from the list of dirty objects |
---|
| 262 | // This will prevent that object from being saved to the server on the next save |
---|
| 263 | // object: |
---|
| 264 | // The item to cancel changes on |
---|
| 265 | if(!object.__id){ |
---|
| 266 | return; |
---|
| 267 | } |
---|
| 268 | dirtyObjects = dirty=rpcJsonRest.getDirtyObjects(); |
---|
| 269 | for(var i=0; i<dirtyObjects.length; i++){ |
---|
| 270 | var dirty = dirtyObjects[i]; |
---|
| 271 | if(object==dirty.object){ |
---|
| 272 | dirtyObjects.splice(i, 1); |
---|
| 273 | return; |
---|
| 274 | } |
---|
| 275 | } |
---|
| 276 | |
---|
| 277 | }, |
---|
| 278 | |
---|
| 279 | setValue: function(item, attribute, value){ |
---|
| 280 | // summary: |
---|
| 281 | // sets 'attribute' on 'item' to 'value' |
---|
| 282 | |
---|
| 283 | var old = item[attribute]; |
---|
| 284 | var store = item.__id ? dataExtCfg._getStoreForItem(item) : this; |
---|
| 285 | if(jsonSchema && store.schema && store.schema.properties){ |
---|
| 286 | // if we have a schema and schema validator available we will validate the property change |
---|
| 287 | jsonSchema.mustBeValid(jsonSchema.checkPropertyChange(value,store.schema.properties[attribute])); |
---|
| 288 | } |
---|
| 289 | if(attribute == store.idAttribute){ |
---|
| 290 | throw new Error("Can not change the identity attribute for an item"); |
---|
| 291 | } |
---|
| 292 | store.changing(item); |
---|
| 293 | item[attribute]=value; |
---|
| 294 | if(value && !value.__parent){ |
---|
| 295 | value.__parent = item; |
---|
| 296 | } |
---|
| 297 | store.onSet(item,attribute,old,value); |
---|
| 298 | }, |
---|
| 299 | setValues: function(item, attribute, values){ |
---|
| 300 | // summary: |
---|
| 301 | // sets 'attribute' on 'item' to 'value' value |
---|
| 302 | // must be an array. |
---|
| 303 | |
---|
| 304 | |
---|
| 305 | if(!lang.isArray(values)){ |
---|
| 306 | throw new Error("setValues expects to be passed an Array object as its value"); |
---|
| 307 | } |
---|
| 308 | this.setValue(item,attribute,values); |
---|
| 309 | }, |
---|
| 310 | |
---|
| 311 | unsetAttribute: function(item, attribute){ |
---|
| 312 | // summary: |
---|
| 313 | // unsets 'attribute' on 'item' |
---|
| 314 | |
---|
| 315 | this.changing(item); |
---|
| 316 | var old = item[attribute]; |
---|
| 317 | delete item[attribute]; |
---|
| 318 | this.onSet(item,attribute,old,undefined); |
---|
| 319 | }, |
---|
| 320 | save: function(kwArgs){ |
---|
| 321 | // summary: |
---|
| 322 | // Saves the dirty data using REST Ajax methods. See dojo/data/api/Write for API. |
---|
| 323 | // kwArgs: |
---|
| 324 | // - global: |
---|
| 325 | // This will cause the save to commit the dirty data for all |
---|
| 326 | // JsonRestStores as a single transaction. |
---|
| 327 | // - revertOnError: |
---|
| 328 | // This will cause the changes to be reverted if there is an |
---|
| 329 | // error on the save. By default a revert is executed unless |
---|
| 330 | // a value of false is provide for this parameter. |
---|
| 331 | // - incrementalUpdates: |
---|
| 332 | // For items that have been updated, if this is enabled, the server will be sent a POST request |
---|
| 333 | // with a JSON object containing the changed properties. By default this is |
---|
| 334 | // not enabled, and a PUT is used to deliver an update, and will include a full |
---|
| 335 | // serialization of all the properties of the item/object. |
---|
| 336 | // If this is true, the POST request body will consist of a JSON object with |
---|
| 337 | // only the changed properties. The incrementalUpdates parameter may also |
---|
| 338 | // be a function, in which case it will be called with the updated and previous objects |
---|
| 339 | // and an object update representation can be returned. |
---|
| 340 | // - alwaysPostNewItems: |
---|
| 341 | // If this is true, new items will always be sent with a POST request. By default |
---|
| 342 | // this is not enabled, and the JsonRestStore will send a POST request if |
---|
| 343 | // the item does not include its identifier (expecting server assigned location/ |
---|
| 344 | // identifier), and will send a PUT request if the item does include its identifier |
---|
| 345 | // (the PUT will be sent to the URI corresponding to the provided identifier). |
---|
| 346 | |
---|
| 347 | if(!(kwArgs && kwArgs.global)){ |
---|
| 348 | (kwArgs = kwArgs || {}).service = this.service; |
---|
| 349 | } |
---|
| 350 | if("syncMode" in kwArgs ? kwArgs.syncMode : this.syncMode){ |
---|
| 351 | rpc._sync = true; |
---|
| 352 | } |
---|
| 353 | |
---|
| 354 | var actions = rpcJsonRest.commit(kwArgs); |
---|
| 355 | this.serverVersion = this._updates && this._updates.length; |
---|
| 356 | return actions; |
---|
| 357 | }, |
---|
| 358 | |
---|
| 359 | revert: function(kwArgs){ |
---|
| 360 | // summary: |
---|
| 361 | // returns any modified data to its original state prior to a save(); |
---|
| 362 | // kwArgs: |
---|
| 363 | // - global: |
---|
| 364 | // This will cause the revert to undo all the changes for all |
---|
| 365 | // JsonRestStores in a single operation. |
---|
| 366 | rpcJsonRest.revert(!(kwArgs && kwArgs.global) && this.service); |
---|
| 367 | }, |
---|
| 368 | |
---|
| 369 | isDirty: function(item){ |
---|
| 370 | // summary: |
---|
| 371 | // returns true if the item is marked as dirty. |
---|
| 372 | return rpcJsonRest.isDirty(item, this); |
---|
| 373 | }, |
---|
| 374 | isItem: function(item, anyStore){ |
---|
| 375 | // summary: |
---|
| 376 | // Checks to see if a passed 'item' |
---|
| 377 | // really belongs to this JsonRestStore. |
---|
| 378 | // item: Object |
---|
| 379 | // The value to test for being an item |
---|
| 380 | // anyStore: Boolean |
---|
| 381 | // If true, this will return true if the value is an item for any JsonRestStore, |
---|
| 382 | // not just this instance |
---|
| 383 | return item && item.__id && (anyStore || this.service == rpcJsonRest.getServiceAndId(item.__id).service); |
---|
| 384 | }, |
---|
| 385 | _doQuery: function(args){ |
---|
| 386 | var query= typeof args.queryStr == 'string' ? args.queryStr : args.query; |
---|
| 387 | var deferred = rpcJsonRest.query(this.service,query, args); |
---|
| 388 | var self = this; |
---|
| 389 | if(this.loadReferencedSchema){ |
---|
| 390 | deferred.addCallback(function(result){ |
---|
| 391 | var contentType = deferred.ioArgs && deferred.ioArgs.xhr && deferred.ioArgs.xhr.getResponseHeader("Content-Type"); |
---|
| 392 | var schemaRef = contentType && contentType.match(/definedby\s*=\s*([^;]*)/); |
---|
| 393 | if(contentType && !schemaRef){ |
---|
| 394 | schemaRef = deferred.ioArgs.xhr.getResponseHeader("Link"); |
---|
| 395 | schemaRef = schemaRef && schemaRef.match(/<([^>]*)>;\s*rel="?definedby"?/); |
---|
| 396 | } |
---|
| 397 | schemaRef = schemaRef && schemaRef[1]; |
---|
| 398 | if(schemaRef){ |
---|
| 399 | var serviceAndId = rpcJsonRest.getServiceAndId((self.target + schemaRef).replace(/^(.*\/)?(\w+:\/\/)|[^\/\.]+\/\.\.\/|^.*\/(\/)/,"$2$3")); |
---|
| 400 | var schemaDeferred = rpcJsonRest.byId(serviceAndId.service, serviceAndId.id); |
---|
| 401 | schemaDeferred.addCallbacks(function(newSchema){ |
---|
| 402 | lang.mixin(self.schema, newSchema); |
---|
| 403 | return result; |
---|
| 404 | }, function(error){ |
---|
| 405 | console.error(error); // log it, but don't let it cause the main request to fail |
---|
| 406 | return result; |
---|
| 407 | }); |
---|
| 408 | return schemaDeferred; |
---|
| 409 | } |
---|
| 410 | return undefined;//don't change anything, and deal with the stupid post-commit lint complaints |
---|
| 411 | }); |
---|
| 412 | } |
---|
| 413 | return deferred; |
---|
| 414 | }, |
---|
| 415 | _processResults: function(results, deferred){ |
---|
| 416 | // index the results |
---|
| 417 | var count = results.length; |
---|
| 418 | // if we don't know the length, and it is partial result, we will guess that it is twice as big, that will work for most widgets |
---|
| 419 | return {totalCount:deferred.fullLength || (deferred.request.count == count ? (deferred.request.start || 0) + count * 2 : count), items: results}; |
---|
| 420 | }, |
---|
| 421 | |
---|
| 422 | getConstructor: function(){ |
---|
| 423 | // summary: |
---|
| 424 | // Gets the constructor for objects from this store |
---|
| 425 | return this._constructor; |
---|
| 426 | }, |
---|
| 427 | getIdentity: function(item){ |
---|
| 428 | var id = item.__clientId || item.__id; |
---|
| 429 | if(!id){ |
---|
| 430 | return id; |
---|
| 431 | } |
---|
| 432 | var prefix = this.service.servicePath.replace(/[^\/]*$/,''); |
---|
| 433 | // support for relative or absolute referencing with ids |
---|
| 434 | return id.substring(0,prefix.length) != prefix ? id : id.substring(prefix.length); // String |
---|
| 435 | }, |
---|
| 436 | fetchItemByIdentity: function(args){ |
---|
| 437 | var id = args.identity; |
---|
| 438 | var store = this; |
---|
| 439 | // if it is an absolute id, we want to find the right store to query |
---|
| 440 | if(id.toString().match(/^(\w*:)?\//)){ |
---|
| 441 | var serviceAndId = rpcJsonRest.getServiceAndId(id); |
---|
| 442 | store = serviceAndId.service._store; |
---|
| 443 | args.identity = serviceAndId.id; |
---|
| 444 | } |
---|
| 445 | args._prefix = store.service.servicePath.replace(/[^\/]*$/,''); |
---|
| 446 | return store.inherited(arguments); |
---|
| 447 | }, |
---|
| 448 | //Notifcation Support |
---|
| 449 | |
---|
| 450 | onSet: function(){}, |
---|
| 451 | onNew: function(){}, |
---|
| 452 | onDelete: function(){}, |
---|
| 453 | |
---|
| 454 | getFeatures: function(){ |
---|
| 455 | // summary: |
---|
| 456 | // return the store feature set |
---|
| 457 | var features = this.inherited(arguments); |
---|
| 458 | features["dojo.data.api.Write"] = true; |
---|
| 459 | features["dojo.data.api.Notification"] = true; |
---|
| 460 | return features; |
---|
| 461 | }, |
---|
| 462 | |
---|
| 463 | getParent: function(item){ |
---|
| 464 | // summary: |
---|
| 465 | // Returns the parent item (or query) for the given item |
---|
| 466 | // item: |
---|
| 467 | // The item to find the parent of |
---|
| 468 | |
---|
| 469 | return item && item.__parent; |
---|
| 470 | } |
---|
| 471 | |
---|
| 472 | |
---|
| 473 | } |
---|
| 474 | ); |
---|
| 475 | JsonRestStore.getStore = function(options, Class){ |
---|
| 476 | // summary: |
---|
| 477 | // Will retrieve or create a store using the given options (the same options |
---|
| 478 | // that are passed to JsonRestStore constructor. Returns a JsonRestStore instance |
---|
| 479 | // options: |
---|
| 480 | // See the JsonRestStore constructor |
---|
| 481 | // Class: |
---|
| 482 | // Constructor to use (for creating stores from JsonRestStore subclasses). |
---|
| 483 | // This is optional and defaults to JsonRestStore. |
---|
| 484 | if(typeof options.target == 'string'){ |
---|
| 485 | options.target = options.target.match(/\/$/) || options.allowNoTrailingSlash ? |
---|
| 486 | options.target : (options.target + '/'); |
---|
| 487 | var store = (rpcJsonRest.services[options.target] || {})._store; |
---|
| 488 | if(store){ |
---|
| 489 | return store; |
---|
| 490 | } |
---|
| 491 | } |
---|
| 492 | return new (Class || JsonRestStore)(options); |
---|
| 493 | }; |
---|
| 494 | |
---|
| 495 | var dataExtCfg = lang.getObject("dojox.data",true); |
---|
| 496 | dataExtCfg._getStoreForItem = function(item){ |
---|
| 497 | if(item.__id){ |
---|
| 498 | var serviceAndId = rpcJsonRest.getServiceAndId(item.__id); |
---|
| 499 | if(serviceAndId && serviceAndId.service._store){ |
---|
| 500 | return serviceAndId.service._store; |
---|
| 501 | }else{ |
---|
| 502 | var servicePath = item.__id.toString().match(/.*\//)[0]; |
---|
| 503 | return new JsonRestStore({target:servicePath}); |
---|
| 504 | } |
---|
| 505 | } |
---|
| 506 | return null; |
---|
| 507 | }; |
---|
| 508 | var jsonRefConfig = lang.getObject("dojox.json.ref", true); |
---|
| 509 | jsonRefConfig._useRefs = true; // Use referencing when identifiable objects are referenced |
---|
| 510 | |
---|
| 511 | return JsonRestStore; |
---|
| 512 | }); |
---|