<%@ language = "JavaScript" %>
<% /*
     $Revision$
     $Id$

    File:           api_SOAP.asp

    Description:    API om een SOAP-achtige service te bieden gebaseerd op een bestaande view.
                    Dat is informatie in XML op te leveren (of een ander formaat dat je via een XSL
                    kunt bewerkstelligen) uit een view en bepaalde toegestande filters.
                    Wat de API kan en doet (view, filters, limiet) moet in fac_api gedefinieerd zijn,
                    geidentificeerd door de apiname.
    Parameters:     apiname, apikey
    Context:        Door een remote systeem (geen persoon) om info uit FACILITOR te halen aan te roepen

    Notes:          De readrechten van de user die met de meegegeven apikey wordt geidentificeerd
                    gehanteerd worden. TODO!
                    Het is Oracle die in eerste instantie de XML fabriceert.

                    BELANGRIJK:
                    De bedoeling is dat de DE M2M SOAP/Machine manier is om info uit FACILITOR te krijgen.
                    De enige manier.
                    Dit mechanisme is dus zo generiek mogelijk, en er worden zoveel mogelijk conventies gehanteerd
                    Iedere aanpassing of gewenste customisation moet kunnen ZONDER dit mechanisme aan te passen en
                    ZONDER een ander mechanisme te gebruiken. Om een dergelijke service te kunnen gebruiken moet:
                    - een view bestaan (gebruik bij voorkeur een bestaande voorgedefinieerde MOD_V_API_XXX view)
                    - een definitierecord (API) in fac_api bestaan/worden toegevoegd die per request aan die views linkt
                    - een user (met APIKEY) de juiste rechten krijgen
                    Vervolgens kan de service worden aangeroepen met API en APIKEY, en bevat de geposte XML
                    de criteria. Een resulterende XML met een Response is het resultaat, alsmede een HTTP-statuscode
                    waarbij 200 Ok is.

     Status:        Autorisatie tegen de API_KEY-user checken
*/
    DOCTYPE_Disable = 1;
    LCL_Disable = 1
    THIS_FILE = "appl/api/api_soap.asp";

//    Session("logging")=1;
%>
<!-- #include file="../Shared/common.inc" -->
<!-- #include file="../Shared/xml_converter.inc" -->
<!-- #include file="../Shared/json2.js" -->
<!-- #include file="../api/api.inc" -->
<%
    Session.Codepage = 65001;
    Response.Charset = 'utf-8';
    Session.Abandon(); // Voor de zekerheid

try
{
    var API = new API_func();   // Valideert ook

    var asJSON = getQParam("json","0")!="0";

    var xmlReq = Server.CreateObject("MSXML2.DOMDocument.6.0");
    xmlReq.load(Request);

    if (API.apidata.loglevel) __Log2File(xmlReq.xml, API.APIname + "_IN");

    if (xmlReq.documentElement.firstChild.prefix)
        var soapns = xmlReq.documentElement.firstChild.prefix + ":";
    else
        var soapns = "";
    var body = xmlReq.getElementsByTagName(soapns+"Body");
    if (!body.length) {
        throw("Invalid request: No XML body");
    }
    var GetReq = body[0].firstChild.baseName;
    // req heeft vorm GetXXX, met XXX is de req van de definitie in fac_api
    var req = GetReq.substring(3);

    var requestData = null;
    // de apidata.viewmapping is een JSON string met (voorbeeld)
    // {"req": "Reserveringen",
    //  "node": "reservering",
    //  "view": "res_v_api_reserveringen",
    //  "filter":{"locatiecode":"locatiecode", "catalogus": "catalogus", "begindatumtijd": {"colName": "van", "operand":"GT", "datatype": "date"}, "einddatumtijd": {"colName": "tot", "operand":"LT", "datatype": "date"}},
    //  "limit":75}
    for (i=0; i < API.apidata.viewmapping.length; i++)
    {
        if (API.apidata.viewmapping[i].req == req)
        {
            requestData = API.apidata.viewmapping[i];
        }
    }
    if (requestData == null) {
        API.error("Invalid request: " + GetReq);
    }

    var viewname = requestData.view;
    var resnodename = requestData.node;

    var autfunction = requestData.autfunction; // optioneel
    if (!autfunction)
    {
        autfunction =
           {"regio":          "WEB_ALGUSE",
            "district":       "WEB_ALGUSE",
            "locatie":        "WEB_ALGUSE",
            "gebouw":         "WEB_ALGUSE",
            "verdieping":     "WEB_ALGUSE",
            "ruimte":         "WEB_ALGUSE",
            "bestelling":     "WEB_BESUSE",
            "bestelopdracht": "WEB_BESUSE",
            "afspraak":       "WEB_BEZUSE",
            "contract":       "WEB_CNTUSE",
            "factuur":        "WEB_FINUSE",
            "deel":           "WEB_INSUSE",
            "melding":        "WEB_MLDUSE",
            "opdracht":       "WEB_MLDUSE",
            "afdeling":       "WEB_PRSUSE",
            "persoon":        "WEB_PRSUSE",
            "reservering":    "WEB_RESUSE",
            "voorziening":    "WEB_RESUSE"}[resnodename];
    }

    // Nu filters zoeken
    var wheres = [];
    var filters = body[0].firstChild.childNodes;
    for (i=0; i < filters.length; i++)
    {
        var filterName = filters[i].baseName;
        var found = false;
        for (xx in requestData.filter) // kennen we dit filter?
        {
            if (filterName == xx)
            {
                var found = true;
                var wildtext = filters[i].text;
                var colHash = requestData.filter[xx];
                if (typeof colHash == "string") // backwards compatible
                {
                    colHash = { colName: colHash,
                                datatype: "string",
                                operand : "LIKE"
                              };
                    wildtext += "%";
                }
                else
                {
                    // heeft colHash de volgende structuur
                    /*
                        res_datum: { colName: "res_rsv_ruimte_van",
                                     datatype: "date",
                                     operand: "LT"
                                   }
                     */
                }
                var criterium = safe.quoted_sql(filters[i].text);
                switch(colHash.datatype)
                {
                    case "string" :
                        criterium = safe.quoted_sql_upper(filters[i].text);
                        break;
                    case "date" :
                        // Er zijn regels over datums
                        // http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/datatypes.html#isoformats
                        // We willen natuurlijk dat het datatype in de view gewoon datum is
                        // en dan moeten we van de string van het criterium volgens conventie een date maken
                        // TODO: Oracle vindt dat standaard Date niet DateTime is...
                        // Voor filtering is dit perfect zo, maar de presentatie in de XML moet ..?
                        // TOCH TODO: wel of geen seconden??!?
                        criterium = "TO_DATE("+safe.quoted_sql(filters[i].text)+", 'yyyy-mm-dd\"T\"hh24:mi:ss')";
                        break;
                }
                switch(colHash.operand)
                {
                    case "EQ":
                        wheres.push("UPPER("+colHash.colName + ") = " + criterium)
                        break;
                    case "LIKE":
                        // altijd case-insensitive vind ik oke, maar de wildcard zou IMO in het filter moeten worden meegegeven.
                        wheres.push("UPPER("+colHash.colName + ") LIKE " + safe.quoted_sql_wild(wildtext))
                        break;
                    case "LT":
                        wheres.push(""+colHash.colName + " < " + criterium)
                        break;
                    case "GT":
                        wheres.push(""+colHash.colName + " > " + criterium)
                        break;
                    case "LTE":
                        wheres.push(""+colHash.colName + " <= " + criterium)
                        break;
                    case "GTE":
                        wheres.push(""+colHash.colName + " >= " + criterium)
                        break;
                }
            }
        }
        if (!found && (filterName.match(/\_KEY$/i) || filterName.toUpperCase() == "KEY")  // fallback: op _KEY velden mag je automatisch filteren met EQ
                   && parseInt(filters[i].text, 10))
        {
            var sql = "SELECT data_type"
                    + "     , data_length"
                    + "     , data_precision"
                    + "     , data_scale"
                    + "  FROM user_tab_columns"
                    + " WHERE table_name = "  + safe.quoted_sql_upper(viewname)
                    + "   AND column_name = " + safe.quoted_sql_upper(filterName);
            var oRs = Oracle.Execute(sql);
            if (!oRs.Eof)
            {
                wheres.push(filterName + " = " + parseInt(filters[i].text, 10))
            }
            oRs.Close();
        }
    }

    // Bu: 3D scope toepassen

    // Check for our special 3D column names
    var has3D = {};
    var any3D = false;
    sql = "SELECT UPPER(column_name) column_name"
        + "  FROM user_tab_columns"
        + " WHERE table_name = " + safe.quoted_sql_upper(viewname)
        + "   AND UPPER(column_name) LIKE 'FCLT_3D_%'";
    var oRs = Oracle.Execute(sql);
    while (!oRs.eof)
    {
        any3D = true;
        has3D[oRs("column_name").Value] = true;
        oRs.MoveNext();
    }
    oRs.Close();
    if (any3D)
    {
        if (!autfunction)
            API.error("3d yet autfunction undefined"); // Strict genomen niet nodig voor FCLT_3D_USER_KEY

        var authparams = user.checkAutorisation(autfunction);
        // TODO: Onderstaande code gaan delen met fac_usrrap.inc?
        if (has3D["FCLT_3D_DISCIPLINE_KEY"])
        {
            wheres.push(" FCLT_3D_DISCIPLINE_KEY IN"
                        + " (SELECT ins_discipline_key FROM fac_v_webgebruiker "
                        +   "  WHERE fac_functie_key = " + authparams.autfunctionkey
                        +   "    AND prs_perslid_key="+user_key + ")");
        }
        if (has3D["FCLT_3D_LOCATIE_KEY"] && authparams.ALGreadlevel > -1)
        {
            wheres.push(" FCLT_3D_LOCATIE_KEY IN"
                      + "  (SELECT alg_locatie_key FROM fac_v_my_locations "
                      + "    WHERE niveau ="+authparams.ALGreadlevel
                      + "      AND prs_perslid_key="+user_key+")");

        }
        if (has3D["FCLT_3D_GEBOUW_KEY"] && authparams.ALGreadlevel > -1)
        {
            wheres.push(" FCLT_3D_GEBOUW_KEY IN"
                      + "  (SELECT alg_gebouw_key FROM fac_v_my_buildings "
                      + "    WHERE niveau ="+authparams.ALGreadlevel
                      + "      AND prs_perslid_key="+user_key+")");

        }
        if (has3D["FCLT_3D_AFDELING_KEY"] && authparams.PRSreadlevel > 0)
        {
            wheres.push(" FCLT_3D_AFDELING_KEY IN"
                      + "    (SELECT prs_afdeling_key FROM prs_v_afdeling_familie a"
                      + "      WHERE a.prs_afdeling_elder_key IN"
                      + "        (SELECT aa.prs_afdeling_elder_key"
                      + "           FROM prs_v_afdeling_familie aa"
                      + "          WHERE aa.prs_afdeling_key = " + user.prs_afdeling_key()
                      + "            AND aa.niveau = " + authparams.PRSreadlevel + "))");
        }
        if (has3D["FCLT_3D_USER_KEY"] )
        {
            wheres.push(" FCLT_3D_USER_KEY = " + user_key);
        }
    }

    if (requestData.limit)
    	  wheres.push("ROWNUM <= " + requestData.limit);

    var where="";
    if (wheres.length)
        where = "WHERE " + wheres.join(" AND ");

    var resultnode = GetReq+'Response';  // Dit is volgens onze eigen conventie zo
    var stylesheet = API.apidata.stylesheet;

    var key = -1;
    var xtrakey = -1;
    var sql_params = safe.quoted_sql(viewname) + ", "
                   + key + " , "
                   + safe.quoted_sql(customerId) + ", "
                   + "'$AspSession$', "
                   + xtrakey + ", "
                   + safe.quoted_sql(where)
                   + ", " + safe.quoted_sql(resultnode)
                   + ", " + safe.quoted_sql(resnodename);
    if (String(Oracle.RealConnection.Properties("Provider Name")).match(/OraOLEDB/i))
    {
        var sql = "SELECT xml.make_view_xml2(" + sql_params + ") xml_blob FROM dual";
        var oRs = Oracle.Execute(sql);
        var xml_content = oRs("xml_blob").Value;
		oRs.Close();
        __Log("XML blob is {0} karakters.".format(xml_content.length));
    }
    else // MSDAORA kan niet rechtstreeks BLOB's ophalen
    {
        sql = "BEGIN xml.make_view_xml(" + sql_params + "); END;";
        Oracle.Execute(sql);
        var xml_content = get_fac_xml('$AspSession$');
    }

    if (API.apidata.loglevel) __Log2File(xml_content, API.APIname + "_DATA");

    if (asJSON)
        Response.ContentType = "application/json";
    else
        Response.ContentType = "text/xml";

    Response.AddHeader("Access-Control-Allow-Origin", "*"); // Opdat FireFox cross-domain toestaat

    // Als er geen afwijkende xsl is gedefinieerd dan leveren we gewoon generiek de rapport_data node
    // op van het resultaat,dus maar zonder de metadata, dus alleen de facilitor/[resultnode]/
    if (stylesheet == null)
    {
        var xmldoc = new ActiveXObject("Msxml2.DOMDocument.6.0");
        xmldoc.async = false;
        xmldoc.loadXML(xml_content);
        if (xmldoc.parseError.errorCode != 0)
        {
            var myErr = xmldoc.parseError;
            throw("Error in xmlfile: " + myErr.reason);
        }
        else
        {
            var resultdoc = xmldoc.getElementsByTagName(resultnode)[0];
            if (resultdoc)
            {
                if (asJSON)
                {
                    var antwoord = JSON.stringify(xmlToJson(resultdoc),null, getQParam("pretty","0")=="1"?2:0);
                }
                else
                    var antwoord = resultdoc.xml;

                var oCrypto = new ActiveXObject("SLNKDWF.Crypto");
                var eTag = '"' + oCrypto.hex_sha1(antwoord).toLowerCase() + '"';
                Response.AddHeader("ETag", eTag);
                if (Request.ServerVariables("HTTP_IF_NONE_MATCH") == eTag)
                {   // We hebben een match!
                    Response.Clear();
                    Response.Status = "304 Not modified";
                    Response.End;
                }
                if (API.apidata.loglevel) __Log2File(antwoord, API.APIname + "_OUT");
                Response.write(antwoord);
            }
        }
    }
    else
    {
        // die req-parameter is waarschijnlijk vrij zinloos
        var params = { mode: req };
        if (API.apidata.loglevel)
            params.log_postfix = API.APIname + "_OUT";
        STR2Stream(xml_content, stylesheet, Response, params);
    }
}
catch (e)
{
    API.error(typeof e == "string"? e : e.description);
}
%>