<% /* $Revision$ $Id$ File: api2_rest.inc Description: Functies voor de http-REST interface op de modellen Notes: Hier wordt van alles met de 'buitenwereld' gecommuniceerd. Het manipuleren met behulp van modellen gebeurt in api2.inc Status: */ %> <% var DEZE = this; api2_rest = { authenticate: function _authenticate() { var method = String(Request.ServerVariables("REQUEST_METHOD")); if (method != "GET" && Session("stateless") != 1) // Vereis dan wel het CSRF token { var token = Request.ServerVariables("HTTP_X_CSRF_TOKEN").Count // Meegegeven als X-CSRF-TOKEN ? String(Request.ServerVariables("HTTP_X_CSRF_TOKEN")) : ""; protectRequest.validateToken(token); } if (user_key < 0) { Response.Status = "401 Unauthorized"; // Sommige applicaties kunnen in reactie hierop een b64 encoded username:password sturen // Die onderscheppen wij in LoginTry.asp uiteindelijk if (S("basic_auth_realm")) Response.AddHeader("WWW-Authenticate", "Basic realm=\"" + S("basic_auth_realm") + "\""); Response.End; if (typeof NO_ADDHEADER == "undefined" && Request.Servervariables("HTTP_FCLT_VERSION").Count > 0) { // wordt opgepikt door FCLTAPI.DLL voor in de logging en daarna gestript. Niet in Fiddler dus Response.AddHeader ("FCLT_USERID", customerId + "\\" + String(user_key)); } oRs.Close(); } // APP? Die kan meta-rechten hebben (bijvoorbeeld auth-token opvragen van gebruiker) var APPKEY; if (S("fac_api_key_in_url")) APPKEY = getQParam("HTTP_X_FACILITOR_APP_KEY", ""); if (!APPKEY && Request.ServerVariables("HTTP_X_FACILITOR_APP_KEY").Count) APPKEY = String(Request.ServerVariables("HTTP_X_FACILITOR_APP_KEY")); // Meegegeven als X-FACILITOR-APP-Key if (APPKEY) { var sql = "SELECT prs_perslid_key, prs_perslid_naam" + " FROM prs_perslid" + " WHERE prs_perslid_verwijder IS NULL" + " AND prs_perslid_apikey = " + safe.quoted_sql(APPKEY); var oRs = Oracle.Execute(sql); if (oRs.Eof) { __DoLog("Unauthorized app"); Response.Status = "401 Unauthorized"; Response.End; }; __Log("APP User is: " + oRs("prs_perslid_naam").Value); /* global */ app_user_key = oRs("prs_perslid_key").Value; /* global */ app_user = new Perslid(app_user_key); oRs.Close() } CheckForLogging(Request.QueryString("logging")); // Nu pas kan autorisatie via user gecontroleerd worden }, impersonate: function _impersonate(model) { // Impersonate? (anno jan-2016 in de praktijk nergens gebruikt, kan mogelijk vervallen) if (!S("fac_api_allow_impersonate") || !model.impersonate_auth) return; var IMPERS; if (S("fac_api_key_in_url")) IMPERS = getQParam("SWITCHUSER", ""); if (!IMPERS && Request.ServerVariables("HTTP_X_FACILITOR_SWITCH_USER").Count) IMPERS = String(Request.ServerVariables("HTTP_X_FACILITOR_SWITCH_USER")); // Meegegeven als X-FACILITOR-SWITCH-USER if (!IMPERS) return; var sql = "SELECT prs_perslid_key, prs_perslid_naam" + " FROM prs_perslid" + " WHERE prs_perslid_verwijder IS NULL" + " AND prs_perslid_oslogin = " + safe.quoted_sql_upper(IMPERS); var oRs = Oracle.Execute(sql); if (oRs.Eof) { Response.Status = "412 Invalid X-Facilitor-Switch-User header"; Response.End; }; __Log("IMPERS User is: " + oRs("prs_perslid_naam").Value); var other_user_key = oRs("prs_perslid_key").Value; oRs.Close(); var xfunc = user.func_enabled2(model.module, { prs_key: other_user_key, isOptional: true }); var can = (xfunc && xfunc.canRead(model.impersonate_auth)); if (can) { /* global */ user_key = other_user_key; /* global */ user = new Perslid(user_key); } else { Response.Status = "412 Unauthorized X-Facilitor-Switch-User header"; Response.End; } }, process: function _process(model) { var wasCodePage = Session.Codepage; Session.Codepage = 65001; // We doen *uitsluitend* utf-8 Response.Charset = 'utf-8'; var inputformat = outputformat = getQParamSafe("format", "invalid").toLowerCase(); if (outputformat == "auto") { var accept = String(Request.ServerVariables("HTTP_ACCEPT")).split(",")[0]; // Altijd alleen eerste bekijken switch (accept.toLowerCase()) { case "application/xml": outputformat = "xml"; break; case "application/json": outputformat = "json"; break; case "text/html": outputformat = "html"; break; // vanuit browser? default: outputformat = "json"; } } if (outputformat == "json") /* global */ JSON_Result = true; // Zelf doen we er niets mee maar // shared.simple_page kijkt er naar var orgError = api2.error; api2.error = function (code, msg) { api2_rest.plugin.error_handler(code, msg, orgError); } api2_rest.authenticate(); // Kip-ei: de omzetting naar new model() mag pas als je geauthenticeerd bent // Hierboven willen we het echter al wel meegeven if (typeof model == "function") // Nieuwe stijl is het een function. Even compatible. model = new model(); api2_rest.impersonate(model); var method = String(Request.ServerVariables("REQUEST_METHOD")); if (!/GET|PUT|POST|DELETE/.test(method)) // Overigens houdt IIS deze al eerder tegen { Response.Status = "405 Method not allowed"; Response.End; } var key = getQParamInt("id", -1); // Voor POST/PUT/DELETE if (/PUT|DELETE/.test(method) && !(key > 0)) api2.error(400, "Missing id"); var jsondata = {}; var filter = shared.qs2json(model); filter = api2_rest.plugin.transform_filter(filter); var requestparams = { filter: filter, include: getQParamArray("include", []) }; if (filter.mode == "attachment" && "custom_fields" in model.includes) { if (key > 0 && method == "POST") // het bestand mag dan wel nieuw zijn (POST is van toepassing), method = "PUT"; // intern is het een update van een bestaand record, dus PUT } if (!("REST_" + method in model) || !model["REST_" + method]) { Response.Status = "501 Not Implemented"; // TODO The response MUST include an Allow header containing a list of valid methods for the requested resource. Response.End; } if (/PUT|POST/.test(method)) // Dan is er in de body data meegestuurd { if (filter.mode == "attachment" && "custom_fields" in model.includes) { // Body bevat application/octet-stream, dat lezen we later wel uit } else { if (inputformat == "auto") { var contenttype = String(Request.ServerVariables("HTTP_CONTENT_TYPE")).split(",")[0]; // Altijd alleen eerste bekijken switch (contenttype.toLowerCase()) { case "application/xml": inputformat = "xml"; break; case "application/json": inputformat = "json"; break; case "application/x-www-form-urlencoded": inputformat = "form"; break; default: inputformat = outputformat; } } switch (inputformat) { case "json": { var parsed = RequestJSON(); if (parsed.error) api2.error(400, "Error parsing input JSON: " + parsed.error); jsondata = api2_rest.plugin.transform_incoming(requestparams, parsed.json); if (!jsondata) api2.error(400, "Error parsing input JSON: Empty"); break; } case "xml": { var parsed = RequestXML(); if (parsed.error) api2.error(400, "Error parsing input XML: " + parsed.error); jsondata = api2_rest.xml2json(parsed.xml); if (!jsondata) api2.error(400, "Error parsing input XML: Empty"); break; } //case "form": //{ // jsondata = { }; // jsondata[model.recordname] = shared.form2json(model); // of api2.form2JSONdata inzetten? //} default: UNKNOWN_CONTENT_TYPE; } if (!jsondata || !(model.record_name in jsondata || (model.multi_update && model.records_name in jsondata))) { api2.error(400, "No '{0}' found in input".format(model.record_name)); } } } var isSingle = /PUT|POST|DELETE/.test(method) || (key > 0); // PUT, POST en DELETE altijd single if (outputformat == "doc") { // Dan hoeven we verder bijna niets te doen if ("autfunction" in model) model.autfunctionname = L("lcl_" + model.autfunction); // TODO: velden strippen waar je niets mee te maken hebt? } else if (outputformat == "api" && getQParamInt("swagger", 0) == 0) { // TODO: Onderstaande in een of ander standaardformaat opleveren? var result = { id: model.records_name, "name": model.record_title, "names": model.records_title, "records_name": model.records_name, "record_name": model.record_name, "authorization": model.autfunction, methods: [], includes: [], fields: [] }; for (var i in model.includes) result.includes.push(i); if (model["REST_GET"]) result.methods.push("GET"); if (model["REST_PUT"]) result.methods.push("PUT"); if (model["REST_POST"]) result.methods.push("POST"); if (model["REST_DELETE"]) result.methods.push("DELETE"); for (var fld in model.fields) { // TODO: We missen hard-coded filters als reservableequipment/allowedinroom nu nog if (!model.fields.hidden) result.fields.push({ id: fld, filter: model.fields[fld].filter, type: model.fields[fld].typ, label: model.fields[fld].label, required: model.fields[fld].required, valuelist: model.fields[fld].LOV && api2.splitLOV(model.fields[fld].LOV) }); } } else if (outputformat == "api" && getQParamInt("swagger", 0) == 1) { result = swaggermodel(model); } else { if (method == "DELETE") { var result = model["REST_" + method]( requestparams, key ); } else if (filter.mode == "attachment" && "custom_fields" in model.includes) { // GET/PUT/POST https://xxxx.facilitor.nl/api2/visitors/99685/attachments/1040/testfile.jpg" bestaande folder // POST https://xxxx.facilitor.nl/api2/issues/0/attachments/1040/testfile.jpg" nieuw object, maakt TEMP aan if (wasCodePage != 65001) { // Door de IIS rewriter is de filenaam in de url utf-8 encoded // Zet dat hier terug om naar Windows-1252 var fileStream = new ActiveXObject("ADODB.Stream"); fileStream.Open(); fileStream.Type = 2; // adTypeText fileStream.Charset = 'Windows-1252'; fileStream.WriteText(filter.filename); fileStream.Position = 0; fileStream.Charset = 'utf-8'; filter.filename = fileStream.ReadText(); fileStream.Close(); } requestparams.filter.id = key; // Die kan er maar beter wel zijn! requestparams.include = ["custom_fields"]; var jsondata = { "custom_fields": [ { "propertyid": parseInt(filter.subfolder, 10), "value": filter.filename, // Gaat de database in bij "F" "attachments": [ { "name": filter.filename // Zo gaat hij heten op het filesysteem } ] } ] }; if (method == "PUT" || method == "POST") { var bytes = Request.TotalBytes; if (bytes == 0) api2.error(400, "Empty file"); var fileStream = Server.CreateObject("ADODB.Stream"); fileStream.Type = 1; // adTypeBinary eerst nog fileStream.Open(); try { fileStream.Write(Request.BinaryRead(bytes)); } catch(e) { var msg = L("lcl_shared_upload_error_start") + e.description + L("lcl_shared_upload_error_end"); api2.error(400, msg); } jsondata["custom_fields"][0]["attachments"][0].datastream = fileStream; if (key == 0) // new record { var token = model.includes["custom_fields"].model.REST_POST(requestparams, jsondata.custom_fields[0], -1); var record = { xflexparentkey: -1, propertyid: jsondata.custom_fields[0].propertyid}; var fileparams = { getFiles: true, api2name: null, tmpfolder: token }; } else { requestparams.custom_fields_only = true; // Voorkom bij nieuwe bijlage onnodige update vam hele issue model["REST_" + method](requestparams, jsondata, key); // via het hoofdmodel met authorisatie controle en alles var record = { xflexparentkey: key, propertyid: jsondata.custom_fields[0].propertyid}; var fileparams = { getFiles: true, api2name: model.records_name }; } var data = model.includes["custom_fields"].model.get_file_info(requestparams, record, fileparams ); api2_rest.deliver(data, /* dummy model */ { record_name: "attachment" }, outputformat, true); return; } // Geen atachment dus door met de reguliere code var data = model["REST_" + method](requestparams, jsondata, key); if (method == "GET") { if (!data.length) // mogelijk not authorized op hele record Response.Status = "404 Not Found"; else model.includes["custom_fields"].model.streamattachment(filter, data[0].custom_fields); Response.End; } else result = data; } else if (method == "GET") { var result = model["REST_" + method]( requestparams, null, key ); } else if (model.record_name in jsondata) // een enkel record { if (jsondata[model.record_name] instanceof Array) api2.error(400, "{0} should be single record only.".format(method)) var result = model["REST_" + method]( requestparams, jsondata[model.record_name], key ); } else { // Loop door de multiple records en geef de REST_ functie altijd één record for (var record in jsondata[model.records_name]) { var thisdata = jsondata[model.records_name][record]; var result = model["REST_" + method]( requestparams, thisdata, thisdata.id ); } } } switch (method) { case "DELETE": { Response.Status = "204 No Content"; Response.End; break; } case "GET": { data = result; break; } case "PUT": case "POST": { var key = result.key; if (key > 0) { var params = { filter: shared.qs2json(model), include: getQParamArray("include", []) }, jsondata, key // requestparams.include is mogelijk uitgebreid met wat er in de body stond data = model.REST_GET({ filter: { id: key }, include: requestparams.include }); // resulterende data weer terug __Log(data); } else { data = []; isSingle = false; } } } api2_rest.deliver(data, model, outputformat, isSingle); }, // Data is een array met 'records' deliver: function _deliver(data, model, format, single ) { if (single && !data.length) { Response.Status = "404 Not Found"; Response.End; } if (format == "html" || format == "json" || format == "table") { var result = { }; if (model.formatted_get) result = data; else if (single) result[model.record_name] = data[0]; else { result.total_count = model.total_count; result.limit = model.limit; result.offset = model.offset; result[model.records_name] = data; } var resultdata = api2_rest.plugin.transform_outgoing({}, result); } switch (format) { case "api": { //var xml_antwoord = api2_rest.json2xml(data, model, single); var str_antwoord = JSON.stringify(data, null, getQParam("pretty","0")=="1"?2:0); Response.ContentType = "application/json"; break; } case "doc": { if (model.fields) // Ga de hints er bij zoeken { var safefieldnames = []; var lcl2fld = {}; for (var fld in model.fields) { if (fld.substring(0,1) == "_") { delete model.fields[fld]; continue; } for (var prop in model.fields[fld]) { if (typeof model.fields[fld][prop] == "function") // Bijvoorbeeld model_issues.filter model.fields[fld][prop] = "<>"; } if (model.fields[fld].LOV) model.fields[fld].valuelist = api2.splitLOV(model.fields[fld].LOV, "lov_"); var lclname = "{0}.{1}.hint".format(model.records_name, fld); lcl2fld[lclname] = fld; safefieldnames.push(safe.quoted_sql(lclname)); } if (safefieldnames.length) { //model.fields[fld].hint = "Hallo"; var sql = "SELECT fac_locale_xsl_label, " + " COALESCE(fac_locale_xsl_cust, fac_locale_xsl_tekst) fac_locale_xsl_tekst" + " FROM fac_locale_xsl xsl" + " WHERE fac_locale_xsl_lang = " + safe.quoted_sql(user_lang) + " AND fac_locale_xsl_module = 'ASP'" + " AND fac_locale_xsl_label IN (" + safefieldnames.join(", ") + ")"; var oRs = Oracle.Execute(sql); while (!oRs.Eof) { model.fields(lcl2fld[oRs("fac_locale_xsl_label").Value]) = oRs("fac_locale_xsl_tekst").value; oRs.MoveNext(); } oRs.Close(); } } Response.ContentType = "text/html"; var str_antwoord = simple_json2xml(model, "api"); var xml_antwoord = new ActiveXObject("MSXML2.DOMDocument.6.0"); xml_antwoord.loadXML(str_antwoord); if (xml_antwoord.parseError.errorCode) { abort_with_warning("XSL error: " + xml_antwoord.parseError.reason + " @ " + xml_antwoord.parseError.line + "." + xml_antwoord.parseError.linepos + "\n"+ xml_antwoord.parseError.srcText); } var style = new ActiveXObject("MSXML2.DOMDocument.6.0"); style.async = false; style.resolveExternals = true; // XSL kan includes hebben style.validateOnParse = true; // en moet correct zijn if (Request.QueryString("debug").Count == 0) { var xslname = model.xslname || "reference.xsl"; style.load(Server.MapPath(rooturl + "/appl/api2/" + xslname)); // De stylesheet laden. API's redeneren vanuit de root var str_antwoord = xml_antwoord.transformNode(style); // terugstoppen in antwoord } else { // Het kan zijn dat de stylesheet bepaalde informatie verbergt. // Daarom niet zo maar aanbieden if (Application("otap_environment") != "O") ONLY_ON_OTAP_O; style.load(Server.MapPath(rooturl + "/appl/shared/indent.xsl")); // De stylesheet laden. API's redeneren vanuit de root var str_antwoord = "
" + Server.HTMLEncode(xml_antwoord.transformNode(style)) + "
"; } if (style.parseError.errorCode) { abort_with_warning("XSL error: " + style.parseError.reason + " @ " + style.parseError.line + "." + style.parseError.linepos ); } break; } case "json": var str_antwoord = JSON.stringify(resultdata, null, getQParam("pretty","0")=="1"?2:0); var jsonp = getQParam("jsonp", getQParam("callback","")); if (jsonp) { str_antwoord = jsonp + "(" + str_antwoord + ")"; Response.ContentType = "application/javascript"; } else Response.ContentType = "application/json"; break; case "html": Response.ContentType = "text/html"; var antwoord = JSON.stringify(resultdata, null, 2); var str_antwoord = "" + "" + "" + "{0}".format(single?model.record_name:model.records_name) + "" + "
"
                                 +      Server.HTMLEncode(antwoord)
                                 + "
"; break; case "table": Response.ContentType = "text/html"; if (single) var antwoord = api2_rest.json2htmltable([resultdata[model.record_name]], model, single); else var antwoord = api2_rest.json2htmltable(resultdata[model.records_name], model, single); var str_antwoord = "" + "" + "" + " {0}".format(single?model.record_name:model.records_name) + " " + "" + "" + antwoord + "" + ""; break; case "xml": Response.ContentType = "text/xml"; var xml_antwoord = api2_rest.json2xml(data, model, single); // TODO: Output XSL transform ondersteunen? var xsl = getQParamSafe("xsl", ""); var xslfile; if (xsl) { var fso = new ActiveXObject("Scripting.FileSystemObject"); xslfile = Server.MapPath(custpath + "/xsl/" + xsl + ".xsl"); if (!fso.FileExists(xslfile)) abort_with_warning("Stylesheet '{0}' not found".format(xsl)); } else if (getQParam("pretty","0")=="1") xslfile = Server.MapPath(rooturl + "/appl/shared/indent.xsl"); if (xslfile) { var style = new ActiveXObject("MSXML2.DOMDocument.6.0"); style.async = false; style.resolveExternals = false; style.load(xslfile); if (style.parseError.errorCode) { abort_with_warning("XSL error: " + style.parseError.reason + " @ " + style.parseError.line + "." + style.parseError.linepos ); } var str_antwoord = xml_antwoord.transformNode(style); // terugstoppen in antwoord } else var str_antwoord = xml_antwoord.xml; //Response.ContentType = "application/json"; //var str_antwoord = JSON.stringify(api2_rest.xml2json(xml_antwoord), null, 2);; break; default: WRONG_FORMAT; } // str_antwoord heeft nu het te versturen antwoord // Bepaal eTag var eTag = api2_rest.plugin.get_eTag({}, resultdata); if (!eTag) { var oCrypto = new ActiveXObject("SLNKDWF.Crypto"); var eTag = '"' + oCrypto.hex_sha1(String(S("cache_changecounter")) + "_" + str_antwoord).toLowerCase() + '"'; } Response.AddHeader("ETag", eTag); if (Request.ServerVariables("HTTP_IF_NONE_MATCH") == eTag) { // We hebben een match! Effectief besparen wel alleen op dataverkeer, de queries zijn al geweest Response.Clear(); Response.Status = "304 Not modified"; Response.End; } Response.write(str_antwoord); }, json2htmltable: function _json2htmltable(data, model, single) { var trs = []; if (data.length) // Header maken { var tds = []; for (var fld in data[0]) { var lbl = fld; if (fld in model.fields) lbl = model.fields[fld].label; else { var val = data[0][fld]; if (val && typeof val == "object") { if (model.includes && fld in model.includes) var lbl = model.includes[fld].model.records_title; else lbl = fld; // Attachments/flexfiles } } tds.push("{1}".format(safe.htmlattr(lbl), fld)); } trs.push(tds.join("")); } for (var i = 0; i < data.length; i++) { var rec = data[i]; var tds = []; for (var fld in rec) { var val = rec[fld]; var safeval = Server.HTMLEncode(String(val)); if (val === null) safeval = ' '; else if (typeof val == 'object' && val instanceof Date) safeval = toISODateTimeString(val); else if (val && typeof val == "object" && "id" in val) { var naam = val.name||"???"; if (typeof naam == 'object' && naam instanceof Date) // Bij appointment naam = toISODateTimeString(naam); safeval = val.id + " (" + Server.HTMLEncode(naam) + ")"; } else if (val && typeof val == "object" && model.includes && fld in model.includes) safeval = api2_rest.json2htmltable(val, model.includes[fld].model, single); // dereference else if (val instanceof Array) // attachments array safeval = api2_rest.json2htmltable(val, model, single); tds.push(safeval); } trs.push("" + tds.join("") + ""); } return "\n\n" + trs.join("\n") + "
"; }, // TODO: Wanneer attributes gebruiken en wanneer (sub)-elements? // Streven: data == xml2json(json2xml(data)) json2xml: function _json2xml(data, model, single) { var rootname = model.records_name; var record_name = model.record_name; var xmlDoc = new ActiveXObject("MSXML2.DOMDocument.6.0"); xmlDoc.appendChild(xmlDoc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"utf-8\"")); var record2json = function(record, record_name) { var elementRecord = xmlDoc.createElement(record_name); for (var fld in record) { var elementField = xmlDoc.createElement(fld); if (record[fld] instanceof Date) { var elementFieldText = xmlDoc.createTextNode(String(record[fld].toJSON())); elementField.appendChild(elementFieldText); } else if (record[fld] instanceof Array) { var inc_record_name; for (var ii in model.includes) { // if (model.includes[ii].model.records_name == fld) // inc_record_name = model.includes[ii].model.record_name; if (ii == fld) inc_record_name = model.includes[ii].model.record_name; } // Fallback voor attachments array --> "attachment" if (!inc_record_name) inc_record_name = fld.substr(0, fld.length-1); for (var i = 0; i < record[fld].length; i++) elementField.appendChild(record2json(record[fld][i], inc_record_name)); } else if (record[fld] && typeof record[fld] == "object") // Veronderstelt dat dit foreign met name/key is { if ("name" in record[fld] && "id" in record[fld]) { elementField.setAttribute("name", record[fld].name===null?"":record[fld].name); elementField.setAttribute("id", record[fld].id); } else elementField = record2json(record[fld], fld); } else { var elementFieldText = xmlDoc.createTextNode(String(record[fld]||"")); elementField.appendChild(elementFieldText); } elementRecord.appendChild(elementField); } return elementRecord; }; if (single) { xmlDoc.appendChild(record2json(data[0], record_name)); } else { var arrayElement = xmlDoc.createElement(rootname); arrayElement.setAttribute("total_count", model.total_count); arrayElement.setAttribute("limit", model.limit); arrayElement.setAttribute("offset", model.offset); for (var i = 0; i < data.length; i++) arrayElement.appendChild(record2json(data[i], record_name)); xmlDoc.appendChild(arrayElement); } return xmlDoc; }, // Streven: data == json2xml(xml2json(xml)) // http://davidwalsh.name/convert-xml-json maar @attributes er uit gehaald xml2json: function _xml2json(xml) { // Create the return object var obj = {}; if (xml.nodeType == 1) { // element // do attributes if (xml.attributes.length > 0) { // JGL removed: obj["@attributes"] = {}; for (var j = 0; j < xml.attributes.length; j++) { var attribute = xml.attributes.item(j); obj[attribute.nodeName] = attribute.nodeValue; } } } else if (xml.nodeType == 3) { // text obj = xml.nodeValue; } // do children if (xml.hasChildNodes()) { for(var i = 0; i < xml.childNodes.length; i++) { var item = xml.childNodes.item(i); var nodeName = item.nodeName; if (typeof(obj[nodeName]) == "undefined") { // JGL Added: Only one Textnode is simplified. Autodetect data if (item.nodeType == 3 && xml.childNodes.length == 1) { var dt = myJSON.internal_parsedate(null, item.nodeValue); if (dt && dt instanceof Date) return dt; return item.nodeValue; } obj[nodeName] = api2_rest.xml2json(item); } else { if (typeof(obj[nodeName].push) == "undefined") { var old = obj[nodeName]; obj[nodeName] = []; obj[nodeName].push(old); } obj[nodeName].push(api2_rest.xml2json(item)); } } } return obj; }, find_plugin: function() { var plugin_name = getQParamSafe("plugin", "").toLowerCase(); if (!plugin_name) return {}; var fso = new ActiveXObject("Scripting.FileSystemObject"); var paths = ["/cust/" + customerId, "/cust", "/appl/api2"]; // Hieronder zoeken naar '/plugins' folder for (var p in paths) { var ppath = Server.MapPath(rooturl + paths[p] + "/plugins/" + plugin_name + ".wsc") //__Log(ppath); if (fso.FileExists(ppath)) { try { var hook = GetObject("script:" + ppath); } catch(e) { api2.error(500, "Loading {0} failed: {1}".format(ppath, e.description)); } // Via DEZE kan de aanroeper eigenlijk alle globale functies benaderen // zoals __Log hook.initialize({ S: S, Oracle: Oracle, customerId: customerId, safe: safe, DEZE: DEZE }); return hook; } } api2.error(500, "Undefined plugin {0}".format(plugin_name)); }, plugin: { transform_filter: function(filter) { var outdata = filter; var hook = api2_rest.find_plugin(); if ("transform_filter" in hook) { outdata = hook.transform_filter(filter); } hook = null; return outdata; }, error_handler: function(code, msg, orgHandler) { var hook = api2_rest.find_plugin(); if ("error_handler" in hook) outdata = hook.error_handler(code, msg, orgHandler); else outdata = orgHandler(code, msg); hook = null; return outdata; }, transform_incoming: function(params, data) { var outdata = data; var hook = api2_rest.find_plugin(); if ("transform_incoming" in hook) { outdata = hook.transform_incoming(params, data); } hook = null; return outdata; }, transform_outgoing: function(params, data) { var outdata = data; var hook = api2_rest.find_plugin(); if ("transform_outgoing" in hook) outdata = hook.transform_outgoing(params, data); hook = null; return outdata; }, get_eTag: function(params, data) { var outdata = null; var hook = api2_rest.find_plugin(); if ("get_eTag" in hook) outdata = hook.get_eTag(params, data); hook = null; return outdata; } } } // LET OP: Verwacht wordt dat de JSON-code in de body utf-8 encoded is, niet windows-1252! // (in de praktijk moet je *moeite* doen om windows-1252 te krijgen dus dit is handiger) function RequestJSON() { var jvraag; if(Request.TotalBytes > 0) { var lngBytesCount = Request.TotalBytes; jvraag = BytesToStr(Request.BinaryRead(lngBytesCount)); } __Log("Vraag: " + jvraag); try { var vraag = myJSON.parse(jvraag); } catch (e) { result = { error: e.description || (e.message + ": " + e.name) }; __DoLog("JSON eval faalt met: '{0}'\n{1}".format(result.error, jvraag), "ffff00"); return result ; } return { json: vraag }; } function RequestXML() { try { var inputXML = Server.CreateObject("MSXML2.DOMDocument.6.0"); inputXML.load(Request); } catch (e) { return { error: e.description }; } if (inputXML.parseError.errorCode) { return { error: inputXML.parseError.reason + " @ " + inputXML.parseError.line + "." + inputXML.parseError.linepos }; } return { xml: inputXML }; } function BytesToStr(bytes) { var stream = Server.CreateObject("ADODB.Stream"); stream.type = 1; stream.open; stream.write(bytes); stream.position = 0; stream.type = 2; // Text stream.charset = "utf-8"; // Als onverhoopt toch windows-1252 is gestuurd met diacrieten gaf // stream.readtext() een 'The parameter is incorrect' // Vreemd genoeg lijkt het expliciet meegeven van een lengte dat te voorkomen. // Die mag zelfs gewoon veel 'te lang' zijn. // Merk op dat we windows-1252 niet ondersteunen, je moet echt utf-8 sturen // (we hadden waarschijnlijk ook 99999999 kunnen kiezen) var sOut = stream.readtext(stream.Size * 2); stream.close; return sOut; } function getQParamISODate(pName, defVal) { return _get_ParamISODate(Request.Querystring, pName, defVal) } function getFParamISODate(pName, defVal) { return _get_ParamISODate(Request.Form, pName, defVal) } function _get_ParamISODate(pColl, pName, defVal) { var strval = _get_Param(pColl, pName, defVal, true); // force: een lege waarde wordt als afwezig beschouwd if (strval) { var dt = myJSON.internal_parsedate(null, strval); if (dt && dt instanceof Date) return dt; } if (defVal instanceof Date) { return defVal; } if (defVal === null) // bewust triple=== { return null; } // Error message will get to client and/or IIS logfiles eval("INTERNAL_ERROR_PARAMETER_" + pName + "_IS_NOT_ISODATE"); } var simple_json2xml = (function (undefined) { "use strict"; var tag = function (name, closing) { return (closing ? ""; }; return function (obj, rootname) { var xml = ""; for (var i in obj) { if (obj.hasOwnProperty(i)) { var value = obj[i], type = typeof value; if (value === obj) // bij merged_model zouden we hier oneindige recursie krijgen continue; if (value instanceof Array && type == 'object') { for (var sub in value) { xml += simple_json2xml(value[sub]); } } else if (value instanceof Object && type == 'object') { xml += tag(i) + simple_json2xml(value) + tag(i, 1); } else { xml += tag(i) + (value===null?"":Server.HTMLEncode(value)) + tag(i, 1); } } } return rootname ? tag(rootname) + xml + tag(rootname, 1) : xml; }; })(simple_json2xml || {}); %>