<% /* $Revision$ $Id$ File: shared.inc Description: Generieke functies die door elke pagina gebruikt worden. Parameters: Context: Vanuit ELK asp bestand Note: Dus gebruik met mate */ %> <% var shared = { trackaction: function (paction, pkey, poms) /* paction is something like 'BEZMUT', 'MLDAFM' etc */ { // noot: trackaction genereert ook eventuele notificaties var sql = "BEGIN fac.trackaction(" + safe.quoted_sql(paction) + ", " + pkey + ", " + user_key + ", NULL, " + safe.quoted_sql(poms, 2000) + "); END;" Oracle.Execute(sql); if (paction.substr(0,1) != "#") // # voor '#MLDBEH' betekent 'niet notificeren' putorders.sendnotifications(pkey, paction); }, // Keep track of GUI actions, params.daily summarizes per day anonymously registeraction: function (pgroup, params) { var counter_key = -1; var flds = []; var safevals = []; flds.push("fac_gui_counter_group"); safevals.push(safe.quoted_sql(pgroup)); if (!params.daily) { flds.push("prs_perslid_key"); safevals.push(user_key); } if (params.truncdate || params.daily) { flds.push("fac_gui_counter_date"); safevals.push("TRUNC(SYSDATE)"); } if (params.info) { flds.push("fac_gui_counter_info"); safevals.push(safe.quoted_sql(params.info)); } if (params.refkey) { flds.push("fac_gui_counter_refkey"); safevals.push(params.refkey); } if (!params.daily) { var sql = "SELECT faq_s_fac_gui_counter_key.nextval FROM dual"; var oRs = Oracle.Execute( sql ); counter_key = oRs(0).value; oRs.Close(); flds.push("fac_gui_counter_key"); safevals.push(counter_key); sql = "BEGIN" + " INSERT INTO fac_gui_counter ("+flds.join(", ")+")" + " VALUES ("+safevals.join(", ")+");" + " EXCEPTION WHEN dup_val_on_index" // Twee keer in dezelfde seconde negeren + " THEN " + " NULL; " + " END;" Oracle.Execute(sql); } else { // upsert the daily counter, relies on the unique index // you might think that update-insert is more efficient (because more often) than insert-update // JGL: inderdaad, dat is efficienter dus omgedraaid. var wheres = []; for (var i=0; i"); Response.Write(bodyhtml); Response.Write(""); Response.End; }, // Maak een heel simpele pagina. // Als DOCTYPE_Disable dan veronderstellen we een JSON-vraag en sturen we JSON terug // (Anders zou er waarschijnlijk een Ajax Error optreden) simpel_page: function(bodyhtml) { if (typeof mobile == "object" && 'simpel_page' in mobile) mobile.simpel_page(bodyhtml); else if (typeof DOCTYPE_Disable == "undefined" || typeof JSON == "undefined") { Response.Clear(); Response.Write(""); FCLTHeader.Generate(); Response.Write(""); shared.simpel_body(bodyhtml); } else { bodyhtml = bodyhtml.replace(/\/ig, "\n") .replace(/\/ig, "\n") .replace(/\<\/p\>/ig, "\n"); Response.Clear(); Response.Write(JSON.stringify({success: false, message: bodyhtml})); Response.End; } }, internal_error: function (message) { shared.simpel_page(L("lcl_internal_error") + "

" + message + "

"); }, record_not_found: function (html) { shared.simpel_page(L("lcl_recnotfound_error") + (html||"")); }, // b64 versie levert op volgens rfc4648 zodat de waarde url-safe is // geen +/ maar -_ // Let op: len is het aantal gewenste karakters, dat zal groter zijn dan het aantal bytes random: function (len, encoding) { len = len || 32; encoding = encoding || "base64"; switch (encoding) { case "base64": bytes = Math.ceil(len * 6 / 8); break; case "base32": bytes = Math.ceil(len * 5 / 8); break; case "hex": bytes = len * 4 / 8; break; default: BAD_RANDOM_ENCODING; } try { var oCrypto = new ActiveXObject("SLNKDWF.Crypto"); // requires version 4.14 var rnd = oCrypto.hex_random(bytes); switch (encoding) { case "base64": rnd = oCrypto.hex2base64(rnd, false, true); break; // no padding, urlsafe case "base32": rnd = oCrypto.hex2base32(rnd); break; case "hex": rnd = rnd; break; default: BAD_RANDOM_ENCODING; } } catch(e) { // Minstens versie 4.14 voor random abort_with_warning("SLNKDWF.DLL not properly installed or too old version.\n{0}".format(e.description)); } return rnd.substr(0, len); }, isGoodNumber: function(str, intOnly, posOnly, numLen, numDec) { var posOnlyStr = (posOnly ? "" : "\\-?"); var numLenStr = (numLen == -1 ? "" : (numDec == -1 ? numLen : (intOnly ? numLen : numLen - numDec))); var numDecStr = (numDec == -1 ? "" : numDec); var intOnlyStr = "\\d{1," + numLenStr + "}"; var dblOnlyStr = "\\d{0," + numLenStr + "}[.,]\\d{1," + numDecStr + "}" + "|" + "\\d{1," + numLenStr + "}[.,]\\d{0," + numDecStr + "}" ; var anum = "^" + posOnlyStr + "(" + intOnlyStr + (intOnly ? "" : "[.,]?" + "|" + dblOnlyStr) + ")" + "$"; var patt = new RegExp(anum); return patt.test(str); }, // Checken of het eenheden selectveld met dagen/uren bij "Acceptatietijd" niet readonly was en wel is meegegeven. // Als dit eenheden selectieveld readonly was dan wordt deze niet opgeslagen en dan moet de tijdsduur ook niet opgeslagen worden. // Als 0 dagen/uren dan beide velden leeg maken. Als eenheidsveld aanwezig is, moet ook het tijdsduurveld aangwezig zijn. add_time_field: function add_time_field(fields, dbsname, frm_tijd, frm_eenheid, track) { var dayshours = Request.Form(frm_eenheid).count > 0 && getFParamFloat(frm_tijd, -1) > 0; if (dayshours) fields.push({ dbs: dbsname + ".tijdsduur", obj: "MLD_T_UITVOERTIJD", typ: "float", frm: frm_tijd, track: (track? track.tracktijdsduur : null) }, { dbs: dbsname + ".eenheid", obj: "MLD_T_UITVOERTIJD", typ: "varchar", frm: frm_eenheid, track: (track? track.trackeenheid : null) }); else // Leegmaken fields.push({ dbs: dbsname + ".tijdsduur", obj: "MLD_T_UITVOERTIJD", typ: "float", val: null, track: (track? track.tracktijdsduur : null) }, { dbs: dbsname + ".eenheid", obj: "MLD_T_UITVOERTIJD", typ: "varchar", val: null, track: (track? track.trackeenheid : null) }); return fields; }, // Converteer zo veel mogelijk direct naar goede type _qssafe: function __qssafe(model, fld, strval) { if (fld.toLowerCase() == 'id') return parseInt(strval, 10); return strval; // NOTE: Rest nog even niet, gaf nog problemen met foreigns (FSN#33730) var field = model.fields[fld]; if (!field) return strval; switch (field.typ) { case "float": return parseFloat(strval.replace(/,/g,".")); break; case "key": case "number": return parseInt(strval, 10); break; case "check": case "check0": //val = (Request.Form(formfields[i].frm).count==1)?1:0; //break; case "date": case "datetime": // Hier zouden we ook de ISO-dates moeten aankunnen? //val = getFParamDate(formfields[i].frm, null); //break; case "memo": case "varchar": return strval; break; default: { return strval; } } return strval; }, qs2json: function _qs2json(model) { var filter = {}; for (var i = 1; i <= Request.QueryString.Count; i++) { var name = Request.QueryString.key(i); var data = Request.QueryString(i); if (data.Count > 1) { filter[name] = []; for (var j = 1; j <= data.Count; j ++) filter[name].push(shared._qssafe(model, name, String(data(j)))); } else filter[name] = shared._qssafe(model, name, String(data)); } return filter; }, stripbbcodes: function (waarde) { var safepairs = "h1,h2,h3,h4,h5,h6,b,i,u,em,strong,small,big,th,td,tr,table,xmp".split(","); for (var i = 0; i < safepairs.length; i++) { var code = safepairs[i]; var strre = "\\[{0}\\](.*?)\\[\\/{0}\\]".format(code); // De ? maakt de .* lazy (ipv greedy) var strnew = "$1"; var re = new RegExp(strre, "g"); var waarde = waarde.replace(re, strnew); } return waarde; } }; // Check for parameter pName and return the value // If not specified return defVal // If defVal not specified pName a required parameter function getQParam(pName, defVal) { return _get_Param(Request.Querystring, pName, defVal); } function getFParam(pName, defVal) { return _get_Param(Request.Form, pName, defVal); } function hasQParam(pName) { return Request.Querystring(pName).Count > 0; } function hasFParam(pName) { return Request.Form(pName).Count > 0; } // Met force wordt een aanwezige maar lege waarde ook afgekeurd // Met name voor (integer) key's function _get_Param(pColl, pName, defVal, force) { var rq = pColl(pName); if (rq.count > 0 && (!force || rq(1) !== "")) return rq(1); else { if (typeof defVal != 'undefined') return defVal; else // Error message will get to client and/or IIS logfiles { if (String(Request.ServerVariables("REQUEST_METHOD")) == "HEAD") { __DoLogForm(); __DoLog("Parameter '" + pName + "' is missing, probably because of unexpected HEAD request", "#FFFF00"); __DoLog("Useragent: " + Request.ServerVariables("HTTP_USER_AGENT")); __DoLog("Referer: " + Request.ServerVariables("HTTP_REFERER")); Response.End; } else { __DoLogForm("#FF0000"); eval("INTERNAL_ERROR_PARAMETER_" + pName + "_IS_MISSING"); } } } } // Check for parameter pName and return the Integer value // If not specified return defVal // If defVal not specified pName is a required parameter // If relaxed==true then return defVal on invalid number function getQParamInt(pName, defVal, relaxed) { return _get_ParamInt(Request.Querystring, pName, defVal, relaxed); } // only plain ASCII charachters are allowed: a-z, A-Z and digits function getQParamSafe(pName, defVal) { var txt = _get_Param(Request.Querystring, pName, defVal); if (txt.match(/[^a-zA-Z0-9]/)) eval("INTERNAL_ERROR_PARAMETER_" + pName + "_IS_BAD"); return txt; } // only plain ASCII charachters are allowed: a-z, A-Z and digits function getFParamSafe(pName, defVal) { var txt = _get_Param(Request.Form, pName, defVal); if (txt.match(/[^a-zA-Z0-9]/)) eval("INTERNAL_ERROR_PARAMETER_" + pName + "_IS_BAD"); return txt; } function getFParamInt(pName, defVal, relaxed) { return _get_ParamInt(Request.Form, pName, defVal, relaxed); } function _get_ParamInt(pColl, pName, defVal, relaxed) { var strval = _get_Param(pColl, pName, defVal, true); // force: een lege waarde wordt als afwezig beschouwd if (strval == "") return defVal; if (strval == "SELF") return user_key; if (strval == defVal) return defVal; if (relaxed) strval = String(strval).replace(/[^0-9]*/, ''); // strip leading non-digits if (String(strval).substr(0,2)=='0x') var val = parseInt(strval); else var val = parseInt(strval, 10); if (isNaN(val)) { if (relaxed) return defVal; else // Error message will get to client and/or IIS logfiles eval("INTERNAL_ERROR_PARAMETER_" + pName + "_IS_NOT_INTEGER"); } else return val; } // Check for parameter pName and return the Float value // If not specified return defVal // If defVal not specified pName is a required parameter // If relaxed==true then return defVal on invalid number function getQParamFloat(pName, defVal, relaxed) { return _get_ParamFloat(Request.Querystring, pName, defVal, relaxed) } function getFParamFloat(pName, defVal, relaxed) { return _get_ParamFloat(Request.Form, pName, defVal, relaxed) } function _get_ParamFloat(pColl, pName, defVal, relaxed) { var strval = _get_Param(pColl, pName, defVal); if (strval == "") return defVal; if (strval == defVal) return defVal; var val = parseFloat(strval.replace(/,/g,".")); if (isNaN(val) || !isFinite(val)) { if (relaxed) return defVal; else // Error message will get to client and/or IIS logfiles eval("INTERNAL_ERROR_PARAMETER_" + pName + "_IS_NOT_FLOAT"); } else return val; } // Check for parameter pName and return the Date value // If not specified return defVal // If defVal not specified pName is a required parameter // defVal can be a date object or null function getQParamDate(pName, defVal) { return _get_ParamDate(Request.Querystring, pName, defVal) } function getFParamDate(pName, defVal) { return _get_ParamDate(Request.Form, pName, defVal) } function _get_ParamDate(pColl, pName, defVal) { var val = _get_ParamInt(pColl, pName, -1); if (val>0) return new Date(val); if (defVal instanceof Date) { return defVal; } if (defVal === null) // bewust triple=== { return null; } // Error message will get to client and/or IIS logfiles if (String(Request.ServerVariables("REQUEST_METHOD")) == "HEAD") { __DoLog("Parameter '" + pName + "' is not date, probably because of unexpected HEAD request", "#FFFF00"); __DoLog("Useragent: " + Request.ServerVariables("HTTP_USER_AGENT")); __DoLog("Referer: " + Request.ServerVariables("HTTP_REFERER")); Response.End; } eval("INTERNAL_ERROR_PARAMETER_" + pName + "_IS_NOT_DATE"); } // Check for parameter pName and return all values // Levert een array op, eventueel leeg // Controleert dat het Integers zijn // Ondersteunt beide formaten: &key=1&key=2&key=3&key=4 etc. // en ook: &key=1,2,3,4 function getQParamIntArray(pName, defVal, pAll) { return _get_ParamIntArray(Request.Querystring, pName, defVal, pAll) } function getFParamIntArray(pName, defVal, pAll) { return _get_ParamIntArray(Request.Form, pName, defVal, pAll) } function _get_ParamIntArray(pColl, pName, defVal, pAll) { var rf = pColl(pName); var arr = []; if (rf.count > 0 && rf(1) !== "") { var i; for (i=1; i <= rf.count; i++) { if (rf(i).indexOf(",") >= 0) { arr = arr.concat(rf(i).split(",")); } else if (rf(i) != "") { arr.push(rf(i)); } } // Nu nog conversie/controle naar integers for (i in arr) { arr[i] = parseInt(arr[i], 10); if (isNaN(arr[i])) { eval("INTERNAL_ERROR_PARAMINTARRAY_" + pName + "_IS_NOT_ALL_INTEGER"); } } if (arr.length == 1 && arr[0] == -1 && !pAll) arr = []; // *alleen* -1 betekent vaak dat iemand 'alle' gekozen heeft // dat behandelen we effectief als een leeg array // Met pAll=true wordt een array met alleen de waarde -1 wel toegestaan. } if (arr.length) return arr; else { if (typeof defVal != 'undefined') return defVal; else // Error message will get to client and/or IIS logfiles eval("INTERNAL_ERROR_PARAMINTARRAY_" + pName + "_IS_MISSING"); } } // Check for parameter pName and return all values // Levert een array op, eventueel leeg // Als de xxxIntxxx variant maar dan zonder controle dat het Integers zijn // Ondersteunt beide formaten: &key=E&key=I&key=L&key=A etc. // en ook: &key=E,I,L,A // Bij nosplit wordt alleen de eerste ondersteund function getQParamArray(pName, defVal, nosplit) { return _get_ParamArray(Request.Querystring, pName, defVal, nosplit) } function getFParamArray(pName, defVal, nosplit) { return _get_ParamArray(Request.Form, pName, defVal, nosplit) } function _get_ParamArray(pColl, pName, defVal, nosplit) { var rf = pColl(pName); if (rf.count > 0 && rf(1) !== "") { var arr = []; var i; for (i=1; i <= rf.count; i++) { if (!nosplit && rf(i).indexOf(",") >= 0) { arr = arr.concat(rf(i).split(",")); } else arr.push(rf(i)); } return arr; } else { if (typeof defVal != 'undefined') return defVal; else // Error message will get to client and/or IIS logfiles eval("INTERNAL_ERROR_PARAMARRAY_" + pName + "_IS_MISSING"); } } // Check for parameter pName and return all values // Levert een array op, eventueel leeg // Controleert dat het Datums zijn // Ondersteunt beide formaten: &key=1&key=2&key=3&key=4 etc. // en ook: &key=1,2,3,4 function getQParamDateArray(pName, defVal) { return _get_ParamDateArray(Request.Querystring, pName, defVal) } function getFParamDateArray(pName, defVal) { return _get_ParamDateArray(Request.Form, pName, defVal) } function _get_ParamDateArray(pColl, pName, defVal) { var rf = pColl(pName); if (rf.count > 0) { var arr = []; var i; for (i=1; i <= rf.count; i++) { if (rf(i).indexOf(",") >= 0) { arr = arr.concat(rf(i).split(",")); } else if (rf(i) != "") { arr.push(rf(i)); } } // Nu nog conversie/controle naar integers for (i in arr) { arr[i] = new Date(parseInt(arr[i], 10)); if (!(arr[i] instanceof Date || arr[i] === null)) // bewust triple=== { // Error message will get to client and/or IIS logfiles eval("INTERNAL_ERROR_PARAMETER_" + pName + "_IS_NOT_DATE"); } } return arr; } else { if (typeof defVal != 'undefined') return defVal; else // Error message will get to client and/or IIS logfiles eval("INTERNAL_ERROR_PARAMINTARRAY_" + pName + "_IS_MISSING"); } } // Usage: "Wilt U melding {0} afmelden?".format(12345) // Localscripts/iface.js heeft de clientside versie String.prototype.format = function() { var formatted = this; for (var i = 0; i < arguments.length; i++) { var regexp = new RegExp('\\{'+i+'\\}', 'gi'); formatted = formatted.replace(regexp, arguments[i]); } return formatted; }; // lijst is een array van parameter namen ["loc_key", "geb_key", "test"] // params is optioneel een hash { loc_key: 345, geb_key: 34 } // (zonder params wordt QueryString genomen) // resultaat: "&loc_key=345&geb_key=34&test=1&test=2" function buildTransitParam(lijst, params) { var result = ""; if (typeof lijst == "string") lijst = lijst.split(","); for (var i = 0; i < lijst.length; i++) { var itm = lijst[i]; if (params && params[itm]) // meegegeven result = result + "&" + itm + "=" + Server.URLencode(params[itm]); else { var urlparams = Request.QueryString(itm); if (urlparams.count > 0) { // Waarde is meegegeven, dan ook doorgeven. // Voeg elke waarde als aparte string waarde toe. for (var j = 1; j <= urlparams.count; j++) { result += "&" + itm + "=" + Server.URLencode(urlparams(j)); } } } } return result; } // Op een XSS veilige manier de complete QueryString door kunnen geven function transitQS() { var result = ""; for (var i = 1; i <= Request.QueryString.count; i++) { var itm = Request.QueryString.key(i); var urlparams = Request.QueryString.item(i); // Nu de waarde of waarden goed uitlezen. // urlparams kan uit meerdere waarden bestaan. Als string staan er dan extra spaties achter de komma's. // Voeg elke waarde als aparte string waarde toe. for (var j = 1; j <= urlparams.count; j++) { if (Request.QueryString("hmac").Count > 0) var safeitm = itm; // anders wordt een underscore in &mld_key een %5F en klopt de hmac niet meer else var safeitm = Server.URLencode(itm); result += "&" + safeitm + "=" + Server.URLencode(urlparams(j)); } } return result; } function padout(number) { return (number < 10) ? "0" + number : number; } // This function should only be used to format display, // – never as part of a computation. function toTimeString(jsDate, bWithSeconds) { if (jsDate===null) return ""; if (typeof jsDate == "object" && jsDate.type == 135/*adDBTimeStamp, oRs("datum")*/) jsDate = new Date(jsDate.value); if (typeof jsDate == "date") // een oRs("datum").value jsDate = new Date(jsDate); if (typeof jsDate == "number") // 8.5 voor 08:30 { if (jsDate < 0 || jsDate > 24) return ""; var hh = Math.floor(jsDate); var mm = (jsDate*60) % 60; jsDate = (new Date).setHours(hh,mm,0,0); } if (!jsDate) return ""; jsDate = new Date(jsDate.valueOf() + 1); // rounding errors ret = padout(jsDate.getHours()) + ":" + padout(jsDate.getMinutes()); if (bWithSeconds) ret += ":" + padout(jsDate.getSeconds()); return ret; } function toDateString(jsDate, noDay, pretty) { var resstr = ""; var today = new Date().midnight(); var yesterday = (new Date(today)) yesterday.setDate(today.getDate()-1); var tomorrow = (new Date(today)); tomorrow.setDate(today.getDate()+1); if (jsDate===null) return ""; if (typeof jsDate == "object" && jsDate.type == 135/*adDBTimeStamp, oRs("datum")*/) jsDate = new Date(jsDate.value); if (typeof jsDate == "date") // een oRs("datum").value jsDate = new Date(jsDate); if (!jsDate) return ""; // Ik wil graag, alleen als parameter friendly?, voor de datums gisteren, vandaag en morgen de tekst Gisteren, Vandaag en Morgen opleveren // Voor lijsten/sortering is dat vaak niet handig, maar soms is het veel begrijpelijker. Als de mogelijkheid er is, kunnen we // geleidelijk de toepassing uitbreiden. In Lopende zaken op de portal om mee te beginnen. if (pretty && jsDate.midnight().getTime() == today.getTime()) resstr = L("lcl_date_today"); else if (pretty && jsDate.midnight().getTime() == yesterday.getTime()) resstr = L("lcl_date_yesterday"); else if (pretty && jsDate.midnight().getTime() == tomorrow.getTime()) resstr = L("lcl_date_tomorrow"); else resstr = (noDay?"":calendar_names.daysMin[jsDate.getDay()] + " ") + padout(jsDate.getDate()) + "-" + padout(jsDate.getMonth() + 1) + "-" + padout(jsDate.getFullYear()); return resstr; } function toDateTimeString(jsDate, bWithSeconds, noDay, prettyday) { if (jsDate===null) return ""; if (typeof jsDate == "object" && jsDate.type == 135/*adDBTimeStamp, oRs("datum")*/) jsDate = new Date(jsDate.value); if (typeof jsDate == "date") // een oRs("datum").value jsDate = new Date(jsDate); return toDateString(jsDate, noDay, prettyday) + " " + toTimeString(jsDate, bWithSeconds) } // Zie document"z:\Project\Sm44\DOC\Intern\Facilitor5i\Facilitor 5i Quotes en Escape.doc" safe = { // getal 0 blijft 0 nvl: function (x) { if (x == null) return ""; else return x; }, // Bijvoorbeeld textarea: function(waarde) { return Server.HTMLEncode(safe.nvl(waarde)); }, // Bijvoorbeeld
< % =safe.fclthtml(omschr)% >
// We ondersteunen bb-codes die 1-op-1 naar html zijn om te zetten // Let op: deze functie lijkt veel op default.xsl/safe.fclthtml // Pas die eventueel ook aan fclthtml: function (waarde, mldlink) { var safepairs = "h1,h2,h3,h4,h5,h6,b,i,u,em,strong,small,big,th,td,tr,table,pre,code".split(","); var safehtml = safe.html(waarde); // Let op: vervangt *alle* \n door
. Dat gaan we deels later nog terugdraaien // We hebben het wel *nodig* omdat .* niet over meerdere regels werkt (hoewel dat // weer oplosbaar schijnt te zijn met [\s\S]) for (var i = 0; i < safepairs.length; i++) { var code = safepairs[i]; var strre = "\\[{0}\\](.*?)\\[\\/{0}\\]".format(code); // De ? maakt de .* lazy (ipv greedy) var strnew = "<" + code + ">$1"; var re = new RegExp(strre, "g"); var safehtml = safehtml.replace(re, strnew); } if (mldlink) var safehtml = safe.mldlinkhtml(safehtml); // Nu de
's binnen de tabel terugzetten. Let op dat ze *binnen* 's weer wel moeten blijven // TODO: Kan dit beter? if (safehtml.indexOf("") > -1) { var tablepairs = "th,td,tr,table".split(","); for (var i = 0; i < tablepairs.length; i++) { var code = tablepairs[i]; var re = new RegExp("<{0}>(\r
)+".format(code), "g"); var safehtml = safehtml.replace(re, "<" + code + ">"); var re = new RegExp("(\r
)+".format(code), "g"); var safehtml = safehtml.replace(re, ""); } } // Nu alle
's binnen
 terugzetten
        var matches = safehtml.match(/(?:.*?)<\/pre>/g);
        if (!matches)
            matches = safehtml.match(/(?:.*?)<\/code>/g);
        if (matches)
        {
            var len = matches.length;
            var i;
            for (i = 0; i < len; i++)
            {
                var pre = matches[i];
                pre = pre.replace(//g, '\n');
                safehtml = safehtml.replace(matches[i], pre);
            }
        }
        return safehtml;
    },
    mldlinkhtml: function (safehtml)
    {
        if (!S("mld_autolink_regexp")) /// (?:(?:melding [A-Z]*)|(?:[A-Z]*\#[A-Z]*))(\d*)
            return safehtml;

        var safehtml = safehtml.replace(new RegExp(S("mld_autolink_regexp"), "g"),
            "$&")

        return safehtml;
    },
    // Bijvoorbeeld 
< % =safe.html(omschr)% >
html: function (waarde) { return Server.HTMLEncode(safe.nvl(waarde)).replace(/\n/g, "
"); }, // bijvoorbeeld htmlattr: function (waarde) { return Server.HTMLEncode(safe.nvl(waarde)).replace(/\'/g,''').replace(/\"/g,'"'); // ' syntax highlight correctie }, // bijvoorbeeld queuemail.asp?subject=" + safe.url(subject) url: function (waarde) { return Server.URLencode(safe.nvl(waarde)); }, // Altijd quotes er om, we verdubbelen interne // CR+LF binnen een waarde maken we alleen LF van csv: function (waarde) { if (waarde==null) return ""; return "\"" + String(waarde).replace(/\x0D\x0A/g,'\x0A').replace(/\"/g,'""') + "\""; // " syntax highlight correctie }, // Levert een getal op als string met een komma of een punt, afhankelijk van de // decimaal scheider die de gebruiker op zijn eigen PC heeft ingesteld // Merk op: géén 1000-separator, dat is te lastig // Te gebruiken voor readonly presentatie van bedragen displayfloat: function (fnum, decimals, trimZeros) { if (fnum == null) return ""; if (typeof decimals == "undefined") decimals = 2; fnum = fnum.toFixed(decimals); var waarde2 = (1.5).toLocaleString(); // Eventueel gewenste komma, dank zij LCID gezet in appl\FAC\facilitor.inc fnum = fnum.substring(0, fnum.length - decimals - 1) + waarde2.substring(2, 1) + fnum.substring(fnum.length - decimals); if (trimZeros) { while (fnum.substring(fnum.length-1) == "0") fnum = fnum.substring(0, fnum.length-1); if (fnum.substring(fnum.length-1) == waarde2.substring(2, 1)) fnum = fnum.substring(0, fnum.length-1); } return fnum; }, // luxe variant: gewenste punt/komma en ook duizendscheiders curr: function (waarde, nocents) { if (waarde == null) return ""; if (waarde && !isNaN(waarde)) waarde = waarde.toLocaleString(); if (waarde && nocents) waarde = waarde.substring(0, waarde.length-3); return waarde; }, // Editable willen we er echt geen duizend-scheiders in hebben // Die zijn namelijk heel moeilijk terug te converteren naar number // Te gebruiken voor bewerkbare velden met bedragen editablefloat: function(fnum, decimals) { return safe.displayfloat(fnum, decimals); // die heeft toch geen duizend-scheiders }, curreditable: function (waarde) { return safe.editablefloat(waarde, 2); }, // alert("< % =safe.jsstring(waarde) % >") // is ook funest! jsstring: function (waarde) { return (safe.nvl(waarde)).replace(/\\/g,'\\\\').replace(/\n/g,'\\n') .replace(/\r/g,'').replace(/\'/g,'\\x27').replace(/\"/g,'\\x22') .replace(/\/g, '\\x3E'); // ' syntax highlight correctie }, // var S("res_t1")=< % =safe.jsfloat(S("res_t1"))% > jsfloat: function (fnum) { return(String(fnum)).replace(',','.'); }, quoted_sql: function (tekst, maxlen) // maxlen is optioneel { if (tekst == null) return "NULL"; if (!maxlen) maxlen = 4000; // Haal alle lage bytes (muv TAB (x09), LF (x0A) en CR (x0D)) er uit. Die representeren // geen geldig karakter in Windows-1252 of iso-8859. Ook in UTF-8 staan ze niet voor // tekst, mochten we dat ooit willen gaan ondersteunen. // Voor ultieme veiligheid ook maar de hoge unicodes er uit? Neen, EURO is ten slotte // ook een 'hoge' unicode tekst = tekst.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]+/g, "?"); tekst = tekst.substr(0, maxlen); // FSN#35288: '{' naar CHR(123) voorkomt Unspecified Error return "'" + tekst.replace(/\'/g,"''").replace(/\{/g,"'||CHR(123)||'") + "'"; // " syntax highlight correctie }, qL: function (p_lcl, params) { return safe.quoted_sql(L(p_lcl, params)); }, quoted_sql_join: function (tekstarr, toupper) // Vooral voor IN () clausules met tekstwaardes { if (!tekstarr || !tekstarr.length) return "NULL"; var safearr = []; for (var i = 0; i < tekstarr.length; i++) { safearr.push(toupper? safe.quoted_sql_upper(tekstarr[i]) : safe.quoted_sql(tekstarr[i])) } return safearr.join(", "); }, quoted_sql_upper: function (tekst, maxlen) { if (tekst == null) return "NULL"; return safe.quoted_sql(tekst.toUpperCase(), maxlen); }, quoted_sql_wild: function (tekst, maxlen) { if (tekst == null) return "NULL"; return safe.quoted_sql(tekst.toUpperCase().replace(/\*/g, "%"), maxlen); }, // converteer alle waarden in arr naar integer. // waar dat niet lukt worden ze gewoon verwijderd(?) int_array: function (arr) { var safe_arr = []; for (var i = 0; i < arr.length; i++) { var v = parseInt(arr[i], 10); if (!isNaN(v)) safe_arr.push(v); } return safe_arr; }, filename: function (naam) // geen 'lage' karakters en geen (back)slashes, *,%,<,>, '"', ':', ';' '?' and '|' of '+' { return naam.replace(/[\x00-\x1F|\/|\\|\*|\%\<\>\"\:\;\?\|\+]+/g, "_"); // " syntax highlight correctie }, UserContext: function () { try { var _oAbout = Server.CreateObject('SLNKDWF.About'); return "UserContext: " + _oAbout.UserContext; } catch (e) { return "UserContext: no SLNKDWF.About " + e.description; } } } function abort_with_warning(warning, code) { if (Request.QueryString("api2").Count > 0) { code = code || 500; var data = { error: { code: code, message: warning } }; var format = Request.QueryString("format"); if (format == "xml") { Response.ContentType = "text/xml"; var xml_antwoord = api2_rest.json2xml([data.error], { records_name: "errors", record_name: "error"}, true); Response.Write(xml_antwoord.xml); } else if (format == "html") { Response.ContentType = "text/html"; var str_antwoord = "
"
                             +      Server.HTMLEncode(JSON.stringify(data, null, 2))
                             + "
"; Response.Write(str_antwoord); } else { Response.ContentType = "application/json"; Response.Write(JSON.stringify(data, null, 2)); } Response.Status = code; Response.End; } else if (JSON_Result && JSON) { __Log("D 1"); Response.Write(JSON.stringify({ warning: warning, keepForm: true })); } else { __Log("D 2"); %> <% FCLTHeader.Generate() %> <% } Response.End } var protectHMAC = { // Alleen geschikt als tijdelijk geheim, doorgaans wordt // IIS elke nacht gerecycled en dan zijn we deze kwijt getProtectSecret: function () { var appsecret = Application(customerId + "_protectsecret"); if (typeof appsecret == "undefined") { appsecret = shared.random(32); Application.Lock(); Application(customerId + "_protectsecret") = appsecret; Application.UnLock(); } return appsecret; }, create: function(str, params) { params = params || {}; params.sleutel = params.sleutel || protectHMAC.getProtectSecret(); var ts = String(Math.round((new Date).getTime() / 1000)); var data = ts + ":" + str __Log("hmacdata: " + data); //__Log("sleutel: "+ params.sleutel); var oCrypto = new ActiveXObject("SLNKDWF.Crypto"); var hmac = oCrypto.b64_hmac_sha1(params.sleutel, data); oCrypto = null; // Caching klinkt leuk maar Oracle sessies blijven langer hangen? return ts+":"+hmac; }, verify: function (str, hmac, params) { params = params || {}; params.expire = params.expire || 720; var expTime = new Date; expTime.setMinutes(expTime.getMinutes()-params.expire); // max expire in het verleden var future = new Date; future.setMinutes(future.getMinutes() + 3); // Over 3 minuten begint de toekomst :-) // Voorkomt misbruik tijdens zomer/wintertijd wissel var hmacArr = hmac.split(":"); var createTimeInt = parseInt(hmacArr[0],10); var createTime = new Date(createTimeInt*1000); if (!createTimeInt || !createTime || createTime < expTime || createTime > future) { createTime.setMinutes(createTime.getMinutes() + params.expire); __Log("HMAC: Te laat: je had voor {0} moeten zijn.".format(toDateTimeString(createTime, true))); if (params.relaxed) return false; else { __DoLog("HMAC: Te laat: je had voor {0} moeten zijn.".format(toDateTimeString(createTime, true))); shared.simpel_page(L("lcl_hmac_late")); } } __Log("testing hmacdata: " + str); params.sleutel = params.sleutel || protectHMAC.getProtectSecret(); //__Log("testing sleutel: "+ params.sleutel); var oCrypto = new ActiveXObject("SLNKDWF.Crypto"); var should_hmac = oCrypto.b64_hmac_sha1(params.sleutel, hmacArr[0] + ":" + str); oCrypto = null; if (hmacArr[1] != should_hmac) { __Log("str: " + str); __Log("hmac: " + hmac); __Log("HMAC[1]: " + hmacArr[1]); __Log("SHOULD: " + should_hmac); if (params.relaxed) { __Log("(relaxed==true, will try hmac again with user_key==-1)"); return false; } else INTERNAL_ERROR_HMAC_TAMPERING; } return true; } } var protectQS = { // Bescherm de url tegen tampering door een hmac achteraan toe te voegen // Te controleren aan de ontvangende kant met protectQS.verify() // PAS OP: protectQS.wsc bevat voor Facmgt nog (even) hetzelfde algoritme create: function(url, params) { params = params || {}; var splitter = url.split("?"); var pad = splitter[0]; var qs = splitter.length>1?splitter[1]:"x=x"; var padsplitter = pad.split("/"); var file = padsplitter[padsplitter.length-1]; // laatste component // Let op dat protectQS.wsc hetzelfde doet voor Facmgt var data = (params.no_user_key?"":(user_key + ":")) + file.toUpperCase() + "?" + qs; var hmacced = protectHMAC.create(data, params); return pad + "?" + qs + "&hmac="+Server.URLencode(hmacced); }, // params: expire: in minuten. Default 720, een heel kritische functie kan zichzelf scherper beschermen // allowparams []: bijv. 'outputmode'. Deze worden er voor de HMAC berekening uitgestript // relaxed: doe niet al te moeilijk over errors (maar wel false) verify: function (params) { params = params || {}; if (params.relaxed && Request.QueryString("hmac").Count == 0) return false; // vanuit API2 geven we checkqs en checkpath mee omdat die door IIS Rewrite // te veel veranderd zijn var checkurl = params.checkqs || String(Request.QueryString); if (params.allowparams) { var i; for (i in params.allowparams) { var param = params.allowparams[i]; if (Request.QueryString(param).Count > 0) { // die rekenen we niet mee in de HMAC checkurl = checkurl.replace(param + "=" + Request.QueryString(param) + "&", ""); checkurl = checkurl.replace("&" + param + "=" + escape(Request.QueryString(param)), ""); } } } var hmac = getQParam("hmac"); var hmacArr = hmac.split(":"); var indexHmac = String(checkurl).indexOf("&hmac="); if (checkurl.substring(indexHmac+1).indexOf("&") >=0) { if (params.relaxed) return false; else INTERNAL_ERROR_HMAC_URL_EXTENSION; } var qs = checkurl.substring(0,indexHmac); var pad = params.checkpath || String(Request.ServerVariables("SCRIPT_NAME")); var padsplitter = pad.split("/"); var file = padsplitter[padsplitter.length-1]; // laatste component // Let op dat create dezelfde data hasht var data = user_key + ":" + file.toUpperCase() + "?" +qs; //__Log("testing hmacdata: " + data); //__Log("testing sleutel: "+ params.sleutel); if (params.allow_anonymous) params.relaxed = true; var is_ok = protectHMAC.verify(data, getQParam("hmac"), params); // klapt er uit bij hmac-tampering en relaxed==false if (!is_ok && params.allow_anonymous) { // Testen met user_key == -1 var data = "-1" + ":" + file.toUpperCase() + "?" +qs; params.relaxed = false; // Nu moet hij echt kloppen protectHMAC.verify(data, getQParam("hmac"), params); } return is_ok; } } var protectRequest = { theToken: function () { return Session("ASPFIXATION") }, // Session ASPFIXATION token wordt gebruikt als cookie voor anti CSRF Cross Site theVar: "__RequestVerificationToken", // De form-name. Komt ook terug in FacmgtTools.asp inputToken: function () // Maak een hidden inputveld met token { %> <% }, // Van dataToken is ook een clientside variant in iface.js dataToken: function (dataName) // Voeg aan een data hash een input token toe { %> <%=dataName%>.<%=protectRequest.theVar%> = "<%=safe.jsstring(protectRequest.theToken())%>"; <% }, validateToken: function () { // De token van het hidden inputveld valideren met de token van de cookie __Log("XXX"); try // API's hebben vaak inputXML.load(Request); gedaan en dan werkt getFParam niet meer { var verificationToken = getFParam(protectRequest.theVar, ""); } catch (e) { // API's die buildInsert of buildUpdate doen moeten daar vaak { noValidateToken: true } bij doen __DoLog("INTERNAL_ERROR_TOKEN_VALIDATIE", "#FFFF00"); INTERNAL_ERROR_TOKEN_VALIDATIE; }; var cookieToken = protectRequest.theToken()||""; // is leeg bij self_register.asp als we nog niet zijn ingelogd. if (verificationToken != cookieToken) { // Is deze functie vanuit een post aangeroepen? Dan afhandeling door post functie af laten handelen. __Log("XXX 1"); if (typeof DOCTYPE_Disable != "undefined" && DOCTYPE_Disable == 1 && typeof JSON != "undefined") { __Log("XXX 2"); var result = {message: L("lcl_authentication_error")}; Response.Write(JSON.stringify(result)); Response.End; } else { __Log("XXX 3"); abort_with_warning(L("lcl_authentication_error")); __Log("XXX 4"); } } } } var cache = { whenTrue: function _whenTrue(b) { if (b) { var eTag = user.lang() + "_" + S("cache_changecounter") + "_" + Session("ASPFIXATION"); // Hoogstens zo lang je ingelogd bent Response.AddHeader("ETag", eTag); Response.AddHeader("Last-Modified", new Date().toGMTString()); if (Request.ServerVariables("HTTP_IF_NONE_MATCH") == eTag) { // We hebben een match! var tm = ""; if (Request.ServerVariables("HTTP_IF_MODIFIED_SINCE").Count) tm = " (" + toDateTimeString(new Date(Request.ServerVariables("HTTP_IF_MODIFIED_SINCE")), true) + ")"; __Log("Geslaagde caching" + tm + ": we geven status 304 terug en stoppen."); Response.Clear(); Response.Status = "304 Not modified"; Response.End; } } }, whenNoRecords: function _whenNorecords(sql) { var oRs = Oracle.Execute(sql); this.whenTrue(oRs.Eof); oRs.Close(); } } // Datum functies Date.prototype.toSQL = function toSQL(withTime) { var year = this.getFullYear(); if (year < 1000 || year > 9999) shared.internal_error("Invalid year " + this.toLocaleString()); // Net iets minder erg dan ORA-01830 var str = padout(this.getDate()) + "-" + padout(this.getMonth() + 1) + "-" + padout(this.getFullYear()) if (withTime) { str += " " + padout(this.getHours()) + ":" + padout(this.getMinutes()); return "to_date(" + safe.quoted_sql(str) + ", 'DD-MM-YYYY HH24:MI')"; } else return "to_date(" + safe.quoted_sql(str) + ", 'DD-MM-YYYY')"; } Date.prototype.beginToSQL = function beginToSQL() { var theDateStart = new Date(this); theDateStart.setHours(0, 0); return theDateStart.toSQL(true); } Date.prototype.endToSQL = function endToSQL() { var theDateEnd = new Date(this); theDateEnd.setHours(23, 59); return theDateEnd.toSQL(true); } Date.prototype.midnight = function midnight() { var midn = new Date(this); midn.setHours(0,0,0,0); return midn; } function inArray(needle, haystack) { var length = haystack.length; for(var i = 0; i < length; i++) { if(haystack[i] == needle) return true; } return false; } // Bepaalt de customerId (en rooturl) // Na afloop is de globale variabele customerId gezet en opgeslagen in Session("customerId") function determineCustomerId() { var validCustId = /[A-Z0-9]/; // autodetect rooturl. Al onze pagina's beginnen met /cust of met /appl var pad = String(Request.ServerVariables("PATH_INFO")); var lowerpad = pad.toLowerCase(); var i1 = lowerpad.indexOf("/cust/"); if (i1 < 0) i1 = lowerpad.indexOf("/appl/"); if (i1 < 0) i1 = lowerpad.indexOf("/default.asp"); // API's /* global */ rooturl = pad.substr(0, i1); var trycust = ""; if ( typeof Session("customerId") == "undefined" ) // ASP-Sessie verlopen of nieuw binnen. { if (Request.QueryString("api").Count>0) { Session.Abandon(); // Voorkom dat een session ontstaat doordat we een API-call doen. // When the Abandon method is called, the current Session object is queued for deletion // but is not actually deleted until all of the script commands on the current page have // been processed. This means that you can access variables stored in the Session object // on the same page as the call to the Abandon method but not in any subsequent Web pages. } // is var customerId al gezet door /default.asp? if (typeof customerId != 'undefined' && customerId != "XXXX") trycust = customerId.toUpperCase(); else if (String(Request.Cookies("fcltcust"))) // Remember me knop trycust = String(Request.Cookies("fcltcust")); else { // URL formaat http://xxxx.5iwork/facilitor5iwork: pak eerste 4 letters var host = String(Request.ServerVariables("HTTP_HOST")); trycust = host.substring(0,4).toUpperCase(); var fso = Server.CreateObject("Scripting.FileSystemObject"); if (!fso.FileExists(Server.MapPath(rooturl + "/cust/" + trycust + "/Oracle.udl"))) trycust = null; } // TODO: is er maar één cust-folder met een Oracle.udl? Gebruik die } // Deze overruled alles: if (getQParamSafe("fac_id","") != "") var trycust = getQParamSafe("fac_id").toUpperCase(); if (trycust && validCustId.test(trycust)) // Geen vreemde karakters en er is een udl? { var fso = Server.CreateObject("Scripting.FileSystemObject"); if (fso.FileExists(Server.MapPath(rooturl + "/cust/" + trycust + "/Oracle.udl"))) Session("customerId") = trycust; else { Response.write("Invalid customerId: " + trycust); Response.end; } } /* global */ customerId = Session("customerId"); if (typeof customerId == "undefined")// Ik geef het op. { if (String(Request.ServerVariables("HTTP_ACCEPT")).match(/application\/json/) != null) { var msg = "Your session has expired.\nPlease restart FACILITOR."; // L("xxx") vertrouw ik ook niet meer Response.Write("FCLTExpired:" + msg); // FCLTExpired wordt zo opgepikt door FcltJquery.js ajaxSetup error handler Response.End; } Response.Redirect(rooturl + "/appl/shared/expired.asp"); } if (!validCustId.test(customerId)) // paranoia { INTERNAL_ERROR_BAD_CUSTOMERID; } } // Merk op dat we een beetje browser capabilities en device capabilities // op één hoop gooien. Goed genoeg vooralsnog device = { // lower 8 bits isSupported: 0x0001, isDesktop : 0x0002, isTouch : 0x0004, isMobile : 0x0008, // next 8 bits canPrint : 0x0100, // Show print buttons canExcel : 0x0200, // Show Excel buttons canUpload : 0x0400, // Can upload files canDownload: 0x0800, // Can download files // more supportsPlaceholder: 0x1000, // Support placeholder attribute of input _devicebits: 0xff01, // Default desktop, alle capabilities init: function () { if (typeof Session("devicebits") != "undefined") this._devicebits = Session("devicebits") else Session("devicebits") = this._devicebits; }, test: function (bits) { return ( (this._devicebits & bits) == bits); }, testAll: function (bits) { return test(bits); }, testAny: function (bits) { return ( (this._devicebits & bits) != 0); }, set: function (bits, newval) { if (newval || typeof newval == "undefined") this._devicebits = this._devicebits | bits; else // reset this._devicebits = this._devicebits & (~bits); Session("devicebits") = this._devicebits; } } myJSON = { internal_parsedate: function (key, value) { var a; if (typeof value === 'string') { a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); if (a) { return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6])); } } return value; }, // myJSON.parse doet hetzelfde als JSON.parse maar herkent automatisch datum variabelen parse: function _parse(text) { return JSON.parse(text, myJSON.internal_parsedate); } } HTTP = { // params: type, data, headers, APIKEY (voor API2) getJSON: function _getJSON(url, params) // Serverside variant van jQuery $.getJSON { params = params || {}; if (!params.type) params.type = params.data?"POST":"GET"; // Met data default POST, zonder data default GET var http_request = Server.CreateObject("WinHTTP.WinHTTPRequest.5.1"); var vraag = JSON.stringify(params.data, null); __Log("HTTP url: " + url); if (vraag) __Log("data: " + vraag); http_request.open(params.type, url, false); // Synchroon // Het volgende lijkt weinig effect te hebben, post-body is nog steeds utf-8? // in api.inc/RequestJSON verwachten we dan ook maar utf-8 dus netto werkt het goed? http_request.setRequestHeader("Content-Type", "application/json; charset=windows-1252"); if (params.APIKEY) http_request.setRequestHeader("X-FACILITOR-API-Key", params.APIKEY); for ( var i in params.headers ) { http_request.setRequestHeader(i, params.headers[ i ]); } // Het mag niet te lang duren! var lResolve = 5 * 1000; var lConnect = 5 * 1000; var lSend = 15 * 1000; var lReceive = 15 * 1000; http_request.setTimeouts(lResolve, lConnect, lSend, lReceive); var errtxt = ""; var errnum = 0; try { http_request.send(vraag); } catch (e) { errtxt = e.description; errnum = e.number; } if (!errtxt && http_request.status != 200) { // Hier HTML lijkt me misplaatst errtxt = "HTTP status " + http_request.status + ": " + http_request.responseText; } if (errtxt) { //__DoLog(" url: " + url, "#FF0000"); //__DoLog(" data: " + vraag); if (errnum == -2147012894) // "The operation timed out", dan is http_request niet te benaderen __DoLog(" http_request {0} mislukt: {1}".format(url, errtxt), "#FF0000"); else if (http_request.status == 404) __Log(" http_request status = 404 {0}".format(url)); else if (http_request.status == 401) __DoLog(" http_request status = 401 {0}".format(url)); else __DoLog(" http_request {0} mislukt: {1}".format(url, errtxt), "#FF0000"); } else { __Log(http_request.ResponseText); try { var result = myJSON.parse(http_request.ResponseText) } catch (e) { __DoLog("Parse error: " + e.message); __DoLog(http_request.ResponseText); result = { success: false, message: e.message }; } return result; } }, urlzelf: function () { return HTTP.urlzelfnoroot() + rooturl; }, urlzelfnoroot: function () { var proto = (Request.ServerVariables("SERVER_PORT") == "443")? "https" : "http"; var sitenoroot = proto + "://" + Request.ServerVariables("SERVER_NAME"); return sitenoroot; } } %>