<% /* $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 */ %> <% 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); // iPad/IPhone uploaden directe foto altijd als 'Image.jpg' wat erg lastig is // In dat geval bestandsnaam aanpassen. var newfilename = renameIphoneIpadFiles(safefilename, i); if (newfilename) { if (i == 0) jsondata.value = newfilename; // Gelijk aan het eerste bestand. jsondata.attachments[i] = newfilename; safefilename = newfilename; } 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 {token: tmpfolder, jsondata: jsondata}; // Tijdelijke folder en attachment data teruggeven. Bestandsnaam kan aangepast zijn. } 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; } %>