From ee2fe9a4245d6f2f4107b455be24605789f9f84d Mon Sep 17 00:00:00 2001 From: Greg Beeley Date: Mon, 3 Aug 2020 12:46:00 -0600 Subject: [PATCH 1/3] CXLib: adding mssUserError() --- centrallix-lib/include/mtsession.h | 1 + centrallix-lib/src/mtsession.c | 34 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/centrallix-lib/include/mtsession.h b/centrallix-lib/include/mtsession.h index ec2d975b7..385c83e66 100644 --- a/centrallix-lib/include/mtsession.h +++ b/centrallix-lib/include/mtsession.h @@ -80,6 +80,7 @@ int mssErrorErrno(int clr, char* module, char* message, ...); int mssClearError(); int mssPrintError(pFile fd); int mssStringError(pXString str); +int mssUserError(pXString str); #endif diff --git a/centrallix-lib/src/mtsession.c b/centrallix-lib/src/mtsession.c index 06c29a255..a06cd493e 100644 --- a/centrallix-lib/src/mtsession.c +++ b/centrallix-lib/src/mtsession.c @@ -702,6 +702,40 @@ mssStringError(pXString str) } +/*** mssUserError() - returns a user-friendly version of the error message + *** stack (i.e., without the module codes). + ***/ +int +mssUserError(pXString str) + { + int i; + pMtSession s; + char* item; + char* colon; + + /** Get session. **/ + s = (pMtSession)thGetParam(NULL,"mss"); + if (!s) return -1; + + /** Create a space-separated string of the messages, without module codes **/ + for(i=s->ErrList.nItems-1;i>=0;i--) + { + item = (char*)(s->ErrList.Items[i]); + if (item) + { + colon = strchr(item, ':'); + if (colon) + item = colon + 2; + xsConcatenate(str, item, -1); + if (i > 0) + xsConcatenate(str, " ", 1); + } + } + + return 0; + } + + /*** mssSetParamPtr - sets a session parameter, given an opaque *** pointer. For strings, use mssSetParam(). ***/ From eb4751b9d5d170bed841a66a580d5e2cc8106e19 Mon Sep 17 00:00:00 2001 From: Greg Beeley Date: Mon, 3 Aug 2020 12:48:59 -0600 Subject: [PATCH 2/3] UX filesystem driver: adding autoname support (name supplied via attribute) --- centrallix/osdrivers/objdrv_ux.c | 205 ++++++++++++++++++++----------- 1 file changed, 131 insertions(+), 74 deletions(-) diff --git a/centrallix/osdrivers/objdrv_ux.c b/centrallix/osdrivers/objdrv_ux.c index b501817e3..54be81ae3 100644 --- a/centrallix/osdrivers/objdrv_ux.c +++ b/centrallix/osdrivers/objdrv_ux.c @@ -551,69 +551,92 @@ uxdOpen(pObject obj, int mask, pContentType systype, char* usrtype, pObjTrxTree* { /** Access a path element **/ path = obj_internal_PathPart(tmp_path,0,i); - if (stat(path,&(inf->Fileinfo)) < 0) - { - /** Couldn't get it? if not last or not create, error. **/ - e = errno; - if (e != ENOENT || (!(obj->Mode & O_CREAT) || i != tmp_path->nElements)) - { - mssErrorErrno(1,"UXD","Cannot access UNIX path component '%s'",path); - nmFree(inf, sizeof(UxdData)); - if (is_new_node) nmFree(node, sizeof(UxdNode)); - nmFree(tmp_path, sizeof(Pathname)); - snnode->OpenCnt--; - return NULL; - } - /** Try to make a directory or file? **/ - if (!strcmp(usrtype, "system/directory")) + /** Autoname this path element? **/ + if (i == tmp_path->nElements && (obj->Mode & OBJ_O_AUTONAME) && (obj->Mode & O_CREAT)) + { + /** wait to actually create it **/ + obj->SubCnt = 1 + i - basecnt; + strcpy(inf->RealPathname, path); + if (strcmp(usrtype, "system/directory") == 0) + inf->Flags |= UXD_F_ISDIR; + obj->Flags |= OBJ_F_CREATED; + + /** Exit the search loop. **/ + break; + } + else + { + /** non-autoname: try to access the path element **/ + if (stat(path,&(inf->Fileinfo)) < 0) { - if (mkdir(path, obj->Mode) < 0) - { - mssErrorErrno(1,"UXD","Cannot create directory '%s'",path); - nmFree(inf, sizeof(UxdData)); - if (is_new_node) nmFree(node, sizeof(UxdNode)); - nmFree(tmp_path, sizeof(Pathname)); - snnode->OpenCnt--; - return NULL; + /** Couldn't get it? if not last or not create, error. **/ + e = errno; + if (e != ENOENT || (!(obj->Mode & O_CREAT) || i != tmp_path->nElements)) + { + mssErrorErrno(1,"UXD","Cannot access UNIX path component '%s'",path); + nmFree(inf, sizeof(UxdData)); + if (is_new_node) nmFree(node, sizeof(UxdNode)); + nmFree(tmp_path, sizeof(Pathname)); + snnode->OpenCnt--; + return NULL; } - } - else - { - fd = fdOpen(path, obj->Mode, mask); - if (!fd) - { - mssErrorErrno(1,"UXD","Cannot create file '%s'",path); - nmFree(inf, sizeof(UxdData)); - if (is_new_node) nmFree(node, sizeof(UxdNode)); - nmFree(tmp_path, sizeof(Pathname)); - snnode->OpenCnt--; - return NULL; + + /** Try to make a directory or file? **/ + if (!strcmp(usrtype, "system/directory")) + { + if (mkdir(path, obj->Mode) < 0) + { + mssErrorErrno(1,"UXD","Cannot create directory '%s'",path); + nmFree(inf, sizeof(UxdData)); + if (is_new_node) nmFree(node, sizeof(UxdNode)); + nmFree(tmp_path, sizeof(Pathname)); + snnode->OpenCnt--; + return NULL; + } + } + else + { + fd = fdOpen(path, obj->Mode, mask); + if (!fd) + { + mssErrorErrno(1,"UXD","Cannot create file '%s'",path); + nmFree(inf, sizeof(UxdData)); + if (is_new_node) nmFree(node, sizeof(UxdNode)); + nmFree(tmp_path, sizeof(Pathname)); + snnode->OpenCnt--; + return NULL; + } + inf->fd = fd; + inf->Flags |= UXD_F_ISOPEN; } - inf->fd = fd; - inf->Flags |= UXD_F_ISOPEN; + obj->Flags |= OBJ_F_CREATED; + stat(path,&(inf->Fileinfo)); } - obj->Flags |= OBJ_F_CREATED; - stat(path,&(inf->Fileinfo)); - } - /** Was it a file? Or last element? That ends the UXD's part of the path **/ - if (!(S_ISDIR(inf->Fileinfo.st_mode)) || i == tmp_path->nElements) - { - /** Last item is a directory? **/ - if (S_ISDIR(inf->Fileinfo.st_mode)) inf->Flags |= UXD_F_ISDIR; + /** Was it a file? Or final element? That ends the UXD's part of the path **/ + if (!(S_ISDIR(inf->Fileinfo.st_mode)) || i == tmp_path->nElements) + { + /** Last item is a directory? **/ + if (S_ISDIR(inf->Fileinfo.st_mode)) + inf->Flags |= UXD_F_ISDIR; - /** Set the count of elements we "consumed" **/ - obj->SubCnt = 1 + i - basecnt; - strcpy(inf->RealPathname, path); + /** Set the count of elements we "consumed" **/ + obj->SubCnt = 1 + i - basecnt; + strcpy(inf->RealPathname, path); - /** Exit the search loop. **/ - break; + /** Exit the search loop. **/ + break; + } } } - /** Access called for RDWR but we can't open it RDWR? **/ - if ((obj->Mode & O_ACCMODE) == O_RDWR && fdAccess(inf->RealPathname,W_OK) < 0) + /** Access called for RDWR but we can't open it RDWR? + ** We skip this check if we're autoname creating the final element. + **/ + if ((obj->Mode & O_ACCMODE) == O_RDWR && + !(i == tmp_path->nElements && (obj->Mode & OBJ_O_AUTONAME) && (obj->Mode & O_CREAT)) && + fdAccess(inf->RealPathname,W_OK) < 0) { obj->Mode &= ~O_ACCMODE; obj->Mode |= O_RDONLY; @@ -639,6 +662,38 @@ uxdOpen(pObject obj, int mask, pContentType systype, char* usrtype, pObjTrxTree* } +/*** uxd_internal_CheckOpen - see if we need to open the file or directory + ***/ +int +uxd_internal_CheckOpen(pUxdData inf) + { + + /** Ok, do we need to open the file? **/ + if (!(inf->Flags & UXD_F_ISOPEN)) + { + /** Need to generate autoname? **/ + if ((inf->Mode & OBJ_O_AUTONAME) && (inf->Mode & O_CREAT)) + { + if (strrchr(inf->RealPathname, '*') == inf->RealPathname + strlen(inf->RealPathname) - 1) + { + mssError(1, "UXD", "Cannot read or write the file until a name is given"); + return -1; + } + } + + inf->fd = fdOpen(inf->RealPathname, inf->Mode & ~OBJ_O_CXOPTS, inf->Mask); + if (!(inf->fd)) + { + mssErrorErrno(1,"UXD","Could not open file"); + return -1; + } + inf->Flags |= UXD_F_ISOPEN; + } + + return 0; + } + + /*** uxdClose - close an open file or directory. ***/ int @@ -752,17 +807,9 @@ uxdRead(void* inf_v, char* buffer, int maxcnt, int offset, int flags, pObjTrxTre return -1; } - /** Ok, do we need to open the dumb thing? **/ - if (!(inf->Flags & UXD_F_ISOPEN)) - { - inf->fd = fdOpen(inf->RealPathname, inf->Mode, inf->Mask); - if (!(inf->fd)) - { - mssErrorErrno(1,"UXD","Could not read from file"); - return -1; - } - inf->Flags |= UXD_F_ISOPEN; - } + /** Need to open it? */ + if (uxd_internal_CheckOpen(inf) < 0) + return -1; /** Now, do the read. **/ rval = fdRead(inf->fd, buffer, maxcnt, offset, flags); @@ -786,17 +833,9 @@ uxdWrite(void* inf_v, char* buffer, int cnt, int offset, int flags, pObjTrxTree* return -1; } - /** Ok, do we need to open the dumb thing? **/ - if (!(inf->Flags & UXD_F_ISOPEN)) - { - inf->fd = fdOpen(inf->RealPathname, inf->Mode, inf->Mask); - if (!(inf->fd)) - { - mssErrorErrno(1,"UXD","Could not read from file"); - return -1; - } - inf->Flags |= UXD_F_ISOPEN; - } + /** Need to open it? */ + if (uxd_internal_CheckOpen(inf) < 0) + return -1; /** Now, do the write. **/ rval = fdWrite(inf->fd, buffer, cnt, offset, flags); @@ -1190,6 +1229,24 @@ uxdSetAttrValue(void* inf_v, char* attrname, int datatype, pObjData val, pObjTrx mssError(1,"UXD","Type mismatch accessing attribute '%s' (should be string)", attrname); return -1; } + if (!(inf->Flags & UXD_F_ISOPEN) && (inf->Mode & OBJ_O_AUTONAME) && (inf->Mode & O_CREAT) && val) + { + if (strlen(val->String) + strlen(inf->RealPathname) + 1 >= sizeof(inf->RealPathname)) + { + mssError(1, "UXD", "Pathname exceeded internal representation: name=%s", val->String); + return -1; + } + + if (strrchr(inf->RealPathname, '*') == inf->RealPathname + strlen(inf->RealPathname) - 1) + { + strcpy(strrchr(inf->RealPathname, '*'), val->String); + } + else + { + mssError(1, "UXD", "Improperly formatted autoname pathname"); + return -1; + } + } /*if (!strcmp(inf->Obj->Pathname->Pathbuf,".")) return -1; if (strlen(inf->Obj->Pathname->Pathbuf) - strlen(strrchr(inf->Obj->Pathname->Pathbuf,'/')) + From a10e77e29e3b192f9c3bc052c48b810cfa7dfb7e Mon Sep 17 00:00:00 2001 From: Greg Beeley Date: Mon, 3 Aug 2020 12:50:26 -0600 Subject: [PATCH 3/3] Beginning of work on using JSON as the client data format --- centrallix-lib/include/xhandle.h | 2 + centrallix-os/sys/js/ht_render.js | 7 +- centrallix-os/sys/js/ht_utils_iface.js | 2 +- centrallix-os/sys/js/htdrv_fileupload.js | 0 centrallix-os/sys/js/htdrv_form.js | 186 ++- centrallix-os/sys/js/htdrv_imagebutton.js | 4 + centrallix-os/sys/js/htdrv_map.js | 0 centrallix-os/sys/js/htdrv_menu.js | 2 +- centrallix-os/sys/js/htdrv_osrc.js | 1833 ++++++++++++--------- centrallix-os/sys/js/htdrv_page.js | 144 +- centrallix-os/sys/js/htdrv_pane.js | 23 +- centrallix-os/sys/js/htdrv_tab.js | 7 +- centrallix-os/sys/js/htdrv_table.js | 90 +- centrallix-os/sys/js/htdrv_textbutton.js | 6 +- centrallix-os/sys/js/htdrv_window.js | 8 + centrallix-sysdoc/JSON-REST.txt | 4 + centrallix-sysdoc/OSRC-JSON-Data.txt | 53 + centrallix/htmlgen/htdrv_osrc.c | 7 + centrallix/htmlgen/htdrv_page.c | 2 + centrallix/htmlgen/htdrv_rule.c | 9 + centrallix/htmlgen/htdrv_table.c | 12 +- centrallix/include/obj.h | 1 + centrallix/netdrivers/net_http.h | 9 + centrallix/netdrivers/net_http_osml.c | 254 ++- centrallix/netdrivers/net_http_rest.c | 78 +- centrallix/objectsystem/obj_datatypes.c | 33 +- centrallix/osdrivers/objdrv_datafile.c | 2 +- centrallix/osdrivers/objdrv_query.c | 2 +- centrallix/report/prtmgmt_v3_lm_table.c | 2 +- centrallix/report/prtmgmt_v3_lm_text.c | 2 +- centrallix/report/prtmgmt_v3_od_ps.c | 4 +- centrallix/utility/hints.c | 5 +- 32 files changed, 1774 insertions(+), 1019 deletions(-) mode change 100755 => 100644 centrallix-os/sys/js/htdrv_fileupload.js mode change 100755 => 100644 centrallix-os/sys/js/htdrv_map.js mode change 100755 => 100644 centrallix-os/sys/js/htdrv_page.js create mode 100644 centrallix-sysdoc/OSRC-JSON-Data.txt diff --git a/centrallix-lib/include/xhandle.h b/centrallix-lib/include/xhandle.h index 0d26ce549..df50c048a 100644 --- a/centrallix-lib/include/xhandle.h +++ b/centrallix-lib/include/xhandle.h @@ -31,9 +31,11 @@ #ifndef __NO_LONGLONG typedef unsigned long long int handle_t; #define XHN_HANDLE_PRT "%16.16llX" +#define XHN_HANDLE_DPRT "%llu" #else typedef unsigned long int handle_t; #define XHN_HANDLE_PRT "%8.8X" +#define XHN_HANDLE_DPRT "%u" #endif #define XHN_INVALID_HANDLE ((handle_t)(0)) diff --git a/centrallix-os/sys/js/ht_render.js b/centrallix-os/sys/js/ht_render.js index 3074bf701..3bd8258d5 100644 --- a/centrallix-os/sys/js/ht_render.js +++ b/centrallix-os/sys/js/ht_render.js @@ -1195,7 +1195,12 @@ function htr_getbgimage(l) if (cx__capabilities.Dom0NS) return l.background.src; else if (cx__capabilities.Dom1HTML) - return l.style.backgroundImage; + { + var i = l.style.backgroundImage; + if (i && i.substr(0,4) == 'url(') + i = i.substr(5, i.length - 7); + return i; + } return null; } diff --git a/centrallix-os/sys/js/ht_utils_iface.js b/centrallix-os/sys/js/ht_utils_iface.js index 01d6b8f31..372895aee 100644 --- a/centrallix-os/sys/js/ht_utils_iface.js +++ b/centrallix-os/sys/js/ht_utils_iface.js @@ -642,7 +642,7 @@ function ifEvent() // key/value pairs. function ifvalue_checkexist(n) { - if (!this._Attributes[n]) + if (!(n in this._Attributes)) this._Attributes[n] = {name:n, exists:false, obj:this.obj, instance:this, watchlist:[], get:null, set:null}; return this._Attributes[n]; } diff --git a/centrallix-os/sys/js/htdrv_fileupload.js b/centrallix-os/sys/js/htdrv_fileupload.js old mode 100755 new mode 100644 diff --git a/centrallix-os/sys/js/htdrv_form.js b/centrallix-os/sys/js/htdrv_form.js index ff007a4f8..75f7b7084 100644 --- a/centrallix-os/sys/js/htdrv_form.js +++ b/centrallix-os/sys/js/htdrv_form.js @@ -263,36 +263,37 @@ function form_cb_is_discard_ready() function form_load_fields(data, no_clear, modify, onefield) { - var name_to_id = []; - if (!data) { + // No data supplied -- load hints based on previous information, if available. for(var i in this.elements) + { if (this.last_hints[this.elements[i].fieldname]) cx_set_hints(this.elements[i], this.last_hints[this.elements[i].fieldname], 'data'); else cx_set_hints(this.elements[i], '', 'data'); - //return; - } - else - { - for(var j in data) - { - if (data[j].oid) - name_to_id[data[j].oid] = j; } } + // Loop through the form elements to load the data. for(var i in this.elements) { - if (onefield && onefield != this.elements[i].fieldname) continue; + var field = this.elements[i].fieldname; - if (!this.elements[i]._form_type && - (typeof name_to_id[this.elements[i].fieldname]) != 'undefined' && - (typeof data[name_to_id[this.elements[i].fieldname]].type) != 'undefined') - this.elements[i]._form_type = data[name_to_id[this.elements[i].fieldname]].type; + // are we loading just one field instead of all of them? + if (onefield && onefield != field) + continue; - if (this.elements[i].fieldname == '__position') + // Make a note of the data type. + if (!this.elements[i]._form_type && data) + { + var t = data.getAttrType(field); + if (t !== null) + this.elements[i]._form_type = t; + } + + // Synthetic "position" form element? + if (field == '__position') { var txt = ""; if (this.mode == "New") @@ -312,18 +313,24 @@ function form_load_fields(data, no_clear, modify, onefield) } this.elements[i].setvalue(txt); } - else if ((typeof name_to_id[this.elements[i].fieldname]) != 'undefined' && (typeof data[name_to_id[this.elements[i].fieldname]].value) != 'undefined') - { - var id = name_to_id[this.elements[i].fieldname]; - this.elements[i].setvalue(data[id].value); - if (modify) - this.elements[i]._form_IsChanged = true; - cx_set_hints(this.elements[i], data[id].hints, 'data'); - this.last_hints[this.elements[i].fieldname] = data[id].hints; - } - else if (!no_clear) + else { - this.elements[i].clearvalue(); + // Normal field -- check to see if it exists in the data. + var attr = null; + if (data && (attr = data.getAttr(field))) + { + // It does exist -- set its value and presentation hints + this.elements[i].setvalue(attr.get()); + if (modify) + this.elements[i]._form_IsChanged = true; + cx_set_hints(this.elements[i], attr.getHints(), 'data'); + this.last_hints[field] = attr.getHints(); + } + else if (!no_clear) + { + // It does not exist - clear it. + this.elements[i].clearvalue(); + } } } } @@ -374,19 +381,20 @@ function form_cb_object_available(data) if (data) { this.ClearAll(true); - for(var j in data) + var alist = data.getAttrList(); + for(var aname in alist) { - if (!this.ifcProbe(ifValue).Exists(data[j].oid, true)) + if (!this.ifcProbe(ifValue).Exists(aname, true)) { - this.ifcProbe(ifValue).Add(data[j].oid, form_cb_getvalue); - this.valuelist.push(data[j].oid); + this.ifcProbe(ifValue).Add(aname, form_cb_getvalue); + this.valuelist.push(aname); } - this.ifcProbe(ifValue).Changing(data[j].oid, data[j].value, true); + this.ifcProbe(ifValue).Changing(aname, data.getAttrValue(aname), true); } this.data=data; - if (data.__osrc_is_last || (this.didsearch && data.id == this.recid)) - this.lastrecid = data.id; - this.recid = data.id; + if (data.__osrc_is_last || (this.didsearch && data.getID() == this.recid)) + this.lastrecid = data.getID(); + this.recid = data.getID(); this.LoadFields(this.data); @@ -920,6 +928,7 @@ function form_change_mode(newmode, reason) // on New or Modify, call appropriate hints routines if (newmode == 'New') { + this.BeginTransaction(); for(var e in this.elements) { if (this.elements[e].cx_hints) cx_hints_setup(this.elements[e]); @@ -932,6 +941,7 @@ function form_change_mode(newmode, reason) { this.LoadFields(templ, true, true); } + this.CommitTransaction(); } else if (newmode == 'Modify') { @@ -970,8 +980,34 @@ function form_send_event(event, eparam) evobj.PrevStatus = this.oldmode; evobj.IsUnsaved = this.IsUnsaved; evobj.is_savable = this.is_savable; - cn_activate(this, event, evobj); - delete evobj; + if (this.in_transaction) + this.trx_events.push({event: event, evobj: evobj}); + else + cn_activate(this, event, evobj); + } + +function form_begin_transaction() + { + this.trx_events = []; + this.in_transaction = true; + } + +function form_commit_transaction() + { + // scan for duplicate data change events + var found_datachange = false; + for(var i=0; i= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) + item += ch; + else + { + if (item.length > 0) + { + switch(item) + { + case 'dd': str += ((this.day < 10)?'0':'') + this.day; break; + case 'MMM': str += (new Intl.DateTimeFormat([], {month: 'short'})).format(this.dateobj); break; + case 'yyyy': str += this.year; break; + case 'HH': str += ((this.hour < 10)?'0':'') + this.hour; break; + case 'mm': str += ((this.minute < 10)?'0':'') + this.minute; break; + } + } + str += ch; + item = ''; + } + } + return str; + } + +$CX.Types.DateTime.prototype.toString = function() + { + return this.format($CX.Globals.dateFormat); + } + +// +// osrcObject methods +// +$CX.Types.osrcObject.prototype.getAttrCount = function() + { + var acnt = 0; + for(var aname in this) + if (typeof this[aname] == 'object' && $CX.Types.osrcAttr.prototype.isPrototypeOf(this[aname])) + acnt++; + return acnt; + } + +$CX.Types.osrcObject.prototype.getAttrList = function() + { + var alist = {}; + for(var aname in this) + if (typeof this[aname] == 'object' && $CX.Types.osrcAttr.prototype.isPrototypeOf(this[aname])) + alist[aname] = true; + return alist; + } + +$CX.Types.osrcObject.prototype.getID = function() + { + return this.__cx_id; + } + +$CX.Types.osrcObject.prototype.setID = function(id) + { + return this.__cx_id = id; + } + +$CX.Types.osrcObject.prototype.setJoin = function(join) + { + return this.__cx_joinstring = join; + } + +$CX.Types.osrcObject.prototype.getJoin = function() + { + return this.__cx_joinstring; + } + +$CX.Types.osrcObject.prototype.getAttr = function(a) + { + if (a != '__cx_joinstring' && a != '__cx_id' && a != '__cx_handle' && a != '__proto__' && typeof this[a] == 'object') + { + if (!this[a].a) this[a].a = a; + return this[a]; + } + return null; + } + +$CX.Types.osrcObject.prototype.getAttrValue = function(a) + { + var attr = this.getAttr(a); + return attr?attr.get():null; + } + +$CX.Types.osrcObject.prototype.getAttrType = function(a) + { + var attr = this.getAttr(a); + return attr?attr.getType():null; + } + +$CX.Types.osrcObject.prototype.copyFrom = function(o) + { + if (typeof o == 'object') + { + if ($CX.Types.osrcObject.prototype.isPrototypeOf(o)) + { + // osrcObject type + var attrlist = o.getAttrList(); + for(var a in attrlist) + { + this[a] = new $CX.Types.osrcAttr(o.getAttr(a)); + } + } + else + { + // Generic object -- just copy property names and values + for(var p in o) + { + this[p] = new $CX.Types.osrcAttr(o[p]); + } + } + } + } + +// Either invoke with newAttr(attrname, value, type) or as newAttr(attrname, attrobj) +$CX.Types.osrcObject.prototype.newAttr = function(a,v,t) + { + if (a) + { + this[a] = new $CX.Types.osrcAttr(v); + if (t) this[a].t = t; + return this[a]; + } + } + +$CX.Types.osrcObject.prototype.removeAttr = function(a) + { + var a = this.getAttr(a); + if (a) + { + delete this[a]; + } + return a; + } + +// +// osrcAttr methods +// +$CX.Types.osrcAttr.prototype.get = function() + { + return this.v; + } + +$CX.Types.osrcAttr.prototype.set = function(v) + { + return this.v = v; + } + +$CX.Types.osrcAttr.prototype.getSystem = function() + { + return this.y; + } + +$CX.Types.osrcAttr.prototype.getType = function() + { + return this.t; + } + +$CX.Types.osrcAttr.prototype.setType = function(t) + { + return this.t = t; + } + +$CX.Types.osrcAttr.prototype.getHints = function() + { + return this.h; + } + +$CX.Types.osrcAttr.prototype.getFilter = function() + { + if (!this.__cx_filter) + this.__cx_filter = {}; + return this.__cx_filter; + } + function osrc_init_query() { @@ -27,23 +330,27 @@ function osrc_action_order_object(aparam) //order) this.ifcProbe(ifAction).Invoke("QueryObject", {query:this.queryobject, client:null, ro:this.readonly}); } + function osrc_criteria_from_aparam(aparam) { - var qo = []; - var t; - var v; - qo.joinstring = 'AND'; + var qo = new $CX.Types.osrcObject(); + qo.setJoin('AND'); + + // Add a criteria item for each aparam item for (var i in aparam) { - if (i == 'cx__enable_lists') continue; - if (i == 'cx__case_insensitive') continue; + var v = aparam[i]; + var t = 'undefined'; + + if (i == 'cx__enable_lists' || i == 'cx__case_insensitive' || i == '_Origin' || i == '_EventName') + continue; if (i == 'joinstring' && (new String(aparam[i])).toLowerCase() == 'or') { - qo.joinstring = 'OR'; + qo.setJoin('OR'); continue; } - v = aparam[i]; - if (i == '_Origin' || i == '_EventName') continue; + + // Determine type if (typeof v == 'string' && (new String(v)).indexOf(',') > 0 && aparam.cx__enable_lists) { t = 'stringarray'; @@ -57,11 +364,15 @@ function osrc_criteria_from_aparam(aparam) t = 'integer'; else t = 'string'; - qo.push({oid:i, value:v, type:t}); + + // Add the attribute to the query object + qo.newAttr(i, v, t); } + return qo; } + function osrc_action_query_param(aparam) { this.init = true; @@ -93,19 +404,14 @@ function osrc_action_refresh(aparam) this.doing_refresh = true; // Keep track of current object by name + this.refresh_objname = null; if (this.replica[this.CurrentRecord]) { - for(var j=0; j 0) { var filt = this.MakeFilter(filter); if (filt) { - if(firstone) - statement+=sep; + if (firstone) + statement += sep; else - statement+=' '+q.joinstring+' '; + statement += ' ' + q.getJoin() + ' '; statement+=filt; } } @@ -406,17 +723,20 @@ function osrc_query_object_handler(aparam) function osrc_make_filter_colref(col) { - return (col.obj?(':"' + col.obj + '"'):'') + ':"' + col.oid + '"'; + var filt = col.getFilter(); + return (filt.obj?(':"' + filt.obj + '"'):'') + ':"' + col.a + '"'; } -function osrc_make_filter_integer(col,val) +function osrc_make_filter_integer(col, val) { - if (val == null && (typeof col.nullisvalue == 'undefined' || col.nullisvalue == true)) + var filt = col.getFilter(); + + if (val == null && (typeof filt.nullisvalue == 'undefined' || filt.nullisvalue == true)) return this.MFCol(col) + ' is null '; else if (val == null) return this.MFCol(col) + ' = null '; - else if (!col.plainsearch && typeof val != 'number' && (new String(val)).search(/-/)>=0) + else if (!filt.plainsearch && typeof val != 'number' && (new String(val)).search(/-/)>=0) { var parts = (new String(val)).split(/-/); return '(' + this.MFCol(col) + ' >=' + parts[0] + ' AND ' + this.MFCol(col) + ' <=' + parts[1] + ')'; @@ -431,13 +751,15 @@ function osrc_make_filter_string(col, val, icase) var str = ''; var ifunc = ''; var colref = this.MFCol(col); + var filt = col.getFilter(); + if (icase) ifunc = 'upper'; - if (val == null && (typeof col.nullisvalue == 'undefined' || col.nullisvalue == true)) + if (val == null && (typeof filt.nullisvalue == 'undefined' || filt.nullisvalue == true)) str=colref + ' is null '; else if (val == null) str=colref + ' = null '; - else if (col.plainsearch) + else if (filt.plainsearch) str=ifunc + '(' + colref + ')='+ifunc+'("'+val+'")'; else if (val.search(/^\*.+\*$/)>=0) @@ -465,6 +787,7 @@ function osrc_make_filter_string(col, val, icase) } else str=ifunc + '(' + colref + ')='+ifunc+'("'+val+'")'; + return str; } @@ -474,194 +797,193 @@ function osrc_make_filter(q) var firstone=true; var statement=''; var isnot; - for(var i in q) + var al = q.getAttrList(); + for(var a in al) { + var col = q.getAttr(a); + var filt = col.getFilter(); isnot = false; - if(i!='oid' && i!='joinstring') + var str; + if (filt.force_empty) + { + str = " (" + this.MFCol(col) + " = null and 1 == 0) "; + } + else { - var str; - if (q[i].force_empty) + var val=col.get(); + + if (typeof val == 'string') + val = new String(val); + + if (val && val.substring && val.substring(0,1) == '~') { - //str = '1 == 0'; - str = " (" + this.MFCol(q[i]) + " = null and 1 == 0) "; + val = val.substring(1); + isnot = true; } - else if(q[i].joinstring) + else if (val && val.length && val[0] && val[0].substring && val[0].substring(0,1) == '~') { - str=this.MakeFilter(q[i]); + val[0] = val[0].substring(1); + isnot = true; } - else - { - var val=q[i].value; - //if (val == null) continue; - if (typeof val == 'string') - val = new String(val); + // Use a "remembered" type for this attribute, if not supplied? + if (typeof col.getType() == "undefined" && this.type_list[a]) + col.setType(this.type_list[a]); - if (val && val.substring && val.substring(0,1) == '~') - { - val = val.substring(1); - isnot = true; - } - else if (val && val.length && val[0] && val[0].substring && val[0].substring(0,1) == '~') - { - val[0] = val[0].substring(1); - isnot = true; - } + var colref = this.MFCol(col); - if (typeof q[i].type == "undefined" && this.type_list[q[i].oid]) - q[i].type = this.type_list[q[i].oid]; + switch(col.getType()) + { + case 'criteria': + str = this.MakeFilter(val); + break; - var colref = this.MFCol(q[i]); + case 'integer': + str = this.MakeFilterInteger(col, val); + break; - switch(q[i].type) - { - case 'integer': - str = this.MakeFilterInteger(q[i], val); - break; - - case 'integerarray': - if (val == null && (typeof col.nullisvalue == 'undefined' || col.nullisvalue == true)) - str = colref + 'is null '; - else if (val == null) - str = colref + ' = null '; - else if (val.length) + case 'integerarray': + if (val == null && (typeof filt.nullisvalue == 'undefined' || filt.nullisvalue == true)) + str = colref + 'is null '; + else if (val == null) + str = colref + ' = null '; + else if (val.length) + { + str = "("; + for(var j=0;j=') - str+=' >= \"'+val[j].substring(2)+'\"'; - else if(val[j].substring(0,2)=='<=') - str+=' <= \"'+val[j].substring(2)+'\"'; - else if(val[j].substring(0,2)=='=>') - str+=' >= \"'+val[j].substring(2)+'\"'; - else if(val[j].substring(0,2)=='=<') - str+=' <= \"'+val[j].substring(2)+'\"'; - else if(val[j].substring(0,1)=='>') - str+=' > \"'+val[j].substring(1)+'\"'; - else if(val[j].substring(0,1)=='<') - str+=' < \"'+val[j].substring(1)+'\"'; - else if(val[j].substring(0,1)=='=') - str+=' = \"'+val[j].substring(1)+'\"'; - } - str+=')'; - break; - - case 'string': - case 'istring': - str = this.MakeFilterString(q[i], val, q[i].type == 'istring'); - break; - - default: - //htr_alert(val, 1); - if(!val || typeof val.substring == 'undefined') // assume integer - str = this.MakeFilterInteger(q[i], val); - else if(val.substring(0,2)=='>=') - str=colref + ' >= '+val.substring(2); - else if(val.substring(0,2)=='<=') - str=colref + ' <= '+val.substring(2); - else if(val.substring(0,2)=='=>') - str=colref + ' >= '+val.substring(2); - else if(val.substring(0,2)=='=<') - str=colref + ' <= '+val.substring(2); - else if(val.substring(0,1)=='>') - str=colref + ' > '+val.substring(1); - else if(val.substring(0,1)=='<') - str=colref + ' < '+val.substring(1); - else if(val.indexOf('-')>=0) - { - //assume integer range in string - var ind = val.indexOf('-'); - var val1 = val.substring(0,ind); - var val2 = val.substring(ind+1); - str='(' + colref + ' >='+val1+' AND ' + colref + ' <='+val2+')'; - } - else - { - str = this.MakeFilterString(q[i], val); - } - break; - } - } - if (isnot) - str = "not (" + str + ")"; - if(firstone) - { - statement+=' ('+str+')'; - } - else - { - statement+=' '+q.joinstring+' ('+str+')'; + str += ")"; + } + break; + case 'datetimearray': + str='(' + colref; + var dtfirst=true; + for(var j in val) + { + if(!dtfirst) str+= ' AND ' + colref; + dtfirst=false; + if(val[j].substring(0,2)=='>=') + str+=' >= \"'+val[j].substring(2)+'\"'; + else if(val[j].substring(0,2)=='<=') + str+=' <= \"'+val[j].substring(2)+'\"'; + else if(val[j].substring(0,2)=='=>') + str+=' >= \"'+val[j].substring(2)+'\"'; + else if(val[j].substring(0,2)=='=<') + str+=' <= \"'+val[j].substring(2)+'\"'; + else if(val[j].substring(0,1)=='>') + str+=' > \"'+val[j].substring(1)+'\"'; + else if(val[j].substring(0,1)=='<') + str+=' < \"'+val[j].substring(1)+'\"'; + else if(val[j].substring(0,1)=='=') + str+=' = \"'+val[j].substring(1)+'\"'; + } + str+=')'; + break; + + case 'string': + case 'istring': + str = this.MakeFilterString(col, val, col.getType() == 'istring'); + break; + + default: + //htr_alert(val, 1); + if(!val || typeof val.substring == 'undefined') // assume integer + str = this.MakeFilterInteger(col, val); + else if(val.substring(0,2)=='>=') + str=colref + ' >= '+val.substring(2); + else if(val.substring(0,2)=='<=') + str=colref + ' <= '+val.substring(2); + else if(val.substring(0,2)=='=>') + str=colref + ' >= '+val.substring(2); + else if(val.substring(0,2)=='=<') + str=colref + ' <= '+val.substring(2); + else if(val.substring(0,1)=='>') + str=colref + ' > '+val.substring(1); + else if(val.substring(0,1)=='<') + str=colref + ' < '+val.substring(1); + else if(val.indexOf('-')>=0) + { + //assume integer range in string + var ind = val.indexOf('-'); + var val1 = val.substring(0,ind); + var val2 = val.substring(ind+1); + str='(' + colref + ' >='+val1+' AND ' + colref + ' <='+val2+')'; + } + else + { + str = this.MakeFilterString(col, val); + } + break; } - firstone=false; } + + if (isnot) + str = "not (" + str + ")"; + + if (firstone) + statement += ' (' + str + ')'; + else + statement += ' ' + q.getJoin() + ' (' + str + ')'; + + firstone=false; } + return statement; } @@ -762,13 +1084,11 @@ function osrc_action_delete(aparam) //up,initiating_client) var up = aparam.data; var initiating_client = aparam.client; - //Delete an object through OSML - //var src = this.baseobj + '?cx__akey='+akey+'&ls__mode=osml&ls__req=delete&ls__sid=' + this.sid + '&ls__oid=' + up.oid; + // Delete an object through OSML this.initiating_client = initiating_client; this.deleteddata=up; this.DoRequest('delete', this.baseobj, {ls__oid:up.oid}, osrc_action_delete_cb); - //this.initiating_client.ObjectDeleted(); - //this.initiating_client.OperationComplete(); + return 0; } @@ -789,7 +1109,7 @@ function osrc_action_delete_cb() for(var i=recnum; i max_j) max_j = parseInt(j); - } - if (!found) - { - max_j++; - cr[max_j] = {}; - cr[max_j].oid = server_rec[i].oid; - cr[max_j].value = server_rec[i].value; - cr[max_j].type = server_rec[i].type; - cr[max_j].id = max_j; - cr[max_j].hints = server_rec[i].hints; - } + cr.copyFrom(data.resultset[0]); + cr.__cx_handle = data.resultset[0].__cx_handle; } - //alert(this.replica[this.CurrentRecord].oid); + // Notify clients of the newly created object. this.in_create = false; this.SyncID = osrc_syncid++; - if (this.initiating_client) this.initiating_client.OperationComplete(true, this); - pg_serialized_load(this, 'about:blank', null, true); + if (this.initiating_client) + this.initiating_client.OperationComplete(true, this); for(var i in this.child) this.child[i].ObjectCreated(recnum, this); this.GiveAllCurrentRecord('create'); @@ -961,60 +1254,83 @@ function osrc_action_create_cb() } else { + // Did not succeed - let the calling client know. this.in_create = false; - if (this.initiating_client) this.initiating_client.OperationComplete(false, this); + if (this.initiating_client) + this.initiating_client.OperationComplete(false, this); } + this.initiating_client=null; delete this.createddata; } + function osrc_action_refresh_object(aparam) { this.QueueRequest({Request:'RefreshObject', Param:aparam}); this.Dispatch(); } + function osrc_refresh_object_handler(aparam) { // Need a "last query" and a valid current record to proceed - if (!this.lastquery || !this.CurrentRecord || !this.replica || !this.replica[this.CurrentRecord]) return false; + if (!this.lastquery || !this.CurrentRecord || !this.replica || !this.replica[this.CurrentRecord]) + return false; // Build list of primary keys var row = this.replica[this.CurrentRecord]; - var keys = {}; var keycnt = 0; - for(var c in row) + var nameattr = null; + var filter = new $CX.Types.osrcObject(); + var attrlist = row.getAttrList(); + for(var a in attrlist) { - if (c == 'oid') continue; - var col = row[c]; - var ph = cx_parse_hints(col.hints); + var attr = row.getAttr(a); + var ph = cx_parse_hints(attr.getHints()); if (ph.Style & cx_hints_style.key) { - keys[col.oid] = col; - keycnt ++; + filter.newAttr(a, attr); + keycnt++; } + if (a == 'name') + nameattr = attr; + } + if (!keycnt) + { + if (!nameattr) + return false; + else + filter.newAttr('name', nameattr); } - if (!keycnt) return false; // Start with the lastquery SQL. var sql = this.lastquery; // Append logic to search for just this row - var first = true; - for(var k in keys) + if (this.use_having) + sql += " HAVING "; + else + sql += " WHERE "; + sql += this.MakeFilter(filter); + /*if (keycnt > 0) { - if (first) + var first = true; + for(var k in keys) { - if (this.use_having) - sql += " HAVING "; - else - sql += " WHERE "; + if (first) + { + if (this.use_having) + sql += " HAVING "; + else + sql += " WHERE "; + } + else + sql += " AND "; + sql += this.MakeFilter([keys[k]]); + first = false; } - else - sql += " AND "; - sql += this.MakeFilter([keys[k]]); - first = false; - } + }*/ sql += " LIMIT 1"; // Now issue the query @@ -1028,31 +1344,36 @@ function osrc_refresh_object_handler(aparam) ls__notify:this.request_updates, ls__sqlparam:this.EncodeParams() }, osrc_refresh_object_cb); + return true; } -function osrc_refresh_object_cb() + +function osrc_refresh_object_cb(data) { - var links = pg_links(this); - var success = links && links[0] && (links[0].target != 'ERR'); - if(success && links.length > 1) + // Do we have a valid result from the refresh? + if (data && data.status == 'OK' && data.resultset.length > 0) { // Check new/corrected data provided by server - var cr=this.replica[this.CurrentRecord]; - var server_rec = this.ParseOneRow(links, 1); - var diff = 0; - for(var i in server_rec) - for(var j in cr) + var cr = this.replica[this.CurrentRecord]; + var server_rec = data.resultset[0]; + var diff = false; + var attrlist = server_rec.getAttrList(); + for(var a in attrlist) + { + var attr = server_rec.getAttr(a); + var cattr = cr.getAttr(a); + if (!cattr) + cattr = cr[a] = new $CX.Types.osrcAttr(); + if (cattr.get() != attr.get()) { - if (cr[j].oid == server_rec[i].oid && cr[j].value != server_rec[i].value) - { - cr[j].value = server_rec[i].value; - cr[j].type = server_rec[i].type; - diff = 1; - } + cattr.v = attr.get(); + cattr.t = attr.getType(); + diff = true; } + } - // if any changes, display them + // if any changes, tell our clients about them. if (diff) { this.SyncID = osrc_syncid++; @@ -1061,7 +1382,8 @@ function osrc_refresh_object_cb() } } -function osrc_action_modify(aparam) //up,initiating_client) + +function osrc_action_modify(aparam) //up,initiating_client { this.doing_refresh = false; if (aparam) @@ -1386,8 +1708,7 @@ function osrc_open_session(cb) //alert('open'); if(this.sid || cb == osrc_open_query) { - this.__osrc_cb = cb; - this.__osrc_cb(); + cb.call(this, null); } else { @@ -1398,13 +1719,6 @@ function osrc_open_session(cb) function osrc_open_query() { //Open Query - /*if(!this.sid) - { - var lnks = pg_links(this); - if (!lnks || !lnks[0] || !lnks[0].target) - return false; - this.sid=pg_links(this)[0].target; - }*/ if(this.qid && this.sid) { this.DoRequest('queryclose', '/', {ls__qid:this.qid}, osrc_open_query); @@ -1420,33 +1734,32 @@ function osrc_open_query() this.querysize = this.replicasize; } -function osrc_get_qid() +function osrc_get_qid(data) { - //return; - var lnk = pg_links(this); - this.data_start = 1; - if (!this.sid && lnk && lnk[0] && lnk[0].target) + // Check for handles + if (data) { - this.sid = lnk[0].target; - this.data_start = 2; - } + // Get session handle + if (!this.sid && data.session) + this.sid = data.session; - if (lnk && lnk[this.data_start-1]) - this.qid=lnk[this.data_start-1].target; - else - this.qid = null; + // Get query handle + if (data.queryclosed) + this.qid = null; + else + this.qid = data.query; + } - //confirm(this.baseobj + " ==> " + this.qid); - if (!this.qid) + // No valid query run? Bail out if so. + if (!data || !data.query) { - /*this.pending=false;*/ this.move_target = null; this.GiveAllCurrentRecord('get_qid'); this.SetPending(false); - /*this.Dispatch();*/ } else { + // Valid query this.query_delay = pg_timestamp() - this.request_start_ts; for(var i in this.child) this.child[i].DataAvailable(this, this.doing_refresh?'refresh':'query'); @@ -1455,23 +1768,22 @@ function osrc_get_qid() else var tgt = 1; this.move_target = null; - if (lnk.length > 1) + if (data.resultset.length > 0) { // did an autofetch - we have the data already - if (!this.do_append) this.ClearReplica(); + if (!this.do_append) + this.ClearReplica(); this.TargetRecord = [tgt,tgt]; this.CurrentRecord = tgt; this.moveop = true; - this.FetchNext(); + this.FetchNext(data); } else { // start the ball rolling for the fetch - //this.ifcProbe(ifAction).Invoke("First", {from_internal:true}); this.ifcProbe(ifAction).Invoke("FindObject", {ID:tgt, from_internal:true}); } } - /** normally don't actually load the data...just let children know that the data is available **/ } function osrc_parse_one_attr(lnk) @@ -1490,14 +1802,15 @@ function osrc_parse_one_attr(lnk) function osrc_new_replica_object(id, oid) { - var obj = []; - obj.oid=oid; - obj.id = id; + var obj = new $CX.Types.osrcObject(); + obj.__cx_handle = oid; + obj.__cx_id = id; return obj; } function osrc_prune_replica(most_recent_id) { + // Remove records from beginning of replica? if(this.LastRecord < most_recent_id) { this.LastRecord = most_recent_id; @@ -1517,11 +1830,16 @@ function osrc_prune_replica(most_recent_id) if (found) break; // clean up replica - this.oldoids.push(this.replica[this.FirstRecord].oid); - delete this.replica[this.FirstRecord]; + if (this.replica[this.FirstRecord]) + { + this.oldoids.push(this.replica[this.FirstRecord].__cx_handle); + delete this.replica[this.FirstRecord]; + } this.FirstRecord++; } } + + // Remove records from end of replica? if(this.FirstRecord > most_recent_id) { this.FirstRecord = most_recent_id; @@ -1543,7 +1861,7 @@ function osrc_prune_replica(most_recent_id) // clean up replica if (this.replica[this.LastRecord]) { - this.oldoids.push(this.replica[this.LastRecord].oid); + this.oldoids.push(this.replica[this.LastRecord].__cx_handle); delete this.replica[this.LastRecord]; } this.LastRecord--; @@ -1561,16 +1879,15 @@ function osrc_action_clear(aparam) function osrc_clear_replica() { - this.TargetRecord = [1,1];/* the record we're aiming for -- go until we get it*/ - this.CurrentRecord=1;/* the current record */ - this.OSMLRecord=0;/* the last record we got from the OSML */ + this.TargetRecord = [1,1]; // the record we're aiming for -- go until we get it + this.CurrentRecord=1; // the current record + this.OSMLRecord=0; // the last record we got from the OSML - /** Clear replica **/ + // Clear replica contents if(this.replica) for(var i in this.replica) - this.oldoids.push(this.replica[i].oid); + this.oldoids.push(this.replica[i].__cx_handle); - if(this.replica) delete this.replica; this.replica = []; this.LastRecord=0; this.FinalRecord=null; @@ -1625,15 +1942,16 @@ function osrc_query_timeout() function osrc_end_query() { - //this.initiating_client.OperationComplete(); /* don't need this...I think....*/ var qid=this.qid this.qid=null; - /* return the last record as the current one if it was our target otherwise, don't */ + + // If we retrieved any data at all, mark the last one as the Final Record. if (this.LastRecord >= this.FirstRecord && this.replica[this.LastRecord]) { this.replica[this.LastRecord].__osrc_is_last = true; this.FinalRecord = this.LastRecord; } + this.query_ended = true; this.FoundRecord(); if(qid) @@ -1667,7 +1985,6 @@ function osrc_found_record() this.GiveAllCurrentRecord('change'); else this.TellAllReplicaMoved(); - /*this.pending=false;*/ this.SetPending(false); this.osrc_oldoid_cleanup(); if (this.query_delay) @@ -1684,79 +2001,72 @@ function osrc_found_record() } } -function osrc_fetch_next() +function osrc_fetch_next(data) { - pg_debug(this.id + ": FetchNext() ==> " + pg_links(this).length + "\n"); - //alert('fetching....'); - if(!this.qid) + // No data supplied? + if (!data || !data.query) { - //if (pg_diag) confirm("ERR: " + this.baseobj + " ==> " + this.qid); if (pg_diag) confirm("fetch_next: error - no qid. first/last/cur/osml: " + this.FirstRecord + "/" + this.LastRecord + "/" + this.CurrentRecord + "/" + this.OSMLRecord + "\n"); - //alert('something is wrong...'); - //alert(this.src); - } - var lnk=pg_links(this); - var lc=lnk.length; - //confirm(this.baseobj + " ==> " + lc + " links"); - if(lc <= this.data_start) - { // query over + return 0; + } + + // query over? + if (data.resultset.length == 0) + { this.EndQuery(); return 0; } - var colnum=0; - var i = this.data_start; + + // Records skipped? + if (data.skipped > 0) + { + this.OSMLRecord += data.skipped; + this.querysize++; + } + + // Import the data var rowcnt = 0; - while (i < lc) + for(var i=0; i 0) + while (!this.replica[this.LastRecord] && this.LastRecord > 0) this.LastRecord--; - if(this.LastRecord= this.querysize) + if ((this.LastRecord-this.FirstRecord+1) < this.replicasize && rowcnt >= this.querysize) { - // make sure we have a full replica if possible + // Replica is not full and more data is likely available: + // Make sure we have a full replica if possible. this.DoFetch(this.replicasize - (this.LastRecord - this.FirstRecord + 1), false); } else { if (rowcnt < this.querysize) + { + // Replica is full and no more data is available this.EndQuery(); + } else + { + // Replica is full but more data is likely available this.FoundRecord(); + } } } } @@ -1788,7 +2105,6 @@ function osrc_oldoid_cleanup() if(this.oldoids && this.oldoids[0]) { this.SetPending(true); - /*this.pending=true;*/ var src=''; for(var i in this.oldoids) src+=this.oldoids[i]; @@ -1808,7 +2124,6 @@ function osrc_oldoid_cleanup_cb() { /*this.pending=false;*/ //alert('cb recieved'); - delete this.oldoids; this.oldoids = []; this.SetPending(false); pg_serialized_load(this, 'about:blank', null, true); @@ -1912,7 +2227,7 @@ function osrc_move_first(aparam) function osrc_change_current_record() { - var newprevcurrent = []; + var newprevcurrent = new $CX.Types.osrcObject(); // first, build the list of fields we're working with. We look both in the // replica and in prevcurrent, since field lists can be irregular (different @@ -1920,13 +2235,15 @@ function osrc_change_current_record() var fieldlist = {}; if (this.prevcurrent) { - for(var i=0; i= this.FirstRecord && this.replica[this.LastRecord] && this.replica[this.LastRecord].__osrc_is_last) { @@ -2018,16 +2295,13 @@ function osrc_give_all_current_record(why) this.child[i].ObjectAvailable(this.replica[this.CurrentRecord], this, (why=='create')?'create':(this.doing_refresh?'refresh':'change')); this.ifcProbe(ifEvent).Activate("DataFocusChanged", {}); this.doing_refresh = false; - //confirm('give_all_current_record done'); } function osrc_tell_all_replica_moved() { - //confirm('tell_all_replica_moved start'); for(var i in this.child) if(this.child[i].ReplicaMoved) this.child[i].ReplicaMoved(this); - //confirm('tell_all_replica_moved done'); } @@ -2046,49 +2320,24 @@ function osrc_move_to_record_handler(param) var from_internal = param.from_internal; if(recnum<1) { - //alert("Can't move past beginning."); return 0; } if(this.pending) { - //alert('you got ahead'); return 0; } this.SetPending(true); - //this.pending=true; - //var someunsaved=false; this.RecordToMoveTo=recnum; if (!from_internal) this.SyncID = osrc_syncid++; this.GoNogo(osrc_cb_query_continue_2, osrc_cb_query_cancel_2, null); - /*for(var i in this.child) - { - if(this.child[i].IsUnsaved) - { - //alert('child: '+i+' : '+this.child[i].IsUnsaved+' isn\\'t saved...IsDiscardReady'); - this.child[i]._osrc_ready=false; - this.child[i].IsDiscardReady(); - someunsaved=true; - } - else - { - this.child[i]._osrc_ready=true; - } - }*/ - //if someunsaved is false, there were no unsaved forms, so no callbacks - // we can just continue - /*if(someunsaved) return 0; - this.MoveToRecordCB(recnum);*/ } function osrc_move_to_record_cb(recnum) { - pg_debug(this.id + ": MoveTo(" + recnum + ")\n"); - //confirm(recnum); this.moveop=true; if(recnum<1) { - //alert("Can't move past beginning."); return 0; } this.RecordToMoveTo=recnum; @@ -2096,25 +2345,22 @@ function osrc_move_to_record_cb(recnum) { if(this.child[i].IsUnsaved) { - //confirm('child: '+i+' : '+this.child[i].IsUnsaved+' isn\\'t saved...'); return 0; } } -/* If we're here, we're ready to go */ this.TargetRecord = [recnum, recnum]; this.CurrentRecord = recnum; if(this.CurrentRecord <= this.LastRecord && this.CurrentRecord >= this.FirstRecord) { this.GiveAllCurrentRecord('change'); this.SetPending(false); - /*this.pending=false; - this.Dispatch();*/ return 1; } else { if(this.CurrentRecord < this.FirstRecord) - { /* data is further back, need new query */ + { + // data is further back, need new query if(this.FirstRecord-this.CurrentRecord0?(this.FirstRecord-this.readahead):1; @@ -2134,12 +2380,13 @@ function osrc_move_to_record_cb(recnum) return 0; } else - { /* data is farther on, act normal */ + { + // data is farther on, act normal if(this.qid) { if(this.CurrentRecord == Number.MAX_VALUE) { - /* rowcount defaults to a really high number if not set */ + // rowcount defaults to a really high number if not set this.DoFetch(this.replicasize, true); } else if (recnum == 1) @@ -2166,11 +2413,9 @@ function osrc_move_to_record_cb(recnum) } else { - //this.pending=false; this.CurrentRecord=this.LastRecord; this.GiveAllCurrentRecord('change'); this.SetPending(false); - //this.Dispatch(); } return 0; } @@ -2197,33 +2442,43 @@ function osrc_open_query_startat() this.DoRequest('multiquery', '/', {ls__startat:this.startat, ls__autoclose_sr:1, ls__autofetch:1, ls__objmode:0, ls__notify:this.request_updates, ls__rowcount:this.querysize, ls__sql:this.query, ls__sqlparam:this.EncodeParams()}, osrc_get_qid_startat); } -function osrc_get_qid_startat() +function osrc_get_qid_startat(data) { - var lnk = pg_links(this); - this.qid=lnk[0].target; - if (!this.qid) + // Check for handles + if (data) + { + // Get session handle + if (!this.sid && data.session) + this.sid = data.session; + + // Get query handle + if (data.queryclosed) + this.qid = null; + else + this.qid = data.query; + } + + // No query performed? + if (!data || !data.query) { this.startat = null; - //this.pending=false; this.GiveAllCurrentRecord('get_qid'); this.SetPending(false); - //this.Dispatch(); return; } + this.OSMLRecord=(this.startat)?(this.startat-1):0; - //this.FirstRecord=this.startat; - /*if(this.startat-this.TargetRecord+1 1) + + // Do we have result set objects? + if (data.resultset.length > 0) { // did an autofetch - we have the data already this.query_delay = pg_timestamp() - this.request_start_ts; - this.FetchNext(); + this.FetchNext(data); } else { + // Did not do autofetch -- grab the records. if(this.FirstRecord - this.startat < this.replicasize) { this.DoFetch(this.FirstRecord - this.startat, false); @@ -2233,6 +2488,7 @@ function osrc_get_qid_startat() this.DoFetch(this.replicasize, false); } } + this.startat=null; } @@ -2418,56 +2674,47 @@ function osrc_action_sync(param) this.SyncID = this.parentosrc.SyncID; // Compile the list of criteria - var query = []; - query.oid=null; - query.joinstring='AND'; - var p=this.parentosrc.CurrentRecord; + var query = new $CX.Types.osrcObject(); + query.setJoin('AND'); + var parentobj = this.parentosrc.getObject(); var force_empty = false; for(var i=1;i<10;i++) { - //this.ParentKey[i]=eval('param.ParentKey'+i); - //this.ChildKey[i]=eval('param.ChildKey'+i); this.ParentKey[i]=param['ParentKey'+i]; this.ChildKey[i]=param['ChildKey'+i]; if(this.ParentKey[i]) { - if (!this.parentosrc.replica[p]) + var parentcol = null; + if (parentobj) + parentcol = parentobj.getAttr(this.ParentKey[i]); + if (!parentobj || !parentcol) { - var t = new Object(); - t.plainsearch = true; - t.oid = this.ChildKey[i]; - t.value = null; - t.type = 'integer'; // type doesn't matter if it is null. + // No current record, or if so, it has no attribute. + // Type doesn't matter if it is null. + var col = query.newAttr(this.ChildKey[i], null, 'integer'); + var filt = col.getFilter(); + filt.plainsearch = true; if (on_norecs == 'nullisvalue') - t.nullisvalue = true; + filt.nullisvalue = true; else if (on_norecs == 'norecs') - force_empty = t.force_empty = true; + force_empty = filt.force_empty = true; else - t.nullisvalue = false; - query.push(t); + filt.nullisvalue = false; } else { - for(var j in this.parentosrc.replica[p]) + // Current record with a valid attribute. + var col = query.newAttr(this.ChildKey[i], parentcol); + var filt = col.getFilter(); + filt.plainsearch = true; + if (col.get() === null) { - if(this.parentosrc.replica[p][j].oid==this.ParentKey[i]) - { - var t = new Object(); - t.plainsearch = true; - t.oid=this.ChildKey[i]; - t.value=this.parentosrc.replica[p][j].value; - t.type=this.parentosrc.replica[p][j].type; - if (t.value === null) - { - if (on_null == 'nullisvalue') - t.nullisvalue = true; - else if (on_null == 'norecs') - force_empty = t.force_empty = true; - else - t.nullisvalue = false; - } - query.push(t); - } + if (on_null == 'nullisvalue') + filt.nullisvalue = true; + else if (on_null == 'norecs') + force_empty = filt.force_empty = true; + else + filt.nullisvalue = false; } } } @@ -2496,27 +2743,36 @@ function osrc_action_sync(param) } this.was_forced_empty = force_empty; - // Did it change from last time? + // Did it change from last time? Get a merged list of the current and previous + // properties, and compare the values to see if anything is different. if (!this.lastSync) - this.lastSync = []; + this.lastSync = new $CX.Types.osrcObject(); var changed = false; - for(var i=0;i cur_seq)) && - (prevseq === null || ((direction == 'backward' && parseInt(field.value) > prevseq) || (direction == 'forward' && parseInt(field.value) < prevseq)))) + var attrval = parseInt(attr.get()); + if (((direction == 'backward' && attrval < cur_seq) || (direction == 'forward' && attrval > cur_seq)) && + (prevseq === null || ((direction == 'backward' && attrval > prevseq) || (direction == 'forward' && attrval < prevseq)))) { previtem = idx; - prevseq = parseInt(field.value); + prevseq = attrval; } - }); + }; }); // Didn't find anything? Nothing to do then. @@ -2794,7 +3036,7 @@ function osrc_seq(direction) if (!doneprev) { reqparam[seqfield] = cur_seq; - reqparam.ls__oid = this.replica[previtem].oid; + reqparam.ls__oid = this.replica[previtem].getID(); this.SetValue(seqfield, cur_seq, previtem); this.DoRequest('setattrs', '/', reqparam, seqproc); doneprev = true; @@ -2805,7 +3047,7 @@ function osrc_seq(direction) if (!donecurr) { reqparam[seqfield] = prevseq; - reqparam.ls__oid = this.replica[this.CurrentRecord].oid; + reqparam.ls__oid = this.replica[this.CurrentRecord].getID(); this.SetValue(seqfield, prevseq); this.DoRequest('setattrs', '/', reqparam, seqproc); donecurr = true; @@ -2848,29 +3090,26 @@ function osrc_apply_sequence(obj) var maxval = -1; this.replica.forEach(function(item) { - item.forEach(function(field) + if (typeof item[rl.field] == 'object') { - if (field.oid == rl.field) - { - var ckval = parseInt(field.value); - if (ckval > maxval) - maxval = ckval; - } - }); + var ckval = parseInt(item.getAttrValue(rl.field)); + if (ckval > maxval) + maxval = ckval; + } }); // got maximum value in the osrc's replica. Assign it now. - var found=false; - obj.forEach(function(field) + if (typeof obj[rl.field] == 'object') + obj[rl.field].v = '' + (maxval + 1); + else { - if (field.oid == rl.field) - { - found = true; - field.value = '' + (maxval + 1); - } - }); - if (!found) - obj.push({hints:"", oid:rl.field, system:false, type:"integer", value:'' + (maxval + 1)}); + obj[rl.field] = new $CX.Types.osrcAttr(); + obj[rl.field].h = ''; + obj[rl.field].y = false; + obj[rl.field].t = 'integer'; + obj[rl.field].v = '' + (maxval + 1); + //obj.push({hints:"", oid:rl.field, system:false, type:"integer", value:'' + (maxval + 1)}); + } } } } @@ -2932,9 +3171,6 @@ function osrc_apply_keys(obj) function osrc_apply_rel(obj, in_create) { - var cnt = 0; - while(typeof obj[cnt] != 'undefined') cnt++; - // First, check for relationships that might imply key values for(var i=0; i + { + if (act) + pg_spinner_dec(); + data = JSON.parse(data, (key, value) => + { + // We revive prototypes for the json via duck-typing. + if (typeof value === 'object') + { + // null + if (value == null) + return value; + // attribute + if (typeof value.v !== 'undefined' && value.t && typeof value.__cx_handle === 'undefined') + value.__proto__ = $CX.Types.osrcAttr.prototype; + // object (i.e., row or tuple) + else if (value.__cx_handle) + value.__proto__ = $CX.Types.osrcObject.prototype; + // money + else if (typeof value.wholepart !== 'undefined' && typeof value.fractionpart !== 'undefined') + value.__proto__ = $CX.Types.Money.prototype; + // date/time + else if (typeof value.year !== 'undefined' && typeof value.month !== 'undefined') + { + value.__proto__ = $CX.Types.DateTime.prototype; + value.adjFromServer(); + } + } + return value; + }); + cb.call(this, data); + }) + .fail( (xhr, stat, err) => + { + if (act) + pg_spinner_dec(); + }); + //pg_serialized_load(target, url, cb, !this.ind_act || !this.req_ind_act); this.req_ind_act = true; - //this.onload = cb; - //target.src = url; } @@ -3768,6 +4059,16 @@ function osrc_set_master_pending(master, p) } +function osrc_api_get_object(id) + { + if (id == null || id == undefined) + id = this.osrcCurrentObjectID; + if (!id || !this.replica[id]) + return null; + return this.replica[id]; + } + + function osrc_destroy() { pg_set(this, "src", "about:blank"); @@ -3892,6 +4193,25 @@ function osrc_init(param) loader.QueryHandler = osrc_query_handler; loader.RefreshObjectHandler = osrc_refresh_object_handler; loader.FindObjectHandler = osrc_find_object_handler; + + // Replica access API + loader.getObject = osrc_api_get_object; + Object.defineProperty(loader, 'osrcFirstObjectID', + { + get: function() { return this.FirstRecord; } + }); + Object.defineProperty(loader, 'osrcLastObjectID', + { + get: function() { return this.LastRecord; } + }); + Object.defineProperty(loader, 'osrcCurrentObjectID', + { + get: function() { return this.CurrentRecord; } + }); + Object.defineProperty(loader, 'osrcFinalObjectID', + { + get: function() { return this.FinalRecord; } + }); // Zero out the replica loader.ClearReplica(); @@ -3925,6 +4245,7 @@ function osrc_init(param) ia.Add("CancelCreateObject", osrc_action_cancelcreate); ia.Add("SeqBackward", osrc_seq_backward); ia.Add("SeqForward", osrc_seq_forward); + ia.Add("ForEach", osrc_action_for_each); // Events var ie = loader.ifcProbeAdd(ifEvent); diff --git a/centrallix-os/sys/js/htdrv_page.js b/centrallix-os/sys/js/htdrv_page.js old mode 100755 new mode 100644 index 538a2b33b..796913052 --- a/centrallix-os/sys/js/htdrv_page.js +++ b/centrallix-os/sys/js/htdrv_page.js @@ -17,6 +17,43 @@ * mouse events. */ +// Main global context for Centrallix widgets +var $CX = + { + Widget: + { + }, + Globals: + { + spinnerCount: 0, + moneyFormat: "$0.00", + dateFormat: "dd MMM yyyy HH:mm" + }, + Tree: null, + Namespaces: {}, + Time: + { + Init: + { + server: null, + serverTZ: null, + client: null, + clockOffset: 0 + } + }, + Scripts: + { + }, + Endorsements: + { + }, + Apps: + { + }, + Types: + { + } + }; var pg_msglist = ''; var pg_init_ts = (new Date()).valueOf(); @@ -1222,7 +1259,7 @@ function pg_keyhandler_internal(k,m,e) return false; } } - return false; + return true; } function pg_status_init() //SETH: ?? @@ -1533,6 +1570,11 @@ function pg_launch(aparam) w_exists = false; } + // Compute the height + var h = aparam.Height; + if (window.devicePixelRatio) + h *= window.devicePixelRatio; + // Open it. if (!w_exists) { @@ -1548,7 +1590,7 @@ function pg_launch(aparam) var scroll = ",scrollbars=yes"; else var scroll = ",scrollbars=no"; - window.windowlist[w_name] = window.open(url, w_name, "toolbar=no" + scroll + ",innerHeight=" + aparam.Height + ",innerWidth=" + aparam.Width + ",personalbar=no,status=no" + menubar + resizable); + window.windowlist[w_name] = window.open(url, w_name, "toolbar=no" + scroll + ",innerHeight=" + h + ",innerWidth=" + aparam.Width + ",personalbar=no,status=no" + menubar + resizable); } } @@ -2076,6 +2118,42 @@ function pg_getrelcoord(l, sub_l) //START SECTION: async request handling ---------------------------------- +// +// This function starts the busy spinner. +// +function pg_spinner_inc() + { + $CX.Globals.spinnerCount++; + + if (!pg_waitlyr || !pg_waitlyr.vis) + { + if (!pg_waitlyr) + { + pg_waitlyr = htr_new_layer(96); + htr_write_content(pg_waitlyr, "
"); + moveToAbsolute(pg_waitlyr, (pg_width-100)/2, (pg_height-24)/2); + htr_setzindex(pg_waitlyr, 99999); + } + if (pg_waitlyr_id) pg_delsched(pg_waitlyr_id); + pg_waitlyr_id = null; + pg_waitlyr.vis = true; + + htr_setvisibility(pg_waitlyr, "inherit"); + } + } + + +// This function terminates the busy spinner if everyone is done with it. +function pg_spinner_dec() + { + $CX.Globals.spinnerCount--; + + if ($CX.Globals.spinnerCount <= 0) + { + pg_clear_waitlyr(); + } + } + // pg_loadqueue_additem() - adds an item to the load queue, sorted by 'level' function pg_loadqueue_additem(item) @@ -2091,7 +2169,7 @@ function pg_serialized_write(l, text, cb) { //pg_debug('pg_serialized_write: ' + pg_loadqueue.length + ': ' + l.name + ' loads "' + text.substring(0,100) + '"\n'); //pg_loadqueue.push({lyr:l, text:text, cb:cb}); - pg_loadqueue_additem({level:1, type:'write', lyr:l, text:text, cb:cb, retry_cnt:0}); + pg_loadqueue_additem({level:1, type:'write', lyr:l, text:text, cb:cb, retry_cnt:0, silent:true}); //pg_debug('pg_serialized_write: ' + pg_loadqueue.length + '\n'); pg_serialized_load_doone(); } @@ -2102,7 +2180,7 @@ function pg_serialized_write(l, text, cb) // complete (even if scheduled later) before it runs. function pg_serialized_func(level, obj, func, params) { - pg_loadqueue_additem({level:level, type:'func', lyr:obj, cb:func, params:params, retry_cnt:0}); + pg_loadqueue_additem({level:level, type:'func', lyr:obj, cb:func, params:params, retry_cnt:0, silent:true}); //pg_serialized_load_doone(); pg_loadqueue_check(); } @@ -2113,24 +2191,11 @@ function pg_serialized_func(level, obj, func, params) // manner that keeps things serialized so server loads don't overlap. function pg_serialized_load(l, newsrc, cb, silent) { - // pg_waitlyr says if the 'wait layer' should be used (the 'wait layer' is the layer that takes focus and says "please wait...") - if (!silent && (!pg_waitlyr || !pg_waitlyr.vis)) - { - if (!pg_waitlyr) - { - pg_waitlyr = htr_new_layer(96); - htr_write_content(pg_waitlyr, "
"); - moveToAbsolute(pg_waitlyr, (pg_width-100)/2, (pg_height-24)/2); - htr_setzindex(pg_waitlyr, 99999); - } - if (pg_waitlyr_id) pg_delsched(pg_waitlyr_id); - pg_waitlyr_id = null; - pg_waitlyr.vis = true; + if (!silent) + pg_spinner_inc(); - htr_setvisibility(pg_waitlyr, "inherit"); - } pg_debug('pg_serialized_load: ' + pg_loadqueue.length + ': ' + l.name + ' loads ' + newsrc + '\n'); - pg_loadqueue_additem({level:1, type:'src', lyr:l, src:newsrc, cb:cb, retry_cnt:0}); + pg_loadqueue_additem({level:1, type:'src', lyr:l, src:newsrc, cb:cb, retry_cnt:0, silent:silent}); pg_debug('pg_serialized_load: ' + pg_loadqueue.length + '\n'); pg_serialized_load_doone(); } @@ -2144,7 +2209,7 @@ function pg_serialized_load_doone() if (pg_loadqueue.length == 0) { //pg_loadqueue_busy = 0; - pg_clear_waitlyr(); + //pg_clear_waitlyr(); return; } @@ -2169,8 +2234,18 @@ function pg_serialized_load_doone() switch(one_item.type) { case 'src': - one_item.lyr.onload = pg_serialized_load_cb; - one_item.lyr.onerror = pg_serialized_load_error_cb; + one_item.lyr.onload = () => + { + if (!one_item.silent) + pg_spinner_dec(); + pg_serialized_load_cb.call(one_item.lyr); + }; + one_item.lyr.onerror = () => + { + if (!one_item.silent) + pg_spinner_dec(); + pg_serialized_load_error_cb.call(one_item.lyr); + }; pg_set(one_item.lyr, 'src', one_item.src); break; @@ -2190,6 +2265,9 @@ function pg_serialized_load_doone() one_item.lyr.__load_busy = false; pg_loadqueue_busy--; } + + if (!one_item.silent) + pg_spinner_dec(); pg_loadqueue_check(); break; @@ -2197,6 +2275,8 @@ function pg_serialized_load_doone() one_item.cb.apply(one_item.lyr, one_item.params); one_item.lyr.__load_busy = false; pg_loadqueue_busy--; + if (!one_item.silent) + pg_spinner_dec(); pg_loadqueue_check(); break; } @@ -2230,13 +2310,13 @@ function pg_loadqueue_check() { if (pg_loadqueue.length > 0) pg_addsched_fn(window, 'pg_serialized_load_doone', [], 0); - else - pg_clear_waitlyr(); + //else +// pg_clear_waitlyr(); } function pg_clear_waitlyr() { - if (pg_waitlyr && !pg_loadqueue_busy) + if (pg_waitlyr) { if (pg_waitlyr_id) pg_delsched(pg_waitlyr_id); pg_waitlyr.vis = false; @@ -2972,6 +3052,18 @@ function pg_check_resize(l) return null; } +function pg_scroll(e) + { + if (e.target == document) + { + return EVENT_HALT | EVENT_PREVENT_DEFAULT_ACTION; + } + else + { + return EVENT_CONTINUE | EVENT_ALLOW_DEFAULT_ACTION; + } + } + // Load indication if (window.pg_scripts) pg_scripts['htdrv_page.js'] = true; diff --git a/centrallix-os/sys/js/htdrv_pane.js b/centrallix-os/sys/js/htdrv_pane.js index daadfe35f..98934627c 100644 --- a/centrallix-os/sys/js/htdrv_pane.js +++ b/centrallix-os/sys/js/htdrv_pane.js @@ -45,17 +45,25 @@ function pn_mousemove(e) function pn_getval(attr) { - return this.enabled; + if (attr == 'enabled') + return this.enabled; + else if (attr == 'background') + return htr_getbgimage(this); } function pn_setval(attr, val) { - if (val) - this.enabled = true; - else - this.enabled = false; - this.style.opacity = this.enabled?1.0:0.4; - return this.enabled; + if (attr == 'enabled') + { + if (val) + this.enabled = true; + else + this.enabled = false; + this.style.opacity = this.enabled?1.0:0.4; + return this.enabled; + } + else if (attr == 'background') + htr_setbgimage(this, val); } function pn_setbackground(aparam) @@ -114,6 +122,7 @@ function pn_init(param) var iv = ml.ifcProbeAdd(ifValue); iv.Add("enabled", pn_getval, pn_setval); + iv.Add("background", pn_getval, pn_setval); ml.enabled = param.enabled; if (param.enabled != null) iv.Changing("enabled", ml.enabled, true, null, true); diff --git a/centrallix-os/sys/js/htdrv_tab.js b/centrallix-os/sys/js/htdrv_tab.js index 98c2ddc1c..73be1900d 100644 --- a/centrallix-os/sys/js/htdrv_tab.js +++ b/centrallix-os/sys/js/htdrv_tab.js @@ -187,12 +187,13 @@ function tc_addtab(l_tab, l_page, l, nm, type,fieldname) l_page.Reveal = tc_cb_reveal; pg_reveal_register_triggerer(l_page); //if (htr_getvisibility(l_page) == 'inherit') pg_addsched("pg_reveal(" + l_tab.tabname + ")"); - l_page.is_visible = (l.tloc != 4 && htr_getvisibility(l_tab) == 'inherit'); + //l_page.is_visible = (l.tloc != 4 && htr_getvisibility(l_tab) == 'inherit'); + l_page.is_visible = true; + l_page.tc_visible_changed = tc_visible_changed; + htr_watch(l_page,"is_visible", "tc_visible_changed"); //visible property var iv = l_page.ifcProbeAdd(ifValue); iv.Add("visible", "is_visible"); - htr_watch(l_page,"is_visible", "tc_visible_changed"); //visible property - l_page.tc_visible_changed = tc_visible_changed; // Show Container API l_page.showcontainer = tc_showcontainer; diff --git a/centrallix-os/sys/js/htdrv_table.js b/centrallix-os/sys/js/htdrv_table.js index fb92a1b0d..83bf4013b 100644 --- a/centrallix-os/sys/js/htdrv_table.js +++ b/centrallix-os/sys/js/htdrv_table.js @@ -157,9 +157,9 @@ function tbld_format_cell(cell, color) // function tbld_attr_cmp(a, b) { - if (a.oid > b.oid) + if (a.a > b.a) return 1; - else if (a.oid < b.oid) + else if (a.a < b.a) return -1; else return 0; @@ -199,16 +199,17 @@ function tbld_redraw_all(dataobj, force_datafetch) // Presentation mode -- rows or propsheet? if (this.datamode == 1) { - if (!dataobj && this.osrc.CurrentRecord && this.osrc.replica[this.osrc.CurrentRecord]) - dataobj = this.osrc.replica[this.osrc.CurrentRecord]; + if (!dataobj && this.osrc.osrcCurrentObjectID) + dataobj = this.osrc.getObject(); + // Propsheet mode - build the attr list + var al = dataobj.getAttrList(); this.attrlist = []; - for(var j in dataobj) + for(var a in al) { - if (dataobj[j].oid && !dataobj[j].system) - { - this.attrlist.push(dataobj[j]); - } + var attr = dataobj.getAttr(a); + if (!attr.getSystem()) + this.attrlist.push({a:a, v:attr.get(), t:attr.getType()}); } this.attrlist.sort(tbld_attr_cmp); this.rows.lastosrc = this.attrlist.length; @@ -374,14 +375,24 @@ function tbld_check_bottom() function tbld_find_osrc_value(rowslot, attrname) { var txt = ''; - if (this.osrc.LastRecord >= rowslot && this.osrc.FirstRecord <= rowslot) + if (this.osrc.osrcLastObjectID >= rowslot && this.osrc.osrcFirstObjectID <= rowslot) { - for(var k in this.osrc.replica[rowslot]) + var obj = this.osrc.getObject(rowslot); + if (obj) { - if (this.osrc.replica[rowslot][k].oid == attrname) + var attr = obj.getAttr(attrname); + if (attr) { - txt = this.osrc.replica[rowslot][k].value; - break; + var type = attr.getType(); + txt = attr.get(); + if (type == 'money') + { + txt = attr.get().toString(); + } + else if (type == 'datetime') + { + txt = attr.get().toString(); + } } } if (txt == null || typeof txt == 'undefined') @@ -417,9 +428,9 @@ function tbld_setup_row_data(rowslot, is_new) var txt = ''; switch(this.cols[j].fieldname) { - case 'name': txt = this.attrlist[attrid].oid; break; - case 'value': txt = htutil_obscure(this.attrlist[attrid].value); break; - case 'type': txt = this.attrlist[attrid].type; break; + case 'name': txt = this.attrlist[attrid].a; break; + case 'value': txt = htutil_obscure(this.attrlist[attrid].v); break; + case 'type': txt = this.attrlist[attrid].t; break; default: txt = ''; } if(txt == null || typeof txt == 'undefined') @@ -2182,6 +2193,7 @@ function tbld_keydown(e) return EVENT_CONTINUE | EVENT_ALLOW_DEFAULT_ACTION; } + function tbld_contextmenu(e) { var ly = e.layer; @@ -2204,14 +2216,16 @@ function tbld_contextmenu(e) event.data = new Object(); event.X = e.pageX; event.Y = e.pageY; - var rec=ly.table.osrc.replica[ly.rownum]; - if(rec) + var rec=ly.table.osrc.getObject(ly.rownum); + if (rec) { - for(var i in rec) + var al = rec.getAttrList(); + for(var a in al) { - event.data[rec[i].oid]=rec[i].value; - if (rec[i].oid != 'data' && rec[i].oid != 'Caller' && rec[i].oid != 'recnum' && rec[i].oid != 'X' && rec[i].oid != 'Y') - event[rec[i].oid] = rec[i].value; + var oneattr = rec.getAttr(a); + event.data[a] = oneattr.get(); + if (typeof event[a] == 'undefined') + event[a] = oneattr.get(); } } ly.table.dta=event.data; @@ -2286,15 +2300,17 @@ function tbld_mousedown(e) } event.Caller = ly.table; event.recnum = ly.rownum; - event.data = new Object(); - var rec=ly.table.osrc.replica[ly.rownum]; - if(rec) + event.data = {}; + var rec=ly.table.osrc.getObject(ly.rownum); + if (rec) { - for(var i in rec) + var al = rec.getAttrList(); + for(var a in al) { - event.data[rec[i].oid]=rec[i].value; - if (rec[i].oid != 'data' && rec[i].oid != 'Caller' && rec[i].oid != 'recnum') - event[rec[i].oid] = rec[i].value; + var oneattr = rec.getAttr(a); + event.data[a] = oneattr.get(); + if (typeof event[a] == 'undefined') + event[a] = oneattr.get(); } } ly.table.dta=event.data; @@ -2322,14 +2338,16 @@ function tbld_mousedown(e) event.Caller = ly.table; event.recnum = ly.rownum; event.data = new Object(); - var rec=ly.table.osrc.replica[ly.rownum]; - if(rec) + var rec=ly.table.osrc.getObject(ly.rownum); + if (rec) { - for(var i in rec) + var al = rec.getAttrList(); + for(var a in al) { - event.data[rec[i].oid]=rec[i].value; - if (rec[i].oid != 'data' && rec[i].oid != 'Caller' && rec[i].oid != 'recnum') - event[rec[i].oid] = rec[i].value; + var oneattr = rec.getAttr(a); + event.data[a] = oneattr.get(); + if (typeof event[a] == 'undefined') + event[a] = oneattr.get(); } } ly.table.dta=event.data; diff --git a/centrallix-os/sys/js/htdrv_textbutton.js b/centrallix-os/sys/js/htdrv_textbutton.js index d2178fda5..3ab262dce 100644 --- a/centrallix-os/sys/js/htdrv_textbutton.js +++ b/centrallix-os/sys/js/htdrv_textbutton.js @@ -72,9 +72,10 @@ function tb_init(param) else l.enabled = true;*/ l.enabled = param.ena?true:false; - + + l.tb_setenable = tb_setenable; if (!cx__capabilities.Dom0IE) - l.watch('enabled', tb_setenable); + htr_watch(l, 'enabled', 'tb_setenable'); else { //alert("watch is not supported!"); @@ -93,6 +94,7 @@ function tb_init(param) // Values var iv = l.ifcProbeAdd(ifValue); iv.Add("text", tb_cb_gettext, tb_cb_settext); + iv.Add("enabled", "enabled"); // Actions var ia = l.ifcProbeAdd(ifAction); diff --git a/centrallix-os/sys/js/htdrv_window.js b/centrallix-os/sys/js/htdrv_window.js index 41acb2bfc..19187a523 100644 --- a/centrallix-os/sys/js/htdrv_window.js +++ b/centrallix-os/sys/js/htdrv_window.js @@ -136,6 +136,12 @@ function wn_init(param) pg_addsched_fn(window, "pg_reveal_event", [l,l,'Reveal'], 0); } + // force on page... + if (getPageY(l) + l.orig_height > getInnerHeight()) + { + moveToAbsolute(l, getPageX(l), getInnerHeight() - l.orig_height - 2); + } + // Show container API l.showcontainer = wn_showcontainer; @@ -291,6 +297,7 @@ function wn_setvisibility_bh(v) moveBy(this, 16, 16); wn_bring_top(this); htr_setvisibility(this,'inherit'); + $(this).css({display:"block"}); this.is_visible = 1; if (this.is_modal) pg_setmodal(this, true); this.ifcProbe(ifEvent).Activate("Open", this.open_params); @@ -532,6 +539,7 @@ function wn_close(l) if (l.closetype == 0 || !cx__capabilities.Dom0NS) { htr_setvisibility(l,'hidden'); + $(l).css({display:"none"}); if (l.point1) htr_setvisibility(l.point1,'hidden'); if (l.point2) htr_setvisibility(l.point2,'hidden'); l.is_visible = 0; diff --git a/centrallix-sysdoc/JSON-REST.txt b/centrallix-sysdoc/JSON-REST.txt index cc5cf9590..d474fbe1c 100644 --- a/centrallix-sysdoc/JSON-REST.txt +++ b/centrallix-sysdoc/JSON-REST.txt @@ -141,6 +141,10 @@ OSML JSON DATA FORMAT... h = presentation hints, as encoded by hntEncodeHints() and as decoded by cx_parse_hints in JavaScript. If no hints are available, the h property may be omitted entirely. + y = whether the attribute is a "system" attribute that ordinarily + is hidden from the user (boolean: true/false). + s = the sequence order of the attribute, giving that natural order + the attributes would ordinarily be viewed in The 'a' property above will be omitted if the name has already been specified outside of the full format attribute, for instance if diff --git a/centrallix-sysdoc/OSRC-JSON-Data.txt b/centrallix-sysdoc/OSRC-JSON-Data.txt new file mode 100644 index 000000000..c55217fe7 --- /dev/null +++ b/centrallix-sysdoc/OSRC-JSON-Data.txt @@ -0,0 +1,53 @@ +Document: JSON data format for OSML-over-HTTP for OSRC widget +Author: Greg Beeley (GRB) +Date: 13-Feb-2018 +------------------------------------------------------------------------------- + +The new data format to be used for OSML-over-HTTP shall be identical to the +"full" format used for JSON-REST: + + Full Format + + The Full Format will be used to transfer metadata about each + attribute in addition to the attribute's name and value: + + { "a":"age", "e":null, "v":32, "t":"integer", "h":"d=0" } + + In the above representation, the property names are abbreviated for + space savings, and mean the following: + + a = name of attribute + e = error status, null (or not present) if no error. If set, it + will either be the string "error" or a string containing an + error message. + v = attribute's value (same representation as in the Basic Format) + t = data type (integer, double, string, money, datetime) + h = presentation hints, as encoded by hntEncodeHints() and as + decoded by cx_parse_hints in JavaScript. If no hints are + available, the h property may be omitted entirely. + + The 'a' property above will be omitted if the name has already been + specified outside of the full format attribute, for instance if + a list of attributes is contained in a JSON Object. + + Full: + + { + "@id":"/people/001?cx__mode=rest&cx__res_format=attrs&cx__res_attrs=full", + "first_name": { "v":"John", "t":"string", "h":"l=64" } + "last_name": { "v":"Smith", "t":"string", "h":"l=64" } + } + +Since the ordering of the objects can be important (as with a SQL order by +clause), the list of such objects will be numbered, rather than associative: + + { + "status": null, + "session": "X ...", + "query": "X ...", + "resultset": + [ + { "@id": ... }, + { "@id": ... }, + ] + } diff --git a/centrallix/htmlgen/htdrv_osrc.c b/centrallix/htmlgen/htdrv_osrc.c index e0c869032..199d77230 100644 --- a/centrallix/htmlgen/htdrv_osrc.c +++ b/centrallix/htmlgen/htdrv_osrc.c @@ -370,5 +370,12 @@ int htosrcInitialize() { "fieldname", DATA_T_STRING, NULL); + htruleRegister("osrc_version", + "fieldname", DATA_T_STRING, + "current", DATA_T_STRING, /* zero, one, or max */ + "version_if", DATA_T_CODE, /* only do a versioned save if this is true */ + "version_fields", DATA_T_STRINGVEC, /* only do a versioned save if one of these fields has changed */ + NULL); + return 0; } diff --git a/centrallix/htmlgen/htdrv_page.c b/centrallix/htmlgen/htdrv_page.c index add93dbfd..a2bec722b 100755 --- a/centrallix/htmlgen/htdrv_page.c +++ b/centrallix/htmlgen/htdrv_page.c @@ -365,6 +365,7 @@ htpageRender(pHtSession s, pWgtrNode tree, int z) htrAddBodyItemLayerEnd(s,0); } + htrAddStylesheetItem_va(s, "\thtml { overflow:hidden; }\n"); htrAddStylesheetItem_va(s, "\tbody { overflow:hidden; %[font-size:%POSpx; %]%[font-family:%STR&CSSVAL; %]}\n", font_size > 0, font_size, *font_name, font_name); htrAddStylesheetItem(s, "\tpre { font-size:90%; }\n"); @@ -402,6 +403,7 @@ htpageRender(pHtSession s, pWgtrNode tree, int z) htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "pg", "pg_mouseover"); htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "pg", "pg_mousedown"); htrAddEventHandlerFunction(s, "document", "MOUSEUP", "pg", "pg_mouseup"); + htrAddEventHandlerFunction(s, "document", "SCROLL", "pg", "pg_scroll"); if (s->Capabilities.Dom1HTML) htrAddEventHandlerFunction(s, "document", "CONTEXTMENU", "pg", "pg_contextmenu"); diff --git a/centrallix/htmlgen/htdrv_rule.c b/centrallix/htmlgen/htdrv_rule.c index d8e25f570..fd245eb96 100644 --- a/centrallix/htmlgen/htdrv_rule.c +++ b/centrallix/htmlgen/htdrv_rule.c @@ -168,6 +168,15 @@ htruleRender(pHtSession s, pWgtrNode tree, int z) ptr = objDataToStringTmp(t, (void*)(od.Generic), 0); xsConcatQPrintf(xs, "\"%STR&JSSTR\"", ptr); } + else if (t == DATA_T_STRINGVEC) + { + xsConcatenate(xs, "[", 1); + for(i=0; inStrings; i++) + { + xsConcatQPrintf(xs, "%[,%]\"%STR&JSSTR\"", (i>0), od.StringVec->Strings[i]); + } + xsConcatenate(xs, "]", 1); + } else { ptr = objDataToStringTmp(t, (void*)(od.Generic), DATA_F_QUOTED); diff --git a/centrallix/htmlgen/htdrv_table.c b/centrallix/htmlgen/htdrv_table.c index 5286acff9..2b4232d32 100644 --- a/centrallix/htmlgen/htdrv_table.c +++ b/centrallix/htmlgen/htdrv_table.c @@ -159,9 +159,9 @@ httblRenderDynamic(pHtSession s, pWgtrNode tree, int z, httbl_struct* t) } /** STYLE for the layer **/ - htrAddStylesheetItem_va(s,"\t#tbld%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; } \n",t->id,t->x,t->y,(t->overlap_scrollbar)?(t->w):(t->w-18),z+1); - htrAddStylesheetItem_va(s,"\t#tbld%POSscroll { POSITION:absolute; VISIBILITY:%STR; LEFT:%INTpx; TOP:%INTpx; WIDTH:18px; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",t->id,(t->hide_scrollbar || t->demand_scrollbar)?"hidden":"inherit",t->x+t->w-18,t->y+first_offset,t->h-first_offset,z+1); - htrAddStylesheetItem_va(s,"\t#tbld%POSbox { POSITION:absolute; VISIBILITY:inherit; LEFT:0px; TOP:18px; WIDTH:16px; HEIGHT:16px; Z-INDEX:%POS; BORDER: solid 1px; BORDER-COLOR: white gray gray white; }\n",t->id,z+2); + htrAddStylesheetItem_va(s,"\t#tbld%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; } \n",t->id,t->x,t->y,(t->overlap_scrollbar)?(t->w):(t->w-18),z+0); + htrAddStylesheetItem_va(s,"\t#tbld%POSscroll { POSITION:absolute; VISIBILITY:%STR; LEFT:%INTpx; TOP:%INTpx; WIDTH:18px; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",t->id,(t->hide_scrollbar || t->demand_scrollbar)?"hidden":"inherit",t->x+t->w-18,t->y+first_offset,t->h-first_offset,z+0); + htrAddStylesheetItem_va(s,"\t#tbld%POSbox { POSITION:absolute; VISIBILITY:inherit; LEFT:0px; TOP:18px; WIDTH:16px; HEIGHT:16px; Z-INDEX:%POS; BORDER: solid 1px; BORDER-COLOR: white gray gray white; }\n",t->id,z+1); htrAddScriptGlobal(s,"tbld_current","null",0); htrAddScriptGlobal(s,"tbldb_current","null",0); @@ -229,9 +229,9 @@ httblRenderDynamic(pHtSession s, pWgtrNode tree, int z, httbl_struct* t) if (wgtrGetPropertyValue(sub_tree,"height",DATA_T_INTEGER,POD(&h)) != 0) h = t->min_rowheight; htrAddStylesheetItem_va(s,"\t#tbld%POSsub%POS { POSITION:absolute; VISIBILITY:hidden; LEFT:0px; TOP:0px; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; } \n", - t->id, ++subcnt, t->w-(t->demand_scrollbar?0:18), h, z+2); + t->id, ++subcnt, t->w-(t->demand_scrollbar?0:18), h, z+1); htrAddBodyItem_va(s,"
\n", t->id, subcnt); - htrRenderSubwidgets(s, sub_tree, z+3); + htrRenderSubwidgets(s, sub_tree, z+2); htrAddBodyItem(s,"
\n"); htrAddWgtrObjLinkage_va(s, sub_tree, "tbld%POSsub%POS", t->id, subcnt); htrCheckAddExpression(s, sub_tree, nptr, "display_for"); @@ -243,7 +243,7 @@ httblRenderDynamic(pHtSession s, pWgtrNode tree, int z, httbl_struct* t) //htrRenderWidget(s, sub_tree, z+3); //} } - htrRenderSubwidgets(s, tree, z+3); + htrRenderSubwidgets(s, tree, z+2); htrAddBodyItem(s,"\n"); diff --git a/centrallix/include/obj.h b/centrallix/include/obj.h index 1bda6ced9..29db3b3b4 100644 --- a/centrallix/include/obj.h +++ b/centrallix/include/obj.h @@ -102,6 +102,7 @@ extern char* obj_default_null_fmt; #define DATA_F_SYBQUOTE 8 /* use '' to quote a ', etc */ #define DATA_F_CONVSPECIAL 16 /* convert literal CR LF and TAB to \r \n and \t */ #define DATA_F_DATECONV 32 /* wrap date/time values using convert() */ +#define DATA_F_BRACKETS 64 /* square brackets for intvec/stringvec */ /** Presentation Hints structure --- diff --git a/centrallix/netdrivers/net_http.h b/centrallix/netdrivers/net_http.h index 98d1980bb..1de54a08c 100755 --- a/centrallix/netdrivers/net_http.h +++ b/centrallix/netdrivers/net_http.h @@ -306,6 +306,7 @@ typedef struct int NoCache:1; int UsingTLS:1; int UsingChunkedEncoding:1; + int nObjectsSent; char ResponseContentType[128]; int ResponseContentLength; XArray RequestHeaders; /* of pHttpHeader */ @@ -404,10 +405,18 @@ char* nht_i_GetHeader(pXArray hdrlist, char* hdrname); int nht_i_FreeHeaders(pXArray hdrlist); int nht_i_CheckAccessLog(); +/*** Some enums to control how we're responding to the request ***/ +typedef enum { ResTypeCollection, ResTypeElement, ResTypeBoth } nhtResType_t; +typedef enum { ResFormatAttrs, ResFormatAuto, ResFormatContent, ResFormatBoth } nhtResFormat_t; +typedef enum { ResAttrsBasic, ResAttrsFull, ResAttrsNone } nhtResAttrs_t; + /*** REST implementation ***/ int nht_i_RestGet(pNhtConn conn, pStruct url_inf, pObject obj); int nht_i_RestPatch(pNhtConn conn, pStruct url_inf, pObject obj, struct json_object*); int nht_i_RestPost(pNhtConn conn, pStruct url_inf, int size, char* content); int nht_i_RestDelete(pNhtConn conn, pStruct url_inf, pObject obj); +int nht_i_RestGetElement(pNhtConn conn, pObject obj, nhtResFormat_t res_format, nhtResAttrs_t res_attrs, char* mime_type); +int nht_i_RestWriteAttrSet(pNhtConn conn, pObject obj, nhtResFormat_t res_format, nhtResAttrs_t res_attrs); + #endif diff --git a/centrallix/netdrivers/net_http_osml.c b/centrallix/netdrivers/net_http_osml.c index e0b25ea27..7962841db 100644 --- a/centrallix/netdrivers/net_http_osml.c +++ b/centrallix/netdrivers/net_http_osml.c @@ -225,6 +225,109 @@ nht_i_WriteOneAttr(pObject obj, pNhtConn conn, handle_t tgt, char* attrname) } +/*** nht_i_JsonWriteResponse - start a JSON-formatted response. + ***/ +int +nht_i_JsonWriteResponse(pNhtConn conn, handle_t h_sess, handle_t h_query, char* status) + { + char sess_str[20]; + char query_str[20]; + pXString err_str = NULL; + + /** Mark this as a JSON response document **/ + strtcpy(conn->ResponseContentType, "application/json", sizeof(conn->ResponseContentType)); + nht_i_WriteResponse(conn, 200, "OK", NULL); + + /** Error condition? try to get the details. **/ + if (!strcmp(status, "ERR")) + { + err_str = xsNew(); + if (err_str) + { + mssUserError(err_str); + if (xsLength(err_str) > 0) + status = xsString(err_str); + } + } + + /** Write the start of the JSON response **/ + snprintf(sess_str, sizeof(sess_str), "X" XHN_HANDLE_PRT, h_sess); + snprintf(query_str, sizeof(query_str), "X" XHN_HANDLE_PRT, h_query); + nht_i_QPrintfConn(conn, 0, "{ \"status\": %[null%]\"%[%STR&JSONSTR%]\"%[, \"session\": \"%STR&JSONSTR\"%]%[, \"query\": \"%STR&JSONSTR\"%], \"resultset\": [", + status == NULL, + status != NULL, + status, + h_sess && h_sess != XHN_INVALID_HANDLE, + sess_str, + h_query && h_query != XHN_INVALID_HANDLE, + query_str + ); + conn->nObjectsSent = 0; + + if (err_str) + xsFree(err_str); + + return 0; + } + + +/*** nht_i_JsonWriteAttrs - send one attribute-value list. + ***/ +int +nht_i_JsonWriteAttrs(pNhtConn conn, pObject obj, handle_t tgt) + { + char obj_str[20]; + + /** Output the start **/ + if (conn->nObjectsSent > 0) + nht_i_WriteConn(conn, ",\n{ ", 4, 0); + else + nht_i_WriteConn(conn, "\n{ ", 3, 0); + + /** Output the attribute set **/ + nht_i_RestWriteAttrSet(conn, obj, ResFormatAttrs, ResAttrsFull); + + /** Output the handle ID and closing. We do this last, so that if there is + ** already an attribute __cx_handle in the object, our trailing attr will + ** take precedence. + **/ + snprintf(obj_str, sizeof(obj_str), "X" XHN_HANDLE_PRT, tgt); + nht_i_QPrintfConn(conn, 0, ", \"__cx_handle\":\"%STR&JSONSTR\" }", obj_str); + conn->nObjectsSent++; + + return 0; + } + + +/*** nht_i_JsonWriteResponseEnd - end the json-based response + ***/ +int +nht_i_JsonWriteResponseEnd(pNhtConn conn, int is_closed, int skipped) + { + + nht_i_QPrintfConn(conn, 0, "], \"queryclosed\": %STR, \"skipped\": %INT }\n", + is_closed?"true":"false", + skipped + ); + + return 0; + } + + +/*** nht_i_JsonWriteEntireResponse - write a JSON-formatted response, both beginning + *** and ending, but without any result set. + ***/ +int +nht_i_JsonWriteEntireResponse(pNhtConn conn, handle_t h_sess, handle_t h_query, char* status) + { + + nht_i_JsonWriteResponse(conn, h_sess, h_query, status); + nht_i_JsonWriteResponseEnd(conn, h_query == XHN_INVALID_HANDLE, 0); + + return 0; + } + + /*** nht_i_WriteAttrs - write an HTML-encoded attribute list for the *** object to the connection, given an object and a connection. ***/ @@ -444,11 +547,9 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN pObjQuery qy = NULL; char* sid = NULL; int auto_session = 0; - char sbuf[256]; - char hexbuf[3]; int mode,mask; char* usrtype; - int i,t,n,o,cnt,start,flags,len,rval; + int i,t,n,start,len,rval; pStruct subinf, find_inf; MoneyType m; DateTime dt; @@ -468,11 +569,10 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN pNhtQuery nht_query; char* strval; XArray tail_buffer; - int n_skipped; - - handle_t session_handle; - handle_t query_handle; - handle_t obj_handle; + int n_skipped = 0; + handle_t session_handle = XHN_INVALID_HANDLE; + handle_t query_handle = XHN_INVALID_HANDLE; + handle_t obj_handle = XHN_INVALID_HANDLE; if (DEBUG_OSML) stPrint_ne(req_inf); @@ -492,8 +592,9 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN if (app) xaAddItem(&app->AppOSMLSessions, objsess); session_handle = xhnAllocHandle(&(sess->Hctx), objsess); } - nht_i_WriteResponse(conn, 200, "OK", NULL); - nht_i_WriteHandle(conn, session_handle); + nht_i_JsonWriteEntireResponse(conn, session_handle, XHN_INVALID_HANDLE, "OK"); + //nht_i_WriteResponse(conn, 200, "OK", NULL); + //nht_i_WriteHandle(conn, session_handle); if (DEBUG_OSML) printf("ls__mode=opensession X" XHN_HANDLE_PRT "\n", session_handle); } else @@ -508,8 +609,9 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN { if (!auto_session) { - nht_i_WriteResponse(conn, 200, "OK", " \r\n"); + //nht_i_WriteResponse(conn, 200, "OK", " \r\n"); mssError(1,"NHT","Session ID required for OSML request '%s'",request); + nht_i_JsonWriteEntireResponse(conn, XHN_INVALID_HANDLE, XHN_INVALID_HANDLE, "ERR"); return -1; } else @@ -517,10 +619,11 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN objsess = objOpenSession(req_inf->StrVal); if (!objsess) { - nht_i_WriteResponse(conn, 200, "OK", NULL); session_handle = XHN_INVALID_HANDLE; - nht_i_WriteHandle(conn, session_handle); + //nht_i_WriteResponse(conn, 200, "OK", NULL); + //nht_i_WriteHandle(conn, session_handle); mssError(1,"NHT","Failed to open new OSML session"); + nht_i_JsonWriteEntireResponse(conn, XHN_INVALID_HANDLE, XHN_INVALID_HANDLE, "ERR"); return -1; } else @@ -547,8 +650,9 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN if (!objsess || !ISMAGIC(objsess, MGK_OBJSESSION)) { - nht_i_WriteResponse(conn, 200, "OK", " \r\n"); + //nht_i_WriteResponse(conn, 200, "OK", " \r\n"); mssError(1,"NHT","Invalid Session ID in OSML request"); + nht_i_JsonWriteEntireResponse(conn, XHN_INVALID_HANDLE, XHN_INVALID_HANDLE, "ERR"); return -1; } @@ -565,8 +669,9 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN obj = (pObject)xhnHandlePtr(&(sess->Hctx), obj_handle); if (!obj || !ISMAGIC(obj, MGK_OBJECT)) { - nht_i_WriteResponse(conn, 200, "OK", " \r\n"); + //nht_i_WriteResponse(conn, 200, "OK", " \r\n"); mssError(1,"NHT","Invalid Object ID in OSML request"); + nht_i_JsonWriteEntireResponse(conn, session_handle, XHN_INVALID_HANDLE, "ERR"); return -1; } } @@ -585,8 +690,9 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN qy = (pObjQuery)xhnHandlePtr(&(sess->Hctx), query_handle); if (!qy || !ISMAGIC(qy, MGK_OBJQUERY)) { - nht_i_WriteResponse(conn, 200, "OK", " \r\n"); + //nht_i_WriteResponse(conn, 200, "OK", " \r\n"); mssError(1,"NHT","Invalid Query ID in OSML request"); + nht_i_JsonWriteEntireResponse(conn, session_handle, XHN_INVALID_HANDLE, "ERR"); return -1; } } @@ -596,16 +702,18 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN !strcmp(request,"read") || !strcmp(request,"write") || !strcmp(request,"attrs") || !strcmp(request, "setattrs") || !strcmp(request,"delete"))) { - nht_i_WriteResponse(conn, 200, "OK", " \r\n"); + //nht_i_WriteResponse(conn, 200, "OK", " \r\n"); mssError(1,"NHT","Object ID required for OSML '%s' request", request); + nht_i_JsonWriteEntireResponse(conn, session_handle, XHN_INVALID_HANDLE, "ERR"); return -1; } /** Does this request require a query handle? **/ if (query_handle == XHN_INVALID_HANDLE && (!strcmp(request,"queryfetch") || !strcmp(request,"queryclose"))) { - nht_i_WriteResponse(conn, 200, "OK", " \r\n"); + //nht_i_WriteResponse(conn, 200, "OK", " \r\n"); mssError(1,"NHT","Query ID required for OSML '%s' request", request); + nht_i_JsonWriteEntireResponse(conn, session_handle, XHN_INVALID_HANDLE, "ERR"); return -1; } @@ -614,16 +722,18 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN { if (session_handle == XHN_INVALID_HANDLE) { - nht_i_WriteResponse(conn, 200, "OK", " \r\n"); + //nht_i_WriteResponse(conn, 200, "OK", " \r\n"); mssError(1,"NHT","Illegal attempt to close the default OSML session."); + nht_i_JsonWriteEntireResponse(conn, XHN_INVALID_HANDLE, XHN_INVALID_HANDLE, "ERR"); return -1; } xhnFreeHandle(&(sess->Hctx), session_handle); if (app) xaRemoveItem(&app->AppOSMLSessions, xaFindItem(&app->AppOSMLSessions, objsess)); objCloseSession(objsess); - nht_i_WriteResponse(conn, 200, "OK", NULL); - nht_i_WriteHandle(conn, (handle_t)0); + //nht_i_WriteResponse(conn, 200, "OK", NULL); + //nht_i_WriteHandle(conn, (handle_t)0); + nht_i_JsonWriteEntireResponse(conn, session_handle, XHN_INVALID_HANDLE, "OK"); } else if (!strcmp(request,"open")) { @@ -638,15 +748,18 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN obj_handle = XHN_INVALID_HANDLE; else obj_handle = xhnAllocHandle(&(sess->Hctx), obj); - nht_i_WriteResponse(conn, 200, "OK", NULL); - nht_i_WriteHandle(conn, obj_handle); if (DEBUG_OSML) printf("ls__mode=open X" XHN_HANDLE_PRT "\n", obj_handle); if (obj && stAttrValue_ne(stLookup_ne(req_inf,"ls__notify"),&ptr) >= 0 && !strcmp(ptr,"1")) objRequestNotify(obj, nht_i_UpdateNotify, sess, OBJ_RN_F_ATTRIB); /** Include an attribute listing **/ - nht_i_WriteAttrs(obj,conn,obj_handle,1); + //nht_i_WriteResponse(conn, 200, "OK", NULL); + //nht_i_WriteHandle(conn, obj_handle); + //nht_i_WriteAttrs(obj,conn,obj_handle,1); + nht_i_JsonWriteResponse(conn, session_handle, XHN_INVALID_HANDLE, "OK"); + nht_i_JsonWriteAttrs(conn, obj, obj_handle); + nht_i_JsonWriteResponseEnd(conn, 1, 0); } else if (!strcmp(request,"close")) { @@ -669,8 +782,9 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN xhnFreeHandle(&(sess->Hctx), obj_handle); objClose(obj); } - nht_i_WriteResponse(conn, 200, "OK", NULL); - nht_i_WriteHandle(conn, (handle_t)0); + //nht_i_WriteResponse(conn, 200, "OK", NULL); + //nht_i_WriteHandle(conn, (handle_t)0); + nht_i_JsonWriteEntireResponse(conn, session_handle, XHN_INVALID_HANDLE, "OK"); } else if (!strcmp(request,"objquery")) { @@ -683,19 +797,22 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN query_handle = XHN_INVALID_HANDLE; else query_handle = xhnAllocHandle(&(sess->Hctx), qy); - nht_i_WriteResponse(conn, 200, "OK", NULL); - nht_i_WriteHandle(conn, query_handle); + //nht_i_WriteResponse(conn, 200, "OK", NULL); + //nht_i_WriteHandle(conn, query_handle); + nht_i_JsonWriteEntireResponse(conn, session_handle, query_handle, "OK"); if (DEBUG_OSML) printf("ls__mode=objquery X" XHN_HANDLE_PRT "\n", query_handle); } else if (!strcmp(request,"queryfetch") || !strcmp(request,"multiquery")) { - nht_i_WriteResponse(conn, 200, "OK", NULL); + //nht_i_WriteResponse(conn, 200, "OK", NULL); nht_query = NULL; + + /** If an initial multiquery request, set up the query. **/ if (!strcmp(request,"multiquery")) { if (auto_session) { - nht_i_WriteHandle(conn, session_handle); + //nht_i_WriteHandle(conn, session_handle); } qy = NULL; @@ -722,7 +839,7 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN qy = NULL; query_handle = XHN_INVALID_HANDLE; } - nht_i_WriteHandle(conn, autoclose?XHN_INVALID_HANDLE:query_handle); + //nht_i_WriteHandle(conn, autoclose?XHN_INVALID_HANDLE:query_handle); if (DEBUG_OSML) printf("ls__mode=multiquery X" XHN_HANDLE_PRT "\n", query_handle); if (!qy && nht_query) { @@ -733,6 +850,13 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN xaAddItem(&sess->OsmlQueryList, nht_query); } } + + /** Send the response header to the client. **/ + nht_i_JsonWriteResponse(conn, session_handle, query_handle, qy?"OK":"ERR"); + + /** If we're returning data -- either because of a queryfetch or because of a multiquery + ** with autofetch enabled, retrieve the objects. + **/ if (!strcmp(request,"queryfetch") || (qy && stAttrValue_ne(stLookup_ne(req_inf,"ls__autofetch"),&ptr) == 0 && strtol(ptr,NULL,0))) { if (!nht_query) @@ -759,7 +883,7 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN if (start < 0) start = 0; if (!strcmp(request,"queryfetch")) { - nht_i_WriteHandle(conn, (handle_t)0); + //nht_i_WriteHandle(conn, (handle_t)0); } /** Skip over objects at the beginning? **/ @@ -776,7 +900,6 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN if (stAttrValue_ne(stLookup_ne(req_inf, "ls__tail"), &ptr) == 0 && strtol(ptr,NULL,0)) { xaInit(&tail_buffer, n); - n_skipped = 0; /** Get the object listing **/ while((obj = objQueryFetch(qy, mode)) != NULL) @@ -791,7 +914,7 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN } /** Send the data to the client **/ - nht_i_QPrintfConn(conn, 0, "%INT\r\n", n_skipped); + //FIXME nht_i_QPrintfConn(conn, 0, "%INT\r\n", n_skipped); for(i=0; i= 0 && !strcmp(ptr,"1")) objRequestNotify(obj, nht_i_UpdateNotify, sess, OBJ_RN_F_ATTRIB); - nht_i_WriteAttrs(obj,conn,obj_handle,1); + //nht_i_WriteAttrs(obj,conn,obj_handle,1); + nht_i_JsonWriteAttrs(conn, obj, obj_handle); n--; if (autoclose) objClose(obj); } @@ -825,7 +949,8 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN if (DEBUG_OSML) printf("ls__mode=queryfetch X" XHN_HANDLE_PRT "\n", obj_handle); if (stAttrValue_ne(stLookup_ne(req_inf,"ls__notify"),&ptr) >= 0 && !strcmp(ptr,"1")) objRequestNotify(obj, nht_i_UpdateNotify, sess, OBJ_RN_F_ATTRIB); - nht_i_WriteAttrs(obj,conn,obj_handle,1); + //nht_i_WriteAttrs(obj,conn,obj_handle,1); + nht_i_JsonWriteAttrs(conn, obj, obj_handle); n--; if (autoclose) objClose(obj); } @@ -847,7 +972,7 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN } objQueryClose(qy); qy = NULL; - nht_i_WriteConn(conn, " \r\n", -1, 0); + //nht_i_WriteConn(conn, " \r\n", -1, 0); } else if (autoclose) { @@ -866,6 +991,7 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN qy = NULL; } } + nht_i_JsonWriteResponseEnd(conn, qy == NULL, n_skipped); } else if (!strcmp(request,"queryclose")) { @@ -881,11 +1007,13 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN } } objQueryClose(qy); - nht_i_WriteResponse(conn, 200, "OK", NULL); - nht_i_WriteHandle(conn, (handle_t)0); + nht_i_JsonWriteEntireResponse(conn, session_handle, XHN_INVALID_HANDLE, "OK"); + //nht_i_WriteResponse(conn, 200, "OK", NULL); + //nht_i_WriteHandle(conn, (handle_t)0); } else if (!strcmp(request,"read")) { +#if 00 if (stAttrValue_ne(stLookup_ne(req_inf,"ls__bytecount"),&ptr) < 0) n = 0x7FFFFFFF; else @@ -899,10 +1027,12 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN else flags = strtoi(ptr,NULL,0); start = 1; + while(n > 0 && (cnt=objRead(obj,sbuf,(256>n)?n:256,(o != -1)?o:0,(o != -1)?flags|OBJ_U_SEEK:flags)) > 0) { if(start) { + nht_i_JsonWriteResponse(conn, session_handle, XHN_INVALID_HANDLE, "OK"); nht_i_WriteResponse(conn, 200, "OK", NULL); nht_i_WriteHandle(conn, (handle_t)0); start = 0; @@ -922,15 +1052,21 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN start = 0; } nht_i_WriteConn(conn, "\r\n", 6,0); +#endif + nht_i_JsonWriteEntireResponse(conn, session_handle, query_handle, "OSML:read() - not yet implemented"); } else if (!strcmp(request,"write")) { + nht_i_JsonWriteEntireResponse(conn, session_handle, query_handle, "OSML:write() - not yet implemented"); } else if (!strcmp(request,"attrs")) { - nht_i_WriteResponse(conn, 200, "OK", NULL); - nht_i_WriteHandle(conn, (handle_t)0); - nht_i_WriteAttrs(obj,conn,obj_handle,1); + nht_i_JsonWriteResponse(conn, session_handle, query_handle, "OK"); + nht_i_JsonWriteAttrs(conn, obj, obj_handle); + nht_i_JsonWriteResponseEnd(conn, query_handle == XHN_INVALID_HANDLE, 0); + //nht_i_WriteResponse(conn, 200, "OK", NULL); + //nht_i_WriteHandle(conn, (handle_t)0); + //nht_i_WriteAttrs(obj,conn,obj_handle,1); } else if (!strcmp(request,"setattrs") || !strcmp(request,"create")) { @@ -947,8 +1083,9 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN obj = objOpen(objsess, req_inf->StrVal, OBJ_O_AUTONAME | O_CREAT | O_RDWR, 0600, "system/object"); if (!obj) { - nht_i_WriteResponse(conn, 200, "OK", " \r\n"); - mssError(0,"NHT","Could not create object"); + //nht_i_WriteResponse(conn, 200, "OK", " \r\n"); + mssError(0, "NHT", "Could not create object"); + nht_i_JsonWriteEntireResponse(conn, session_handle, query_handle, "ERR"); return -1; } else @@ -1054,7 +1191,8 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN if (retval < 0) { mssError(0, "NHT", "Failed to set attribute <%s> on object <%s>", subinf->Name, obj->Pathname->Pathbuf + 1); - nht_i_WriteResponse(conn, 200, "OK", " \r\n"); + nht_i_JsonWriteEntireResponse(conn, session_handle, query_handle, "ERR"); + //nht_i_WriteResponse(conn, 200, "OK", " \r\n"); if (!strcmp(request, "create")) { xhnFreeHandle(&(sess->Hctx), obj_handle); @@ -1070,7 +1208,8 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN rval = objCommitObject(obj); if (rval < 0) { - nht_i_WriteResponse(conn, 200, "OK", " \r\n"); + nht_i_JsonWriteEntireResponse(conn, session_handle, query_handle, "ERR"); + //nht_i_WriteResponse(conn, 200, "OK", " \r\n"); objClose(obj); } else @@ -1154,25 +1293,33 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN if (!strcmp(request,"create")) { if (DEBUG_OSML) printf("ls__mode=create X" XHN_HANDLE_PRT "\n", obj_handle); - nht_i_WriteResponse(conn, 200, "OK", NULL); + //nht_i_WriteResponse(conn, 200, "OK", NULL); if (obj) { - nht_i_WriteHandle(conn, obj_handle); + nht_i_JsonWriteResponse(conn, session_handle, query_handle, "OK"); + //nht_i_WriteHandle(conn, obj_handle); } else { - nht_i_WriteConn(conn, " \r\n", -1, 0); + nht_i_JsonWriteEntireResponse(conn, session_handle, query_handle, "ERR"); + //nht_i_WriteConn(conn, " \r\n", -1, 0); } } else { - nht_i_WriteResponse(conn, 200, "OK", NULL); - nht_i_WriteHandle(conn, (handle_t)0); + nht_i_JsonWriteResponse(conn, session_handle, query_handle, "OK"); + //nht_i_WriteResponse(conn, 200, "OK", NULL); + //nht_i_WriteHandle(conn, (handle_t)0); } /** Write the (possibly updated) attrs to the connection **/ if (obj) - nht_i_WriteAttrs(obj,conn,obj_handle,1); + { + //nht_i_WriteAttrs(obj,conn,obj_handle,1); + nht_i_JsonWriteAttrs(conn, obj, obj_handle); + } + + nht_i_JsonWriteResponseEnd(conn, query_handle == XHN_INVALID_HANDLE, 0); } if (nht_query) nht_i_FreeQuery(nht_query); @@ -1199,7 +1346,8 @@ nht_i_OSML(pNhtConn conn, pObject target_obj, char* request, pStruct req_inf, pN rval = objDeleteObj(obj); if (rval < 0) break; } - nht_i_WriteResponse(conn, 200, "OK", (rval==0)?" \r\n":" \r\n"); + //nht_i_WriteResponse(conn, 200, "OK", (rval==0)?" \r\n":" \r\n"); + nht_i_JsonWriteEntireResponse(conn, session_handle, XHN_INVALID_HANDLE, (rval == 0)?"OK":"ERR"); } } diff --git a/centrallix/netdrivers/net_http_rest.c b/centrallix/netdrivers/net_http_rest.c index c236c4da2..5480318b1 100644 --- a/centrallix/netdrivers/net_http_rest.c +++ b/centrallix/netdrivers/net_http_rest.c @@ -31,13 +31,7 @@ /************************************************************************/ -/*** Some enums to control how we're responding to the request ***/ -typedef enum { ResTypeCollection, ResTypeElement, ResTypeBoth } nhtResType_t; -typedef enum { ResFormatAttrs, ResFormatAuto, ResFormatContent, ResFormatBoth } nhtResFormat_t; -typedef enum { ResAttrsBasic, ResAttrsFull, ResAttrsNone } nhtResAttrs_t; - int nht_i_RestGetElementContent(pNhtConn conn, pObject obj, nhtResFormat_t res_format, nhtResAttrs_t res_attrs, char* mime_type); -int nht_i_RestGetElement(pNhtConn conn, pObject obj, nhtResFormat_t res_format, nhtResAttrs_t res_attrs, char* mime_type); int nht_i_RestGetCollection(pNhtConn conn, pObject obj, nhtResType_t res_type, nhtResFormat_t res_format, nhtResAttrs_t res_attrs, int levels); int nht_i_RestGetBoth(pNhtConn conn, pObject obj, nhtResFormat_t res_format, nhtResAttrs_t res_attrs, int res_levels, char* mime_type); @@ -135,7 +129,7 @@ nht_i_RestWriteAttrValue(pNhtConn conn, pObject obj, char* attrname, int data_ty *** entry here, res_attrs will be either ResAttrsBasic or ResAttrsFull. ***/ int -nht_i_RestWriteAttr(pNhtConn conn, pObject obj, char* attrname, nhtResAttrs_t res_attrs, int do_comma) +nht_i_RestWriteAttr(pNhtConn conn, pObject obj, char* attrname, nhtResAttrs_t res_attrs, int do_comma, int sequence, int is_system) { int data_type; int err; @@ -179,7 +173,7 @@ nht_i_RestWriteAttr(pNhtConn conn, pObject obj, char* attrname, nhtResAttrs_t re xsDeInit(&hints_str); objFreeHints(hints); } - nht_i_WriteConn(conn, " }", 2, 0); + nht_i_QPrintfConn(conn, 0, ", \"y\":%STR%[, \"s\":%INT%] }", is_system?"true":"false", sequence > 0, sequence); } return 0; @@ -187,35 +181,18 @@ nht_i_RestWriteAttr(pNhtConn conn, pObject obj, char* attrname, nhtResAttrs_t re -/*** nht_i_RestGetElement() - get a REST element, which will be - *** the list of its attributes, possibly including an objcontent attribute. - *** - *** On entry here, res_format will be ResFormatAttrs or ResFormatBoth. - *** res_attrs could be ResAttrsBasic, ResAttrsFull, or ResAttrsNone. +/*** nht_i_RestWriteAttrSet() - send the list of attributes of an object/element ***/ int -nht_i_RestGetElement(pNhtConn conn, pObject obj, nhtResFormat_t res_format, nhtResAttrs_t res_attrs, char* mime_type) +nht_i_RestWriteAttrSet(pNhtConn conn, pObject obj, nhtResFormat_t res_format, nhtResAttrs_t res_attrs) { - char* path; - char pathbuf[OBJSYS_MAX_PATH]; + int i, seq; + int is_system; char* attr; char xfer_buf[256]; int rcnt; char* sys_attrs[] = { "name", "annotation", "inner_type", "outer_type" }; char sent_sys_attrs[sizeof(sys_attrs)/sizeof(char*)]; - int i; - - /** We begin our object with just a { **/ - nht_i_WriteConn(conn, "{\r\n", -1, 0); - - /** First thing we need to output is the URI of the element **/ - path = objGetPathname(obj); - strtcpy(pathbuf, path, sizeof(pathbuf)); - nht_i_QPrintfConn(conn, 0, "\"@id\":\"%STR&JSONSTR?cx__mode=rest&cx__res_format=%STR&JSONSTR%[&cx__res_attrs=full%]\"", - pathbuf, - (res_format == ResFormatAttrs)?"attrs":"both", - (res_attrs == ResAttrsFull) - ); /** Write any attrs? **/ if (res_attrs != ResAttrsNone) @@ -224,24 +201,31 @@ nht_i_RestGetElement(pNhtConn conn, pObject obj, nhtResFormat_t res_format, nhtR memset(sent_sys_attrs, 0, sizeof(sent_sys_attrs)); /** We loop through the main attributes that are iterable **/ + seq = 1; for(attr=objGetFirstAttr(obj); attr; attr=objGetNextAttr(obj)) { for(i=0; inStrings;i++) { - sprintf(sbuf,"%s\"%s\"", (i==0)?"":",", sv->Strings[i]); - xsConcatenate(dest, sbuf, -1); + xsConcatQPrintf(dest, (flags & DATA_F_SINGLE)?"%[,%]%STR"":"%[,%]%STR&DQUOT", i!=0, sv->Strings[i]); + /*sprintf(sbuf,"%s\"%s\"", (i==0)?"":",", sv->Strings[i]); + xsConcatenate(dest, sbuf, -1);*/ } - if (flags & DATA_F_QUOTED) xsConcatenate(dest,") ",2); + if (flags & DATA_F_QUOTED) xsConcatenate(dest, (flags & DATA_F_BRACKETS)?"] ":") ", 2); break; } @@ -858,7 +859,7 @@ objDataToDouble(int data_type, void* data_ptr) char* objDataToStringTmp(int data_type, void* data_ptr, int flags) { - static char sbuf[80]; + static char sbuf[160]; static char* alloc_str = NULL; static int alloc_len = 0; pDateTime d; @@ -877,9 +878,9 @@ objDataToStringTmp(int data_type, void* data_ptr, int flags) if (data_ptr == NULL) { if (flags & DATA_F_QUOTED) - strcpy(sbuf, " NULL "); + strcpy(sbuf, " NULL "); else - strcpy(sbuf, "NULL"); + strcpy(sbuf, "NULL"); return sbuf; } @@ -888,9 +889,9 @@ objDataToStringTmp(int data_type, void* data_ptr, int flags) { case DATA_T_INTEGER: if (flags & DATA_F_QUOTED) - sprintf(sbuf," %d ",*(int*)data_ptr); + sprintf(sbuf, " %d ", *(int*)data_ptr); else - sprintf(sbuf,"%d",*(int*)data_ptr); + sprintf(sbuf, "%d", *(int*)data_ptr); break; case DATA_T_BINARY: @@ -980,7 +981,7 @@ objDataToStringTmp(int data_type, void* data_ptr, int flags) break; case DATA_T_DOUBLE: - /** sbuf is 80 chars, plenty for our purposes here. **/ + /** sbuf is 160 chars, plenty for our purposes here. **/ if (flags & DATA_F_QUOTED) sprintf(sbuf," %.15g ", *(double*)data_ptr); else @@ -1008,7 +1009,7 @@ objDataToStringTmp(int data_type, void* data_ptr, int flags) m = (pMoneyType)data_ptr; sbuf[0] = '\0'; if (flags & DATA_F_QUOTED) strcat(sbuf, " "); - obj_internal_FormatMoney(m, sbuf + strlen(sbuf),NULL,80-strlen(sbuf)); + obj_internal_FormatMoney(m, sbuf + strlen(sbuf),NULL, sizeof(sbuf)-strlen(sbuf)); if (flags & DATA_F_QUOTED) strcat(sbuf, " "); break; @@ -1016,7 +1017,7 @@ objDataToStringTmp(int data_type, void* data_ptr, int flags) d = (pDateTime)data_ptr; sbuf[0] = '\0'; if (flags & DATA_F_QUOTED) strcat(sbuf, " '"); - obj_internal_FormatDate(d, sbuf + strlen(sbuf),NULL,80-strlen(sbuf)); + obj_internal_FormatDate(d, sbuf + strlen(sbuf),NULL, sizeof(sbuf)-strlen(sbuf)); if (flags & DATA_F_QUOTED) strcat(sbuf, "' "); break; @@ -1029,7 +1030,7 @@ objDataToStringTmp(int data_type, void* data_ptr, int flags) } for(i=0;inIntegers;i++) { - sprintf(ptr,"%s%d", (i==0)?"":",", iv->Integers[i]); + snprintf(ptr, sizeof(sbuf) - (ptr - sbuf) - 2, "%s%d", (i==0)?"":",", iv->Integers[i]); ptr += strlen(ptr); } if (flags & DATA_F_QUOTED) @@ -1044,17 +1045,17 @@ objDataToStringTmp(int data_type, void* data_ptr, int flags) sv = (pStringVec)data_ptr; if (flags & DATA_F_QUOTED) { - strcpy(ptr," ("); + strcpy(ptr, (flags & DATA_F_BRACKETS)?" [":" ("); ptr += 2; } for(i=0;inStrings;i++) { - sprintf(ptr,"%s\"%s\"", (i==0)?"":",", sv->Strings[i]); + snprintf(ptr, sizeof(sbuf) - (ptr - sbuf) - 2, "%s\"%s\"", (i==0)?"":",", sv->Strings[i]); ptr += strlen(ptr); } if (flags & DATA_F_QUOTED) { - strcpy(ptr,") "); + strcpy(ptr, (flags & DATA_F_BRACKETS)?"] ":") "); ptr += 2; } ptr = sbuf; diff --git a/centrallix/osdrivers/objdrv_datafile.c b/centrallix/osdrivers/objdrv_datafile.c index e5d1db376..902f86035 100755 --- a/centrallix/osdrivers/objdrv_datafile.c +++ b/centrallix/osdrivers/objdrv_datafile.c @@ -3754,7 +3754,7 @@ dat_internal_FillTdata(pDatNode dn, pObject obj, char* filename) int columns = 0; unsigned char text[DAT_CACHE_PAGESIZE]; /** Read in the first line of the csv file **/ - int len = objRead(obj->Prev, text, DAT_CACHE_PAGESIZE, 0, 0); + int len = objRead(obj->Prev, (char*)text, DAT_CACHE_PAGESIZE, 0, 0); if (len < 1) { success = 0; diff --git a/centrallix/osdrivers/objdrv_query.c b/centrallix/osdrivers/objdrv_query.c index fca8193da..fb05b391e 100644 --- a/centrallix/osdrivers/objdrv_query.c +++ b/centrallix/osdrivers/objdrv_query.c @@ -355,7 +355,7 @@ qyOpen(pObject obj, int mask, pContentType systype, char* usrtype, pObjTrxTree* inf->Node->OpenCnt++; /** Get SQL string **/ - if (stAttrValue(stLookup(inf->Node->Data,"sql"),NULL,&sql,0) != 0) + if (stGetAttrValue(stLookup(inf->Node->Data,"sql"), DATA_T_STRING, POD(&sql), 0) != 0) { mssError(1,"QY","'sql' property must be supplied for query objects"); goto error; diff --git a/centrallix/report/prtmgmt_v3_lm_table.c b/centrallix/report/prtmgmt_v3_lm_table.c index b89bf7584..e6ac0700d 100644 --- a/centrallix/report/prtmgmt_v3_lm_table.c +++ b/centrallix/report/prtmgmt_v3_lm_table.c @@ -240,7 +240,7 @@ prt_tablm_ChildBreakReq(pPrtObjStream this, pPrtObjStream child, pPrtObjStream * int prt_tablm_ChildResizeReq(pPrtObjStream this, pPrtObjStream child, double req_width, double req_height) { - pPrtObjStream table_obj, new_parent, old_parent; + pPrtObjStream table_obj, new_parent, old_parent = NULL; double new_height; PRT_DEBUG("prt_tablm_ChildResizeReq() %s %8.8x %s %8.8x\n", this->ObjType->TypeName, this, child->ObjType->TypeName, child); diff --git a/centrallix/report/prtmgmt_v3_lm_text.c b/centrallix/report/prtmgmt_v3_lm_text.c index 3b30f0a91..42e83ec5b 100644 --- a/centrallix/report/prtmgmt_v3_lm_text.c +++ b/centrallix/report/prtmgmt_v3_lm_text.c @@ -836,7 +836,7 @@ prt_textlm_AddObject(pPrtObjStream this, pPrtObjStream new_child_obj) pPrtObjStream split_obj_list = NULL; pPrtObjStream new_parent; pPrtObjStream search; - double x,y,yb; + double x,y; int handle_id; /** Need to adjust the height/width if unspecified? **/ diff --git a/centrallix/report/prtmgmt_v3_od_ps.c b/centrallix/report/prtmgmt_v3_od_ps.c index 4b58eb258..c8ae3d0cb 100644 --- a/centrallix/report/prtmgmt_v3_od_ps.c +++ b/centrallix/report/prtmgmt_v3_od_ps.c @@ -867,7 +867,7 @@ prt_psod_WriteRasterData(void* context_v, pPrtImage img, double width, double he direct_map = 1; /** Allocate image data row **/ - imgrow = (char*)nmSysMalloc(cols*6 + 1 + 1); + imgrow = (unsigned char*)nmSysMalloc(cols*6 + 1 + 1); if (!imgrow) return -1; @@ -919,7 +919,7 @@ prt_psod_WriteRasterData(void* context_v, pPrtImage img, double width, double he } imgrow[cols*6] = '\n'; imgrow[cols*6+1] = '\0'; - prt_psod_Output(context, imgrow, cols*6+1); + prt_psod_Output(context, (char*)imgrow, cols*6+1); } /** Restore graphics context when done **/ diff --git a/centrallix/utility/hints.c b/centrallix/utility/hints.c index add9e1d70..f0650e52f 100644 --- a/centrallix/utility/hints.c +++ b/centrallix/utility/hints.c @@ -281,7 +281,6 @@ pObjPresentationHints objInfToHints(pStructInf inf, int data_type) { pObjPresentationHints ph; - pParamObjects tmplist; char* ptr; char* newptr; int n,cnt; @@ -300,6 +299,10 @@ objInfToHints(pStructInf inf, int data_type) ph->DefaultExpr = stGetExpression(stLookup(inf,"default"), 0); ph->MinValue = stGetExpression(stLookup(inf,"min"), 0); ph->MaxValue = stGetExpression(stLookup(inf,"max"), 0); + if (ph->Constraint) ph->Constraint = expDuplicateExpression(ph->Constraint); + if (ph->DefaultExpr) ph->DefaultExpr = expDuplicateExpression(ph->DefaultExpr); + if (ph->MinValue) ph->MinValue = expDuplicateExpression(ph->MinValue); + if (ph->MaxValue) ph->MaxValue = expDuplicateExpression(ph->MaxValue); /** Compile expressions, if any **/ /*if (ph->Constraint || ph->DefaultExpr || ph->MinValue || ph->MaxValue)