<% /*
    $Revision$
    $Id$

    File:           model_custom_fields.inc

    Description:    Flexkenmerken model
    Context:        Er is géén eigen api_custom_fields
                    We kunnen alleen via een include van een parent die dan ook
                    vooral alle autorisatiecontrole voor ons kan verzorgen

    Notes:          Voor de GET gebruiken we getSqlFlex uit resultset_flex
                    Lekker gemakkelijk hoewel hij niet 100% de volgnummer beperkingen volgt

                    Voor de PUT/POST/DELETE gebruiken we de functies van PACKAGE flx

*/

%>
<!-- #include file="../Shared/kenmerk_common.inc" -->
<!-- #include file="../Shared/resultset_flex.inc"-->
<%

function model_custom_fields(formodel, flexModel, flexParams)
{
    var flexModule = flexModel.module;

    flexParams         = flexParams || {};
    this.module        = flexModule;
    this.records_name  = "custom_fields";
    this.record_name   = "custom_field";
    this.table         = "flex";
    this.primary       = "kenmerk_key";
    this.records_title = L("custom_field_m");
    this.record_title  =  L("custom_field");
    this.keyfield      =  "propertyid"; // api2/process_includes werkt nu iets anders

    // getSqlFlex heeft bij multi-language 'user' al nodig voor de lcl's
    // daarom hieronder een indirecte/ delayed fntablesql
    this.fntablesql = function _fntablesql()
        {
            var sqlModule = flexModule;
            if (flexModule == 'MLD' && flexParams.pNiveau == 'O')
                sqlModule = 'OPDR';
            if (flexModule == 'FIN' && flexParams.pNiveau == 'R')
                sqlModule = 'FINR';
            var theSqlFlex = getSqlFlex(sqlModule, formodel.primary, flexParams);
            // enigszins undocumented maar met parameter '&custom_fields=' kun je het aantal opgeleverde velden beperken
            // (net zoals je met '&fields=' de gewone vaste velden kunt beperken)
            var custom_fields = getQParamIntArray("custom_fields", []);
            if (custom_fields.length)
                theSqlFlex = "SELECT * FROM ({0}) WHERE kenmerk_key IN ({1})".format(theSqlFlex, custom_fields.join(", "))
            return "(" + theSqlFlex + ") flex";
        }

    this.fnkenmerktablesql = function _fnkenmerktablesql()
        {
            var sqlModule = flexModule;
            if (flexModule == 'MLD' && flexParams.pNiveau == 'O')
                sqlModule = 'OPDR';
            if (flexModule == 'FIN' && flexParams.pNiveau == 'R')
                sqlModule = 'FINR';

            var theSqlFlex = "SELECT * FROM ({0}) ".format(getSqlFlex(sqlModule, formodel.primary, flexParams));
            return theSqlFlex;
        }

    this.fields = {
        // De echte unieke waarde "id" is bez_kenmerkwaarde_key.
        // We doen echter alles op basis van de samengestelde kenmerk_key + parent_key
        // De naam van dat veld weten we hier even niet echter
        // en hebben we ook niet nodig
        "propertyid": {
            "dbs": "kenmerk_key",
            "label": L("lcl_key"),
            "typ": "key"
        },
        "value": {
            "dbs": "waarde",
            "label": L("lcl_fac_value"),
            "typ": "varchar"
        },
        "type": {
            "dbs": "kenmerktype",
            "label": L("mgt_srtkenmerk_kenmerktype"),
            "typ": "varchar"
        },
        "sequence": {
            "dbs": "volgnummer",
            "label": L("mgt_kenmerk_volgnummer"),
            "len": 3,
            "typ": "number" },
        "label": {
            "dbs": "omschrijving",
            "label": L("mgt_srtkenmerk_label"),
            "typ": "varchar"
        },
        "flexparentkey": {
            "dbs": "flexparentkey",
            "typ": "key"
        },
        // TODO: record.flexparentid wordt er uitgestript vanwege niet teruglinken naar de parent
        //       we hebben hem echter wel nodig. Dit moet beter....
        "xflexparentkey": {
            "dbs": "flexparentkey",
            "typ": "key",
            "sql": "flexparentkey"
        }
    };

    this.get_file_info = function (params, record, fileparams)
    {
        var flexparam = flexProps(flexModule, record.xflexparentkey,
                                  String(record.propertyid), flexParams.pNiveau,
                                  fileparams);
        var attachments = [];
        for (var f in flexparam.files)
        {
            var file = flexparam.files[f];
            var attachment = { name: file.name,
                               date: file.date,
                               size: file.size };
            if (file.deepurl) attachment.content_url = file.deepurl;
            if (file.token)   attachment.token = file.token;
            if (file.key)     attachment.id = file.key;
            if (file.digest)  attachment.digest = file.digest;
            switch (params.filter.fileencoding) // De enige twee encodings die we ondersteunen
            {
                case "base64":
                    attachment.content_base64 = file.data;
                    break;
                case "hex":
                    attachment.content_hex = file.data;
                    break;
            }
            attachments.push(attachment);
        };
        return attachments;
    }
    // Deze functie wordt na de GET aangeroepen. De bijlagen zijn zo afwijkend
    // dat ik dat niet fatsoenlijk in 'fields' verwerkt kreeg
    this.post_get = function (params, record)
        {
            if (record.type == 'F' || record.type == 'M')
            {
                var fileparams = { getFiles: true, getFileEncoded: params.filter.fileencoding, api2name: formodel.records_name };
                record.attachments = this.get_file_info(params, record, fileparams);
            }
            delete record["xflexparentkey"]; // nu niet meer nodig
        }
    // Voor flexbijlagen kennen we een duale interface
    // Enerzijds zitten ze gewoon als attachements-object in de JSON
    // die een GET van custom_fields oplevert
    // Anderzijds zijn ze met GET/PUT/POST/DELETE rechtstreeks te benaderen onder
    //      /api2/visitors/1234/attachments/1000/test.jpg
    this.streamattachment = function(filter, flexfields)
    {
        for (var fid in flexfields)
        {
            if (flexfields[fid].propertyid == filter.subfolder && flexfields[fid].attachments)
            {
                for (var i = 0; i < flexfields[fid].attachments.length; i++)
                {
                    var att = flexfields[fid].attachments[i];
                    if (att.name.toLowerCase() == filter.filename.toLowerCase()) // Gevonden!!!!
                    {
                        var sql = "SELECT fac_bijlagen_disk_directory"
                                + "     , fac_bijlagen_filename"
                                + "  FROM fac_v_bijlagen"
                                + " WHERE fac_bijlagen_key = " + att.id;
                        var oRs = Oracle.Execute(sql);
                        vAttachPath = S("flexFilesPath") + "/" + oRs("fac_bijlagen_disk_directory").Value;
                        vFileName = oRs("fac_bijlagen_filename").Value;
                        oRs.Close();

                        // In de praktijk (b)lijkt IE de ETag niet te sturen voor downloads
                        // maar wij ondersteunen het in ieder geval wel.
                        var eTag = '"' + att.digest + '"';
                        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;
                        }
                        var extension = vFileName.split('.').pop();
                        var mime = S("flex_mimetypes")[extension]; // Sommige mimetypes zijn bekend
                        // Content-Type: image/jpeg; Charset=utf-8
                        // Eigenlijk moet de Charset er alleen bij text/.... bij maar ik krijg die er niet netjes af
                        // IE, FF en Chrome doen er in ieder geval niet moeilijk over
                        //if (mime)
                        //    Response.Charset = ''; /
                        StreamFile(vAttachPath, vFileName, { mime: mime || "application/octet-stream"});
                    }
                }
            }
        }
        __Log(filter);
        Response.Status = "404 Not Found";
    }

    if (flexModel) // nog even niet voor MLD
    this.REST_PUT = function (params, jsondata, the_key, parent_key) /* update custom_field */
    {   // Let op: parameter the_key is de kenmerk_key, niet een een kenmerkwaarde_key
        // Merk op dat flexProps ook wel het type oplevert. Ik wil echter migreren naar modellen
        var kenmerkdata = api2.GET(flexModel, jsondata.propertyid);
        if (!kenmerkdata) // PNBR test
        {
            __DoLog("Missing kenmerkdata", "#F00");
            __DoLog(params);
            __DoLog(jsondata);
            __DoLog(the_key);
            __DoLog(parent_key);
        }
        // Als je op de volgende regel een 'Object expected' krijgt is er
        // gesubmit met een kenmerk_key welke niet bestaat
        // Ik weet nog niet of daar een 'Unautorized', 'Bad request' of 'Stil negeren' van toepassing is
        // Vooralsnog maar even ongewijzigd er hard uitklappen
        if ("attributetype" in kenmerkdata)
        {
            var typ = kenmerkdata.attributetype.id;
        }
        else
        {
            var typedata = flexModel.getPropertyType(kenmerkdata); // getPropertyType moet gedefinieerd zijn
            var typ = typedata.attributetype.id;
        }
        if (typ == "F" || typ == "M" || typ == "E")
        {
            if (jsondata.attachments)
            {   // Alleen vooraanroep vanuit API2.
                if (!(parent_key > 0))
                    var tmpfolder =  safe.filename(shared.random(32));
                var flexparams = flexProps(this.module, parent_key, the_key, flexParams.pNiveau, { tmpfolder: tmpfolder });
                // TODO: bij type F zorgen dat er één file overblijft
                for (var i = 0; i < jsondata.attachments.length; i++)
                {
                    var attachment = jsondata.attachments[i];
                    var safefilename = safe.filename(attachment.name);
                    if (flexparams.isAllowedName(safefilename))
                    {
                        CreateFullPath(flexparams.AttachPath);
                        if ("datastream" in attachment) // PUT op /api2/visitors/1234/attachments/1000/test.jpg
                        {
                            __Log("Saving datastream file to {0}".format(flexparams.AttachPath + safefilename));
                            attachment.datastream.SaveToFile(safe.UNC(flexparams.AttachPath + safefilename), 2); // adSaveCreateOverWrite
                        }
                        else if ("content_base64" in attachment || "content_hex" in attachment)
                        {
                            var fileencoding = "content_base64" in attachment?"bin.base64":"bin.hex";
                            encodedString2File(flexparams.AttachPath + safefilename,
                                               attachment.content_base64 || attachment.content_hex,
                                               fileencoding);
                        }
                        // Toevoegen bijlage/bestand tracken.
                        trackBijlagen(this.module, parent_key, flexparams, {safefilename: [safefilename]}, "upload");
                        if (flexparams.fac_bijlagen)
                        {
                          var oCrypto = new ActiveXObject("SLNKDWF.Crypto");
                          var fso = Server.CreateObject("Scripting.FileSystemObject")
                          var sql = "BEGIN "
                                  + "  flx.setflexbijlage"
                                          + "(" + safe.quoted_sql(this.module)    // MLD | RES | ....
                                          + ", " + the_key // kenmerk
                                          + ", " + parent_key
                                          + ", " + safe.quoted_sql(flexparams.AttachSubPath)
                                          + ", " + "NULL" // diskfilename
                                          + ", " + safe.quoted_sql(safefilename)
                                          + ", " + fso.GetFile(flexparams.AttachPath + safefilename).Size
                                          + ", " + "SYSDATE"
                                          + ", " + safe.quoted_sql(oCrypto.hex_sha1_file(flexparams.AttachPath + safefilename))
                                          + ");"
                                  + " END;";
                          Oracle.Execute(sql);
                        }
                    }
                    else
                    {
                        api2.error(422, L("lcl_shared_file_ext_not_allowed"));
                    }
                }
            }
        }

        if (typ != "M" && parent_key > 0)
        {
          var setFlexModule = this.module;
          if (this.module == 'MLD' && flexParams.pNiveau == 'O')
              setFlexModule = 'OPD';
          if (this.module == 'FIN' && flexParams.pNiveau == 'R')
              setFlexModule = 'FINR';
          var sql = "BEGIN flx.setflex({0}".format(safe.quoted_sql(setFlexModule))
                  + "                , {0}".format(the_key) // == jsondata.propertyid
                  + "                , {0}".format(parent_key)
                  + "                , {0}".format(this.module == "MLD" || this.module == "FIN"? "NULL" : safe.quoted_sql(flexParams.pNiveau))
                  + "                , {0});".format(safe.quoted_sql(jsondata.value))
                  + "END;"
          var err = Oracle.Execute(sql, true);
          if (err.friendlyMsg)
              api2.error(400, err.friendlyMsg);
        }
        return tmpfolder;
    }

    if (flexModel) // nog even niet voor MLD
    this.REST_POST = function (params, jsondata, parent_key) /* add custom_field */
    {   // Voor flexvelden is er geen verschil tusen 'toevoegen' en 'bijwerken'
        return this.REST_PUT(params, jsondata, jsondata.propertyid, parent_key);
    }

    this.disabled_REST_DELETE = function (params, the_key) /* remove custom_field */
    {
        // custom_fields zijn ietwat afwijkend
        // 'verwijderen' doe je door de waarde op NULL te zetten (net zoals met vaste velden)
        // Anders loop je risico dat bij een update velden die niet in de include=custom_fields
        // zitten (omdat je er gewoon geen rechten op had) zo maar verwijderd worden
        // TODO: flexfiles wel kunnen deleten
    }

    this.list = { columns: ["propertyid", "label", "value"] }

    // Merk op: géén eigen REST_GET
    // We werken toch alleen via een include van een parent die dan ook
    // vooral alle autorisatiecontrole voor ons kan verzorgen
}

function set_custom_field(jsondata, kenmerk_key, val, typ)
{
    if (!("custom_fields" in jsondata))
        jsondata.custom_fields = [];
    for (var i=0; i< jsondata.custom_fields.length; i++)
    {
        if (jsondata.custom_fields[i].propertyid == kenmerk_key)
        {
            jsondata.custom_fields[i].value = val;
            return; // direct klaar
        }
    }
    // JGL: is het volgende wel nodig? Opdrachten gingen via saveFlexKenmerken maar dat is achterhaald?
    if (!typ)
        abort_with_warning("Cannot set_custom_field key '{0}' without type".format(kenmerk_key));

    jsondata.custom_fields.push({ propertyid: kenmerk_key, value: val, type: typ });
}

function get_custom_field(jsondata, kenmerk_key)
{
    if (!("custom_fields" in jsondata))
        return null;
    for (var i=0; i< jsondata.custom_fields.length; i++)
    {
        if (jsondata.custom_fields[i].propertyid == kenmerk_key)
        {
            return jsondata.custom_fields[i].value; // direct klaar
        }
    }
    return null;
}
%>