Files
Facilitor/APPL/API2/api2.inc
Jos Groot Lipman 86f5f25ebd Merge 2017.1 Gold E patches
svn path=/Website/trunk/; revision=34851
2017-08-07 15:49:58 +00:00

1710 lines
75 KiB
PHP
Raw Blame History

<% /*
$Revision$
$Id$
File: api2.inc
Description: Functies voor manipuleren van de modellen
Notes: Hier wordt weinig met de 'buitenwereld' gecommuniceerd,
dat gebeurt voornamelijk in api2_rest.inc
Status: Nog TODO's wegwerken
*/
%>
<!-- #include file="../Shared/save2db.inc" -->
<%
api2 = {
form2JSONdata: function _form2JSONdata(model, params, formfields) // Maak een jsondata-object voor gebruik vanuit model_xxx.inc
{
var jsondata = {id: params.filter.id};
// Form parameters van gewone velden.
for (i=0; i<formfields.length; i++)
{
// Voeg een nieuwe combinatie naam:waarde toe aan het jsondata object als er een frm of val bekend is.
var val;
if (formfields[i].frm)
{
var fld = model.fields[formfields[i].name];
var typ = model.fields[formfields[i].name].typ;
// Haal waarde uit de formparameters.
if (typ == "check" || typ == "check0")
{
if (Request.Form("has_" + formfields[i].frm).count == 0)
continue; // niet aanwezig
}
else
{
if (Request.Form(formfields[i].frm).count == 0)
continue; // niet aanwezig
}
switch (typ)
{
case "float":
case "currency":
val = getFParamFloat(formfields[i].frm, null);
break;
case "key":
case "number":
if (fld.bits)
{
val = 0;
for (var b=0; b < fld.bits.length; b++)
{
var bit = fld.bits[b];
if (bit.typ == "check")
{
for (var r=0; r < bit.radios.length; r++)
{
var radio = bit.radios[r];
var invert = radio.invert;
// LET OP: Als de bit readonly is wordt er wel een form-parameter meegegeven met waarde "on" of leeg,
// om de oude waarde te kunnen bewaren.
// Als de bit editable is er alleen de form-parameter aanwezig als het veld aangevinkt is.
if (radio.readonly)
val += ((Request.Form(radio.name)=="on")?(invert?0:radio.mask):(invert?radio.mask:0));
else
val += ((Request.Form(radio.name).count==1)?(invert?0:radio.mask):(invert?radio.mask:0));
}
}
else if (bit.typ == "radio")
{
val += parseInt(Request.Form(bit.name));
}
else if (bit.typ == "LOV")
{
val += getFParamInt(bit.name, 0);
}
}
}
else
{
if (params.multiadd == formfields[i].frm)
val = getFParamIntArray(formfields[i].frm, null);
else
val = getFParamInt(formfields[i].frm, null);
}
break;
case "check":
case "check0":
var invert = model.fields[formfields[i].name].invert;
val = (Request.Form(formfields[i].frm).count==1)?(invert?0:1):(invert?1:0);
break;
case "date":
case "datetime":
val = getFParamDate(formfields[i].frm, null);
break;
case "memo":
case "varchar":
val = getFParam(formfields[i].frm, null);
break;
default:
{
__DoLog(model.fields[formfields[i].name].typ);
INTERN_ERROR_UNKNOWN_TYPE;
}
}
}
else
val = formfields[i].val;
//
jsondata[formfields[i].name] = val;
}
return jsondata;
},
// Verwerk filtervelden die in de url zijn meegegeven
sqlfilter: function _sqlfilter(params, model)
{
var wheres = [];
if (params.filter)
{
for (var fld in model.fields)
{
if (fld.substring(0,1) == "_")
continue;
var field = model.fields[fld];
if ("filter" in field)
var filter = field.filter
else // default filtertypes
{
switch (field.typ)
{
case "key":
filter = "exact";
break;
case "float":
case "currency":
case "number":
filter = "range";
break;
case "check":
case "check0":
filter = "exact";
break;
case "varchar":
case "memo":
case "html":
filter = "like";
break;
case "date":
case "datetime":
filter = "range";
break;
default:
if (fld in params.filter) // Je probeert er wel op te filteren?
{
__DoLog(field);
__DoLog(params.filter);
UNKNOWN_FILTER_TYPE;
}
}
}
// We kijken of dit model-veld in de filterparameters voorkomt. Voor ranges kunnen prefixes in gebruik zijn
var filterval = null;
if (fld in params.filter || "start_" + fld in params.filter || "end_" + fld in params.filter)
{
// filterval is de meegegeven filterwaarde voor dit veld
if (fld in params.filter)
{
filterval = params.filter[fld];
if (filterval === "" || filterval === null)
continue;
if (filterval < 0 && field.typ == "key")
continue;
if (filter == "like" && typeof filterval == "string" &&
(filterval.toUpperCase() == "NULL" || filterval.toUpperCase() == "NOT NULL"))
continue; // niet compatible met "like" filter
}
// Voor ranges komt de naam (misschien) niet letterlijk voor, maar (mogelijk) met start_ of end_ ervoor
// meerdere filters voor 1 veld dus wellicht
if (filter == "range") {
var filterval1 = params.filter["start_" + fld];
var filterval2 = params.filter["end_" + fld];
// Als er maar 1 filterwaarde is, dan gedraagt zich die als start_
if (filterval && !filterval1)
filterval1 = filterval;
if ((filterval1 === "" || filterval1 === null) && (filterval2 === "" || filterval2 === null))
continue;
if (typeof filterval1 == "string" && (filterval1.toUpperCase() == "NULL" || filterval1.toUpperCase() == "NOT NULL"))
continue; // niet compatible met "range" filter
}
// Nu is ook voor ranges alles weer normaal
var clause;
var operand = " = ";
if (filterval && typeof filterval == "string" &&
(filterval.toUpperCase() == "NULL" || filterval.toUpperCase() == "NOT NULL"))
{
operand = " IS ";
var safe_val = filterval;
}
else switch (field.typ)
{
case "key":
if (!filterval)
continue;
if (filterval instanceof Array)
{
safe_val = filterval.join(", ");
if (typeof filter != 'function')
safe_val = "(" + safe_val + ")";
operand = " IN ";
}
else if (String(filterval).indexOf(",") != -1) // let op: bij buildings/1234.json is id al numeriek gemaakt
// NB: index=-1 als het geen array is.
{
var arr = params.filter[fld].split(",");
safe_val = safe.int_array(arr).join(",");
if (typeof filter != 'function')
safe_val = "(" + safe_val + ")";
operand = " IN ";
}
else if (String(filterval).substr(0,1) == '~'
&& field.foreign
&& typeof field.foreign == 'string'
) // Als je een foreign filter met ~ begint dan filtert hij op de tekst
{
operand = " IN ";
var foreign = foreignKeyTable(field.foreign);
if (!foreign)
MISSING_FOREIGN;
var fieldname = (foreign.name||foreign.desc);
var foreignsql = "SELECT " + foreign.key
+ " FROM " + foreign.tbl + " " + (foreign.alias||"xx")
+ " WHERE ";
if (foreign.where)
foreignsql += foreign.where + " AND ";
foreignsql += "UPPER(" + (foreign.alias||"xx") + "." + fieldname + ") LIKE " + safe.quoted_sql_wild("%"+String(filterval).substr(1) + "%");
safe_val = foreignsql;
if (typeof filter != 'function')
safe_val = "(" + safe_val + ")";
}
else
{
if (String(filterval).toLowerCase() == "self")
var safe_val = user_key;
else
var safe_val = parseInt(filterval, 10);
if (isNaN(safe_val))
{
return ["0=1"];
}
}
break;
case "float":
case "currency":
var safe_val = parseFloat(filterval);
if (filter == "range")
{
if (filterval1) {
var safe_val1 = parseFloat(filterval1);
}
if (filterval2) {
var safe_val2 = parseFloat(filterval2);
}
if (filterval1 && filterval2) {
operand = " BETWEEN ";
safe_val = safe_val1+" AND "+safe_val2;
} else if (filterval1) {
operand = " >= ";
safe_val = safe_val1;
} else if (filterval2) {
operand = " <= ";
safe_val = safe_val2;
}
}
break;
case "number":
var safe_val = parseInt(filterval, 10);
if (filterval && isNaN(safe_val))
{
return ["0=1"]; // niets gevonden
}
if (filter == "range")
{
if (filterval1) {
var safe_val1 = parseInt(filterval1, 10);
}
if (filterval2) {
var safe_val2 = parseInt(filterval2, 10);
}
if (filterval1 && filterval2) {
operand = " BETWEEN ";
safe_val = safe_val1+" AND "+safe_val2;
} else if (filterval1) {
operand = " >= ";
safe_val = safe_val1;
} else if (filterval2) {
operand = " <= ";
safe_val = safe_val2;
}
}
break;
case "check":
case "check0":
if (filterval == "on")
var safe_val = 1;
else if (filterval == "off")
var safe_val = 0;
else
var safe_val = parseInt(filterval, 10);
if (isNaN(safe_val))
{
return ["0=1"]; // niets gevonden
}
break;
case "varchar": // Als fld.match(/^fclt_d_/) dan is het een old-style rapport filterveld
case "memo":
case "html":
if (filterval instanceof Array)
{
safe_val = safe.quoted_sql_join(filterval);
if (typeof filter != 'function')
safe_val = "(" + safe_val + ")";
operand = " IN ";
}
else switch (filter)
{
case "exact":
if (field.autolike) // Doen we bij rapportages
{
filter = "like"; // Lijkt tegenstrijdig voor 'exact' maar we staan nu wel
// toe dat je zelf *, % en _ gebruikt
var safe_val = safe.quoted_sql_wild(filterval);
}
else
{
if (field.caseinsensitive)
var safe_val = safe.quoted_sql_upper(filterval);
else
var safe_val = safe.quoted_sql(filterval);
}
break;
case "like":
var safe_val = safe.quoted_sql_wild("%"+filterval+"%");
break;
case "range":
var safe_val;
if (filterval1) {
if (fld.match(/^fclt_d_/))
safe_val1 = api2.toDate(filterval1).beginToSQL();
else
safe_val1 = safe.quoted_sql(filterval1);
}
if (filterval2) {
if (fld.match(/^fclt_d_/))
safe_val2 = api2.toDate(filterval2).endToSQL();
else
safe_val2 = safe.quoted_sql(filterval2);
}
if (filterval1 && filterval2) {
operand = " BETWEEN ";
safe_val = safe_val1+" AND "+safe_val2;
} else if (filterval1) {
operand = " >= ";
safe_val = safe_val1;
} else if (filterval2) {
operand = " <= ";
safe_val = safe_val2;
}
break;
}
break;
case "date": // onderscheid date en datetime?
case "datetime":
if (filter == "exact")
{
var safe_val = api2.toDate(filterval);
safe_val = safe_val.toSQL(); // Een exacte *tijd* component ondersteunen we niet.
operand = " = ";
}
else if (filter == "range")
{
var safe_val;
if (filterval1) {
var safe_val1 = api2.toDate(filterval1);
var withtime = (field.typ == "datetime" && api2.hasTime(safe_val1));
safe_val1 = withtime?safe_val1.toSQL(true):safe_val1.beginToSQL();
}
if (filterval2) {
var safe_val2 = api2.toDate(filterval2);
var withtime = (field.typ == "datetime" && api2.hasTime(safe_val2));
safe_val2 = withtime?safe_val2.toSQL(true):safe_val2.endToSQL();
}
if (filterval1 && filterval2) {
operand = " BETWEEN ";
safe_val = safe_val1+" AND "+safe_val2;
} else if (filterval1) {
operand = " >= ";
safe_val = safe_val1;
} else if (filterval2) {
operand = " <= ";
safe_val = safe_val2;
}
}
break;
default:
__DoLog(field.typ);
UNKNOWN_FILTER_TYPE;
}
var dbs = field.dbs;
if (model.aliasprefix)
dbs = model.aliasprefix + dbs;
if (dbs.indexOf(".") < 0)
dbs = model.table + "." + field.dbs;
if (field.sql && !field.keepdbsforfilter)
dbs = field.sql; /* dit werkt bv voor cnt-XD */
if (filter == "like")
{
if (operand != " IN ")
operand = " LIKE ";
if (field.typ == "varchar" || field.typ == "memo" || field.typ == "html")
{
if (field.islcl)
dbs = 'lcl.l({0}, 1)'.format(dbs);
field.caseinsensitive = true;
}
}
if (field.caseinsensitive)
{
dbs = "UPPER(" + dbs + ")";
safe_val = safe_val.toUpperCase();
}
if (filter == "range" && fld.match(/^fclt_d_/))
{
dbs = "TO_DATE(" + dbs + ",'dd-mm-yyyy')";
}
if (filter == "exact" && (field.typ == "date" || field.typ == "datetime"))
{
dbs = "TRUNC("+dbs+")";
}
if (fld == 'id') // Bij filtering op id negeren we *al het andere*
return [dbs + operand + safe_val];
// Als filter een functie is dan de functie aanroepen.
if (typeof filter == 'function')
wheres.push(filter(safe_val));
else
wheres.push(dbs + operand + safe_val);
}
}
if (params.filter.includefilter) // Bijvoorbeeld "rooms.location,configurations.res_opstelling_key"
{
var incarr = params.filter.includefilter.split(",");
for (var i = 0; i < incarr.length; i++)
{
var inc = incarr[i].split(".")[0];
var fld = incarr[i].split(".")[1];
var incmodel = model.includes[inc].model;
var newfilter = {};
newfilter[fld] = params.filter[fld];
var wheresinc = api2.sqlfilter({ filter: newfilter }, incmodel);
if (wheresinc.length)
{
// TODO: res_alg_ruimte heeft ook nog weer een foreign op het locatiekey veld....
wheresinc.push(incmodel.table + "." + incmodel.fields[model.includes[inc].joinfield].dbs + " = " + model.table + "." + model.fields.id.dbs);
var where = "EXISTS (SELECT 1 FROM {0} WHERE {1})".format(incmodel.table, wheresinc.join(" AND "))
wheres.push(where);
}
}
}
}
return wheres;
},
// Als er voor een veldnaam een alias moet worden gebruikt, doe het dan op deze manier.
sqlfield_alias: function _sqlfield_alias(model, fld)
{
var field = model.fields[fld];
return model.aliasprefix + (field.dbs == model.primary ? field.dbs : fld);
},
// Vult bij alle varchar en memo velden de len property in als dat nog niet is gedaan
setfieldlengths: function _setfieldlengths(model)
{
var dblengths = null;
for (var fld in model.fields)
{
if (fld.substring(0,1) == "_")
continue;
var field = model.fields[fld];
if (!field.dbs || field.hidden || field.len || field.readonly)
continue;
if (field.typ != "varchar" && field.typ != "memo")
continue;
if (!dblengths)
{
dblengths = {};
var sql = "SELECT column_name, data_length"
+ " FROM user_tab_columns"
+ " WHERE table_name = " + safe.quoted_sql_upper(model.table)
+ " AND data_type = 'VARCHAR2'";
var oRs = Oracle.Execute(sql);
while (!oRs.Eof)
{
dblengths[oRs("column_name").Value] = oRs("data_length").Value;
oRs.MoveNext();
}
oRs.Close()
__Log(dblengths);
}
field.len = dblengths[field.dbs.toUpperCase()];
}
},
// Bepaal bij een GET welke velden op te halen
sqlfields: function _sqlfields(params, model)
{
model.aliasprefix = model.aliasprefix || "";
var selects = [];
if ("fntablesql" in model)
model.tablesql = model.fntablesql();
var withs = {};
var tables = [ model.tablesql || model.table ];
var wheres = [];
var orderbys = [];
var name_cnt = 0;
if (model.includes && "tracking" in model.includes && params.include && inArray("lastchange", params.include))
{
// lastchange veld altijd toevoegen.
// Onafhankelijk van het feit of de include=tracking aanstaat
var sql = "SELECT MAX(fac_tracking_datum)"
+ " FROM fac_tracking ft, "
+ " fac_srtnotificatie fs"
+ " WHERE ft.fac_srtnotificatie_key = fs.fac_srtnotificatie_key"
+ " AND fs.fac_srtnotificatie_xmlnode IN (" + safe.quoted_sql_join(model.includes["tracking"].model.xmlnodes) + ") "
+ " AND fac_tracking_refkey = " + model.table + "." + model.primary;
model.fields["lastchange"] = { dbs: "lastchange", sql: "(" + sql + ")", typ: 'datetime', filter: "range" };
}
for (var fld in model.fields)
{
if (fld.substring(0,1) == "_")
continue;
var field = model.fields[fld];
if (field.hidden)
continue;
var dbs = field.dbs;
if (field.sql)
{
if (model.aliasprefix)
dbs = model.aliasprefix + fld;
selects.push(field.sql + " AS " + dbs);
}
else if (dbs)
{
// veldnamen moeten gelijk zijn bij het ophalen. Zie ook: sql2jsonval
if (dbs.indexOf(".") < 0)
{
dbs = model.table + "." + dbs;
if (field.translate && !params.for_edit)
dbs = lcl.xsql(dbs, model.table + "." + model.fields.id.dbs, null, true); // never autolcl
if (model.aliasprefix)
dbs += " AS " + api2.sqlfield_alias(model, fld);
else
if (field.translate && !params.for_edit)
dbs += " AS " + field.dbs;
}
if (!inArray(dbs, selects))
selects.push(dbs);
}
if (field.val instanceof Function)
continue;
if (field.foreign && typeof field.foreign != 'function') // de functions komen later
{
if (typeof field.foreign == 'string')
{
var foreign = foreignKeyTable(field.foreign);
if (!foreign)
MISSING_FOREIGN;
}
else
foreign = field.foreign;
var fieldname = (foreign.name||foreign.desc);
field.foreignsql = "SELECT " + fieldname
+ " FROM " + foreign.tbl + " " + (foreign.alias||"xx")
+ " WHERE ";
if (foreign.where)
field.foreignsql += foreign.where + " AND ";
field.foreignsql += (foreign.alias||"xx") + "." + foreign.key + " = " + (field.sql||(field.dbs.indexOf(".") < 0? model.table + ".":"") + field.dbs);
}
if (field.foreignsql)
{
name_cnt ++;
field._foreignname = "foreign_" + name_cnt; // Genereer een niet al te extreem lange naam
selects.push("(" + field.foreignsql + ") " + " AS " + model.aliasprefix + field._foreignname);
}
}
if (params.include && model.includes)
{
// Dubbelen er uit halen, dat geeft lelijke foutmeldingen en kan ontstaan
// als je include=visitors in de url meegeeft <20>n visitors in de body POST
var tmp = {};
for (var i in params.include) {
tmp[params.include[i]] = 1;
}
params.include = [];
for (var i in tmp) {
params.include.push(i);
}
var inccnt = 0;
for (var i in params.include)
{
if (params.include[i] == "lastchange") // special case
continue;
if (params.include[i] in model.includes)
{
var inc = model.includes[params.include[i]];
// Geneste includes verwijderen, we doen hooguit 1 diep
if ("model" in inc) // reservablerooms/include/occupations heeft geen model
delete inc.model.includes;
if (inc.model)
{
if (inc.single_only && !params.filter.id)
{
abort_with_warning("Include '{0}' only allowed with single '{1}'".format(params.include[i], model.record_name));
}
inccnt ++;
inc.model.aliasprefix = "I" + inccnt;
// Zorg dat de parent_key niet genoemd wordt bij de children. Dat is redundant
if (inc.joinfield in inc.model.fields)
inc.model.fields[inc.joinfield].hidden = true;
// Fabriceer een limited include tabel zodat bij fac_groep niet te veel
// personen uit fac_groeprechten getoond worden
if (inc.limit)
{
withs["L_" + inc.model.table] =
"(SELECT " + inc.model.table + ".*,"
+ " ROW_NUMBER ()"
+ " OVER (PARTITION BY " + inc.model.fields[inc.joinfield].dbs
+ " ORDER BY " + inc.limit + " ASC)"
+ " AS rn"
+ " FROM " + inc.model.table + ")";
inc.model.table = "L_" + inc.model.table; // LET OP: We vervangen dus de tabel tijdelijk
wheres.push(inc.model.table + ".rn <= 6");
}
var incquery = api2.sqlfields(params, inc.model);
selects = selects.concat (incquery.selects);
if (inc.model.gparams && inc.model.gparams.GET.wheres)
{
var tablesql = "(SELECT * FROM {0} WHERE {1}) {2}".format(incquery.tables.join(", "),
inc.model.gparams.GET.wheres.join(" AND "),
inc.model.table);
tables.push(tablesql);
}
else
tables = tables.concat (incquery.tables);
wheres = wheres.concat (incquery.wheres);
if (inc.model.gparams && inc.model.gparams.GET.tables)
tables = tables.concat(inc.model.gparams.GET.tables);
// Bij includes tonen we ook verwijderde records indien gewenst.
if (inc.model.soft_delete &&
( (params.filter.has_show_deleted && params.filter.show_deleted != "on") || !params.filter.has_show_deleted)
)
wheres.push(inc.model.soft_delete + " IS NULL");
// Bij includes ook de vervallen records tonen indien gewenst.
if (inc.model.soft_expire &&
( (params.filter.has_show_expired && params.filter.show_expired != "on") || !params.filter.has_show_expired)
)
wheres.push("(" + model.soft_expire + " IS NULL OR " + model.soft_expire + " > SYSDATE)");
if (inc.joinfunction)
{
var where = inc.joinfunction(params);
if (typeof where == "string")
wheres.push (where);
else
{
tables = tables.concat (where.tables);
wheres.push (where.where);
}
}
else
{
if (params.orderby)
{
for (var i = 0; i < params.orderby.length; i++)
{
var field = inc.model.fields[params.orderby[i]];
if (field)
orderbys.push(inc.model.table + "." + field.dbs);
}
}
// In orderbys worden de include primary keys verzameld om te sorteren
// We bieden daar geen garantie op maar tijdens testen is deterministisch
// gedrag wel fijn.
orderbys.push(inc.model.aliasprefix + inc.model.primary);
// simpel op joinfield
var outer = "(+)";
if ("outertoggle" in inc)
{
if (params.filter.has_scf_outer && params.filter.scf_outer == "on")
outer = "";
else
outer = inc.outertoggle.def?"":"(+)";
}
wheres.push ( model.table + "." + model.primary + "=" + inc.model.table + "." + inc.model.fields[inc.joinfield].dbs + outer);
}
}
}
else
__Log("Unknown include '{0}' requested".format(params.include[i]));
}
}
return { withs: withs, selects: selects, tables: tables, wheres: wheres, orderbys: orderbys };
},
// TODO the_key *moet* bestaan. Andere filtervelden negeren we
// Bouw een fields object zoals save2db dat verwacht/ kent
update_fields: function _update_fields(params, model, jsondata)
{
var fields = {};
for (var fld in model.fields)
{
var field = model.fields[fld];
// De key halen we uit de url, die in de JSON negeren we
if (fld == "id")
continue;
if ("sql" in field)
continue;
if (!(fld in jsondata) && !field.fnval)
continue;
if (field.readonly)
continue;
if (field.insertonly && !params.isNew)
continue;
if (field.fnval)
var newval = field.fnval(jsondata);
else // simpel
var newval = jsondata[fld];
if (field.LOV && newval && typeof newval == "object" && "id" in newval)
{ // dereference
newval = newval.id;
}
switch (field.typ)
{
case "key": // De foreign keys action { "id": "5", "name": "afhalen" }
if (newval && typeof newval == "object")
{
if ("id" in newval)
{ // dereference
newval = newval.id;
jsondata[fld] = newval;
}
else if ("name" in newval && field.foreign && typeof field.foreign != 'function')
{
if (typeof field.foreign == 'string')
{
field.foreign = foreignKeyTable(field.foreign);
}
if (field.foreign.desc_is_unique) // Dan mag je die bij saven ook opgeven
{
field.typ = "sql";
newval = "(SELECT {0} FROM {1} WHERE {2} = {3})".format(field.foreign.key,
field.foreign.tbl,
field.foreign.desc,
safe.quoted_sql(newval.name));
}
}
}
break;
case "date":
case "datetime":
if (newval === "")
newval = null;
// LET OP: Een (new Date) gemaakt binnen een plugin is vreemd genoeg geen (instanceof Date)
// Waarschijnlijk gebruikt een wsc een ander Date object als (ASP)JScript?
if (newval !== null && typeof newval == "object" && !(newval instanceof Date))
newval = new Date(newval);
if (newval !== null && !(newval instanceof Date))
{
abort_with_warning("Invalid " + field.typ + " (" + model.record_name + "." + fld + "): " + newval);
}
break;
case "float":
case "currency":
case "number":
if (isNaN(newval))
abort_with_warning("Invalid number (" + model.record_name + "." + fld + "): " + newval);
break;
}
if (field.dbs.indexOf(".") >= 0) // complexe foreign key
continue;
// LET OP: newfield en fields zijn in het formaat wat save2db verwacht/kent
// dat lijkt wel op een standaard model.fields object maar is soms een fractie anders
// Zo heeft een save2db field geen "label" en is "track" geen boolean maar de label-tekst
// LET OP2: save2db werkt eigenlijk met een Array voor fields maar wij hebben een
// object met "name": { field.. } wat daar wel omgezet wordt.
var newfield = { dbs: field.dbs,
typ: field.typ,
track: field.track && field.label?field.label:field.track,
foreign: field.foreign,
val: newval,
len: field.len
};
fields[fld] = newfield;
}
return fields;
},
// Verwerk de POST en PUT (== insert en update)
// Merk op dat het resultaat van deze functie redelijk ongedefinieerd is
process_includes: function(params, model, jsondata, the_key)
{
if (!model.includes)
return [];
for (var incname in model.includes)
{
if (incname in jsondata) // i=="visitors"
{
var inc = model.includes[incname];
if (inc.model && inc.enable_update) // andere includes zijn nog niet bij te werken
{
// Als je bij een PUT/POST een include in de BODY zet geven we hem automagisch terug
params.include = params.include || [];
params.include.push(incname);
var incmodel = inc.model;
var existing_includes = {};
if (!params.isNew)
{
// Vul existing_includes met bestaande records in de database
if ("fntablesql" in incmodel)
incmodel.tablesql = incmodel.fntablesql();
var sql = "SELECT " + incmodel.primary
+ " FROM " + (incmodel.tablesql || incmodel.table)
+ " WHERE " + incmodel.table + "." + incmodel.fields[inc.joinfield].dbs + "=" + the_key;
var oRs = Oracle.Execute(sql);
while (!oRs.Eof)
{
existing_includes[oRs(incmodel.primary).Value] = { found: false }; // vooralsnog niet in json-data gevonden
oRs.MoveNext();
}
oRs.Close();
}
var incdata = jsondata[incname]; // Array zoals via API aangeleverd
for (var j=0; j<incdata.length; j++)
{
var inckey = incdata[j][incmodel.keyfield || "id"]; // Die kan er zijn. custom_field werkt via keyfield
if (incmodel.keyfield || !(inckey > 0) || params.isNew)
{
delete incdata[j]["id"]; // voor als je bij isNew toch keys had meegegeven
incdata[j][inc.joinfield] = the_key; // de parent_key altijd zetten
__Log("Nu ga ik een '{0}' toevoegen".format(incname));
incmodel.REST_POST(params, incdata[j], the_key);
}
else if (inckey in existing_includes)
{
__Log("Nu ga ik '{0}' {1} updaten".format(incname, inckey));
incmodel.REST_PUT(params, incdata[j], inckey, the_key);
existing_includes[inckey].found = true;
}
}
for (oldi in existing_includes)
{
if (!existing_includes[oldi].found)
{
incmodel.REST_DELETE(params, oldi);
}
}
}
else
{
//if (inc.func)
}
}
}
},
// Geeft de GET terug van een enkel veld
sql2jsonval: function _sql2jsonval(oRs, fld, model)
{
var field = model.fields[fld];
var sqlfieldname = (model.aliasprefix ? api2.sqlfield_alias(model, fld) : field.dbs);
if (field.val instanceof Function)
var val = field.val(oRs, field, model, sqlfieldname);
else if (field.dbs.indexOf(".") < 0)
{
var val = oRs(sqlfieldname).Value;
}
else
var val = oRs(field.dbs.split(".")[1]).Value;
if (field.typ == "check" || field.typ == "check0")
{
if (field.invert)
val = (val==1?0:1);
}
if (val === null)
return val;
if (field.typ == "date")
val = new Date(val);
if (field.typ == "datetime")
val = new Date(val);
if (field.typ == "time") // In de database ondersteunen we dit niet maar komt voor bij fac_report
val = new Date(val);
// Wat te doen met lege waarde
// action: null
// action: {key: null, name: null}
// action: {}
// of helemaal weglaten? We hebben nu de 1e optie. Dat is zelfdocumenterend
// En wat bij een leeg (include) array? Dan kun je ook nog occupations:[] krijgen
if (field.foreign || field.foreignsql)
{
if (field.foreignsql)
{
var name = oRs(model.aliasprefix + field._foreignname).Value;
if (name != null && typeof name == "date" )
name = new Date(name);
}
else
{
var name = field.foreign(val);
}
val = { id: val };
val.name = name;
}
// if (field.typ == 'check' && !field.LOV)
// field.LOV = "0;" + L("lcl_No") + ";1;" + L("lcl_Yes");
if (field.LOV)
{
var spl = api2.splitLOV(field.LOV);
val = { id: val, name: spl[val] };
}
if (field.flexmodule)
{
var flexparam = flexProps(field.flexmodule, -1, null, null,
{ getFile: val, api2name: null });
if (flexparam.files.length)
{
var file = flexparam.files[0];
val = { name: file.name,
date: file.date,
size: file.size,
content_url: file.deepurl,
digest: file.digest };
}
}
return val;
},
filterLOV: function _splitLOV(LOV, filter)
{
filter = filter.split(",");
var spl = api2.splitLOV(LOV);
var result = [];
for (var f in filter)
result.push(filter[f] + ";" + spl[filter[f]]);
return result.join(";");
},
splitLOV: function _splitLOV(LOV, nameprefix)
{
var splits = LOV.split(";");
if (splits % 2)
abort_with_warning("LOV '{0}' mismatch".format(LOV));
var result = {};
for (var i=0; i < splits.length / 2; i++)
{
var name = splits[i*2];
if (nameprefix)
name = nameprefix + name;
result[name] = splits[i*2 + 1];
}
return result;
},
splitLOV2sql: function _splitLOV(LOV)
{
var spl = api2.splitLOV(LOV);
var dual = [];
var cnt = 0;
for (var l in spl)
{
cnt ++;
dual.push("SELECT " + safe.quoted_sql(l) + ", " + safe.quoted_sql(spl[l]) + ", " + cnt + " FROM DUAL");
}
return dual.join(" UNION ALL ") + " ORDER BY 3";
},
splitLOV2select: function _splitLOV(LOV, current, params)
{
params = params || {};
var spl = api2.splitLOV(LOV);
var options = [];
for (var l in spl)
options.push("<option value='" + safe.htmlattr(l) + "'" + (l == current?" selected='1'":"") + ">" + safe.html(spl[l]) + "</option>");
return "<select" + (params.selclass?" class=" + params.selclass:"") + ">" + options.join("") + "</select>";
},
// ['xxx','yyy','zzz'] ==> "0;xxx;1;yyy;2;zzz"
makeLOV: function _makeLOV(arr)
{
var LOVarr = [];
for (var i = 0; i < arr.length; i++)
{
LOVarr.push(String(i));
LOVarr.push(arr[i]);
}
return LOVarr.join(";");
},
getTimetable: function()
{
var timetable = [];
for (var t = 0; t < 24*4-1; t++)
{ // "0;0:00;0.25;0:15;0.5;0:30;0.75;0:45;" enzovoorts
timetable.push(t / 4);
timetable.push(String(Math.floor(t / 4)) + ":" + padout(15*(t % 4)));
}
return timetable.join(";");
},
// Geef alle velden van een record terug
// ?? Alleen bij includes in gebruik ??
sql2jsonfields: function _sql2jsonfields(params, oRs, model)
{
var record = {};
for (var fld in model.fields)
{
if (fld.substring(0,1) == "_")
continue;
var field = model.fields[fld];
if (field.hidden)
continue;
var val = api2.sql2jsonval(oRs, fld, model);
//waarom was dit aanwezig?
//if (field.readonly && !val.id)
// continue;
record[fld] = val;
}
if ("post_get" in model)
model.post_get(params, record);
return record;
},
sql2json: function _sql2json(params, sql, model)
{
var prefuncdata;
var prefuncdatainitialized = false;
var prefilterfuncdata;
if (model.filter && model.filter.prefunc)
prefilterfuncdata = model.filter.prefunc(params);
var data = [];
var oRs = Oracle.Execute(sql, params.errmsg);
if (params.errmsg && oRs.friendlyMsg)
{
var record = {};
record[model.list.columns[0]] = oRs.friendlyMsg;
data.push(record);
return data;
}
var lastkey = 0;
var record = {};
// Merk op dat onze recordset meer regels kan bevatten dan je zou verwachten
// omdat de includes er bij zijn gejoind
var total_count = 0;
var limit = S("qp_maxrows"); // TODO: maximaliseren op qp_maxrows2?
if ("limit" in params.filter)
limit = params.filter.limit;
if (params.filter.nolimit || limit == -1)
limit = 99999999; // Excel/CSV alles tonen
while (!oRs.Eof)
{
if (data.length >= limit) // Alleen nog maar tellen
{
if (model.primary)
{
var key = oRs(model.primary).Value; // Oppassen met de includes
if (key != lastkey)
total_count ++;
lastkey = key;
}
else
{
total_count ++;
}
oRs.MoveNext();
continue;
}
if (model.primary)
{
var key = oRs(model.primary).Value;
if (key != lastkey)
{
if (key < lastkey && params.include && params.include.length && !params.include[0] == 'disc_params') // disc_params is toch one-on-one
{
abort_with_warning("Include '{0}' only allowed when main record is ordered by '{1}'".format(params.include[0], model.primary));
}
if (lastkey && !isEmptyObject(record)) // Record was er mogelijk uit gefilterd
{
data.push(record);
total_count ++;
if (data.length == limit)
continue; // alleen nog tellen, 'volgende' record niet meer opbouwen
}
var record = {};
}
}
// Complexe filtering die we niet voor elkaar kregen met een WHERE-clause
if (model.filter && model.filter.func)
{
if (!model.filter.func(oRs, params, prefilterfuncdata))
{
lastkey = key;
oRs.MoveNext();
continue;
}
}
var fld;
for (fld in model.fields)
{
if (fld.substring(0,1) == "_")
continue;
var field = model.fields[fld];
if (field.hidden)
continue;
if ("fields" in params.filter && !inArray(fld, params.filter.fields.split(",")))
continue;
var val = api2.sql2jsonval(oRs, fld, model)
//waarom was dit aanwezig?
//if (field.readonly && !val.id)
// continue;
record[fld] = val;
}
if (params.include && model.includes)
{
for (var i in params.include) // welke includes worden opgevraagd?
{
var incname = params.include[i];
if (incname == "lastchange") // special case
continue;
if (incname in model.includes) // Ondersteunen we deze include?
{
if (!(incname in record))
record[incname] = [];
var incmodel = model.includes[incname].model;
if (incmodel)
{ // Standaard include via model. Ons 'hoofd' record zal meerdere keren
// uit de query komen met telkens een ander 'include' record
incmodel.aliasprefix = incmodel.aliasprefix || "";
var inc_key = oRs(incmodel.aliasprefix + incmodel.primary).value;
if (inc_key == null) // Geen record door outer join
continue;
// Nu kan het zo zijn dat ons include record al eerder is toegevoegd
// Kan gebeuren bij res_ruimte als je een include van res_alg_ruimte *en* opstelling doet
var incfound = false;
for (var i = 0; i < record[incname].length && !incfound; i++)
{
if (record[incname][i].id == inc_key)
incfound = true;
}
if (!incfound)
record[incname].push(api2.sql2jsonfields(params, oRs, incmodel));
}
else if (model.includes[incname].func) // include via callback functie zoals reservablerooms/occupation
{
if (model.includes[incname].prefunc && !prefuncdatainitialized)
{
prefuncdata = model.includes[incname].prefunc(params);
prefuncdatainitialized = true;
}
var incdata = model.includes[incname].func(key, params, oRs, record, prefuncdata);
if (incdata !== null)
record[incname] = incdata; // de callback geeft de gehele include in een keer
}
}
else
__Log("Unknown include '{0}' requested".format(incname));
}
}
lastkey = key;
if (!model.primary)
{
data.push(record);
record = {};
total_count ++;
}
oRs.MoveNext();
}
if (lastkey && data.length < limit)
{
total_count ++;
data.push(record);
}
if ("merged_model" in model) // de disc_params truc
{
model.merged_model.limit = limit;
//model.offset = offset;
model.merged_model.total_count = total_count;
}
model.limit = limit;
//model.offset = offset;
model.total_count = total_count;
return data;
},
error: function (code, msg)
{
abort_with_warning(msg, code);
},
find_fieldindex_by_dbsname: function(array, value)
{
for(var i = 0; i < array.length; i++)
{
if(array[i].hasOwnProperty("dbs") && array[i]["dbs"] === value)
{
return i;
}
}
return -1;
},
// Pas op: fields is hier alleen de fields die bijgewerkt moeten worden
field_alter: function(fields, fld, newval, model)
{
if (fld in fields)
{
fields[fld].val = newval;
}
else
{
// Als veld niet aanwezig is, dan toevoegen.
// Het typ halen we uit het api-model.
var newfield = {};
newfield.dbs = model[fld].dbs;
newfield.typ = model[fld].typ;
if (model[fld].track)
newfield.fld = model[fld].label||model[fld].track;
newfield.val = newval;
__Log(newfield);
fields[fld] = newfield;
}
},
// Maak van inkomende data een standaard formaat
cleanup_data: function(model, jsondata)
{
// foreigns's leveren we op als issuetype: { id: 123, name: "de standaardmelding") }
// Zo mag je het met een PUT en POST ook opgeven maar we ondersteunen/prefereren ook rechtstreeks issuetype: 123
// Onderstaande functie converteert inkomende data naar het simpele formaat
for (var fld in model.fields)
{
if (model.fields[fld].foreign)
{
if (fld in jsondata && typeof jsondata[fld] == "object" && jsondata[fld] && "id" in jsondata[fld])
jsondata[fld] = jsondata[fld].id;
}
}
},
get_jdata_refkey: function(jsondataobject)
{
var value = null;
if (jsondataobject)
{
if (jsondataobject.id && typeof jsondataobject.id != "object")
value = jsondataobject.id;
else
value = jsondataobject;
}
return value;
},
hasTime: function (dt)
{
return dt.getHours() > 0 ||
dt.getMinutes() > 0 ||
dt.getSeconds() > 0
},
toDate: function (dt)
{
if (dt instanceof Array)
MULTI_DATE_NOT_SUPPORTED;
if (dt instanceof Date)
return dt;
// LET OP: Een (new Date) gemaakt binnen een plugin is vreemd genoeg geen (instanceof Date)
// Waarschijnlijk gebruikt een wsc een ander Date object als (ASP)JScript?
if (dt !== null && typeof dt == "object")
{
dt = new Date(dt);
if (dt instanceof Date)
return dt;
}
if (typeof dt == 'string')
{
if (dt.match(/^[0-9]+$/))
return new Date(parseInt(dt, 10));
var dt = myJSON.internal_parsedate(null, dt);
if (dt && dt instanceof Date)
return dt;
}
NOT_A_DATE;
},
generic_REST: function(model, gparams)
{
__Log("Creating generic_REST for model " + model.records_name);
gparams = gparams || {};
if (typeof model == "function") // Nieuwe stijl is het een function. Even compatible.
DIT_KLOPT_NIET;
// autfunction === false kun je het expliciet uitschakelen
if (model.autfunction !== false && !model.autfunction)
GENERIC_ONLY_WITH_AUTFUNCTION;
model.REST_GET = generic_REST_GET(model, gparams);
if (model.primary)
{
model.REST_POST = generic_REST_POST(model, gparams);
model.REST_PUT = generic_REST_PUT(model, gparams);
model.REST_DELETE = generic_REST_DELETE(model, gparams);
}
},
GET: function _GET(model, key, params) // Haal <20><>n enkel record op of null als niet gevonden
{ // Key is optioneel, dan meot je zelf in params maar goed filteren
if (typeof key == "object")
{
params = key;
key = null;
}
else
{
params = params || {};
params.filter = params.filter || {};
params.filter.id = key;
}
var xxx_array = model.REST_GET(params);
if (!xxx_array.length)
return null;
return xxx_array[0];
},
POST: function _POST(model, jsondata, parent_key) // Maak <20><>n enkel record aan
{ // Key is optioneel, dan meot je zelf in params maar goed filteren
var params = {};
return model.REST_POST(params, jsondata, parent_key);
},
merge_disc_params_model: function(deze, template_model)
{
template_model.merged_model = deze; // hebben we nodig om total_count te kunnen zetten
for (var x in template_model)
deze[x] = template_model[x];
deze.fields = {}; // Die krijgt een deepclone
for (var fld in template_model.fields)
deze.fields[fld] = template_model.fields[fld];
for (var fld in deze.disc_params.model.fields)
{
if (fld != "id")
deze.fields[fld] = deze.disc_params.model.fields[fld];
}
}
}
function generic_REST_GET(model, gparams)
{
gparams = gparams || {};
gparams.GET = gparams.GET || {};
model.gparams = gparams;
return function _generic_REST_GET(params, jsondata)
{
if (model.autfunction !== false)
user.checkAutorisation(model.autfunction); // Als je zelfs geen readrechten hebt ben je heel fout bezig
if ("disc_params" in model)
{
// We zouden net als bij de PUT etc. een rechtstreekse call op de eigen REST_GET kunnen doen
// maar dat werkt niet (zo soepeltjes) voor meerdere records
model.includes = model.includes || {};
model.includes["disc_params"] = model.disc_params;
params.include = params.include || [];
params.include.push("disc_params"); // Altijd
}
var query = api2.sqlfields(params, model);
var wheres = api2.sqlfilter(params, model);
query.wheres = query.wheres.concat(wheres);
if ("disc_params" in model) // Daar mag je ook op filteren
{
if (params.filter && "id" in params.filter)
delete params.filter.id; // anders wordt de disc_params_key onterecht gefilterd.
var wheres = api2.sqlfilter(params, model.disc_params.model);
query.wheres = query.wheres.concat(wheres);
}
if (gparams.GET.tables)
query.tables = query.tables.concat(gparams.GET.tables);
if (gparams.GET.wheres)
query.wheres = query.wheres.concat(gparams.GET.wheres);
if (model.soft_delete && params.filter.show_deleted != "on")
query.wheres.push(model.soft_delete + " IS NULL");
if (model.soft_expire && params.filter.show_expired != "on" && !params.filter.id)
query.wheres.push("(" + model.soft_expire + " IS NULL OR " + model.soft_expire + " > SYSDATE)");
var sql = "SELECT " + query.selects.join(", ")
+ " FROM " + query.tables.join(", ")
+ (query.wheres.length ? " WHERE " + query.wheres.join(" AND " ) : "");
if (query.withs)
{
for (var w in query.withs)
sql = " WITH " + w + " AS " + query.withs[w] + sql;
}
var orderbys = ((params.orderby && params.orderby.length > 0) ? [] : gparams.GET.orderbys || []);
if (params.orderby)
{
for (var i = 0; i < params.orderby.length; i++)
{
var field = model.fields[params.orderby[i]];
if (field.sql)
orderbys.push(field.sql);
else
orderbys.push(field._foreignname || field.dbs);
}
}
if (!params.include && params.columns && params.columns.length > 1) // Sorteren volgens 'columns'
{
for (var i = 0; i < params.columns.length; i++)
{
if (params.columns[i] != "id")// Sla id-veld over bij sorteren
{
var field = model.fields[params.columns[i]];
orderbys.push(model.fields[params.columns[i]]._foreignname || model.fields[params.columns[i]].dbs);
}
}
}
if (!orderbys.length)
{
if (model.fields && "name" in model.fields)
orderbys.push(model.fields.name.dbs);
else if (model.fields && "id" in model.fields)
orderbys.push(model.fields.id.dbs);
else
orderbys.push(1); // FAC_VERSION heeft niets van dit alles
}
sql += " ORDER BY " + orderbys.join(", "); // TODO: Altijd goed met includes?
// Probeer ook nog de 1e include te sorteren. Fraaier met subframes in scaffolding
if (params.include && params.include.length && model.includes && params.include[0] in model.includes)
{
var inc0 = model.includes[params.include[0]].model;
if (inc0.fields["sequence"])
sql += ", " + inc0.table + "." + inc0.fields["sequence"].dbs;
if (inc0.fields["name"])
sql += ", " + inc0.table + "." + inc0.fields["name"].dbs;
if (inc0.fields["id"])
sql += ", " + inc0.table + "." + inc0.fields["id"].dbs;
}
var maxcnt = params.filter.showall==1?S("qp_maxrows2"):S("qp_maxrows");
if (params.include && params.include.length)
{
// geen beperking
// We proberen te voorkomen dat we onnodig veel rijen ophalen.
// Als er echter includes zijn krijgen we veel meer records dan je direct zou verwachten en
// klopt de beperking op basis van ROWNUM geheel niet. Met gepaste tegenzin bij includes
// dan maar (veel) te veel records ophalen.
}
else
{
if (!params.filter.nolimit) // Dan niet (Excel, CSV)
var sql = "SELECT * FROM (" + sql + ") WHERE ROWNUM <= " + (maxcnt + 1); // Eentje extra om overflow-melding te triggeren in resultsettable
}
if (model.fields)
var json = api2.sql2json (params, sql, model);
else
json = {};
if ("disc_params" in model)
{
// include van disc_params data niveau 'hoger' zetten
for (var i = 0; i < json.length; i ++)
{
for (var fld in json[i].disc_params[0])
{
if (fld != "id")
json[i][fld] = json[i].disc_params[0][fld];
}
delete json[i].disc_params;
}
}
return json;
}
}
function generic_REST_POST(model, gparams)
{
gparams = gparams || {};
if (!model.fields["id"].seq) // We willen per se een sequence
return false;
return function _generic_REST_POST(params, jsondata, parent_key)
{
params.isNew = true; // negeer eventuele bestaande keys
if (model.autfunction !== false)
{
var autparams = user.checkAutorisation(model.autfunction);
user.auth_required_or_abort(autparams.PRSwritelevel < 9 && autparams.ALGwritelevel < 9);
}
if ("disc_params" in model)
{
// disc_params velden er uit trekken naar eigen json object
var module = model.fields.ins_discipline_module || "";
var jsondata_dp = { };
for (var fld in jsondata)
{
if (fld in model.disc_params.model.fields && !(fld in model.fields))
{
jsondata_dp[fld] = jsondata[fld];
delete jsondata[fld];
}
}
}
for (var fld in model.fields)
{
if (!(fld in jsondata) && model.fields[fld].required)
jsondata[fld] = model.fields[fld].defaultvalue; // hopen dat die is gezet
}
var dbfields = api2.update_fields(params, model, jsondata);
dbfields["id"] = model.fields["id"]; // Die zal een seq hebben
var xxxIns = buildInsert(model.table, dbfields, { noValidateToken: true });
var the_key = xxxIns.sequences[model.fields.id.dbs];
// TODO: Generieke tracking?
var err = Oracle.Execute(xxxIns.sql, true);
if (err.friendlyMsg)
abort_with_warning(err.friendlyMsg);
var inctrack = api2.process_includes(params, model, jsondata, the_key);
if ("disc_params" in model)
{
// Nu de one-on-one tabel. De eigen sequence daarvan interesseert ons niet
jsondata_dp[model.disc_params.joinfield] = the_key;
var dbfields = api2.update_fields(params, model.disc_params.model, jsondata_dp);
var xxxIns = buildInsert(model.disc_params.model.table, dbfields, { noValidateToken: true });
var err = Oracle.Execute(xxxIns.sql, true);
if (err.friendlyMsg)
{
var sql = "DELETE FROM " + model.table + " WHERE " + model.fields.id.dbs + " = " + the_key;
Oracle.Execute(sql);
abort_with_warning(err.friendlyMsg);
}
}
return { key: the_key, warning: "" };
}
}
function generic_REST_PUT(model, gparams)
{
gparams = gparams || {};
return function _generic_REST_PUT(params, jsondata, the_key)
{
if (model.autfunction !== false)
{
var autparams = user.checkAutorisation(model.autfunction);
user.auth_required_or_abort(autparams.PRSwritelevel < 9 && autparams.ALGwritelevel < 9);
}
if ("disc_params" in model)
{
// disc_params velden er uit trekken naar eigen json object
var jsondata_dp = { };
for (var fld in jsondata)
{
if (fld in model.disc_params.model.fields && !(fld in model.fields))
{
jsondata_dp[fld] = jsondata[fld];
// delete jsondata[fld]; neen, dan gaat multi-save fout
}
}
model.disc_params.model.fields[model.disc_params.joinfield].readonly = true; // Voor de zekerheid
jsondata_dp[model.disc_params.joinfield] = the_key;
}
var dbfields = api2.update_fields(params, model, jsondata);
var wheres = [model.fields["id"].dbs + " = " + the_key];
var xxxUpd = buildTrackingUpdate(model.table, wheres.join(" AND " ), dbfields, { noValidateToken: true });
// TODO: Generieke tracking?
if (xxxUpd) // Misschien geen velden opgegeven.
{
var err = Oracle.Execute(xxxUpd.sql, true);
if (err.friendlyMsg)
abort_with_warning(err.friendlyMsg);
if (model.trackcode && xxxUpd.trackarray.length > 0)
shared.trackaction(model.trackcode, the_key, L("lcl_updated") + "\n" + xxxUpd.trackarray.join("\n"));
}
var inctrack = api2.process_includes(params, model, jsondata, the_key);
if ("disc_params" in model)
{
// Nu de one-on-one tabel
var dbfields = api2.update_fields(params, model.disc_params.model, jsondata_dp);
var wheres = [model.disc_params.model.fields[model.disc_params.joinfield].dbs + " = " + the_key];
var xxxUpd = buildTrackingUpdate(model.disc_params.model.table, wheres.join(" AND " ), dbfields, { noValidateToken: true });
if (xxxUpd) // Misschien geen velden opgegeven.
{
var err = Oracle.Execute(xxxUpd.sql, true);
if (err.friendlyMsg)
abort_with_warning(err.friendlyMsg);
}
}
return { key: the_key, warning: "" };
}
}
function generic_REST_DELETE(model, gparams)
{
gparams = gparams || {};
return function _generic_REST_DELETE(params, the_key)
{
if (model.autfunction !== false)
{
var autparams = user.checkAutorisation(model.autfunction);
user.auth_required_or_abort(autparams.PRSwritelevel < 9 && autparams.ALGwritelevel < 9);
}
if (model.soft_delete)
{
var sql = "UPDATE " + model.table
+ " SET " + model.soft_delete + " = SYSDATE"
+ " WHERE " + model.fields.id.dbs + " = " + the_key
+ " AND " + model.soft_delete + " IS NULL";
}
else
{
var sql = "DELETE " + model.table
+ " WHERE " + model.fields.id.dbs + " = " + the_key;
}
var err = Oracle.Execute(sql, true);
if (err.friendlyMsg)
abort_with_warning(err.friendlyMsg);
return { key: the_key, warning: "" };
}
}
// Algemene normalizing
function def(hash, fld, val)
{
if (!(fld in hash))
hash[fld] = val;
}
function isEmptyObject ( obj )
{
var name;
for ( name in obj ) {
return false;
}
return true;
}
%>