UNPKG

jsharmony

Version:

Rapid Application Development (RAD) Platform for Node.js Database Application Development

747 lines (692 loc) 32.5 kB
/* Copyright 2017 apHarmony This file is part of jsHarmony. jsHarmony is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. jsHarmony is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this package. If not, see <http://www.gnu.org/licenses/>. */ var _ = require('lodash'); var Helper = require('./lib/Helper.js'); var ejs = require('ejs'); var ejsext = require('./lib/ejsext.js'); var async = require('async'); /* --------------------------- Return model data to client --------------------------- Current server-side dependencies: > toptitle > tabs / bindings > buttons > helpurl / helpurl_onclick > breadcrumbs > field.serverejs > link -> jsh.getURL > onclick -> jsh.getURL_onclick > jsh.getAuxFields * */ function AppSrvModel(appsrv) { this.AppSrv = appsrv; this.srcfiles = {}; this.initialized_srcfiles = {}; this.loadEJS(); } /******************* | LOAD EJS | *******************/ AppSrvModel.prototype.loadEJS = function () { var jsh = this.AppSrv.jsh; this.srcfiles = { 'jsh_form': jsh.getEJS('jsh_client_form'), 'jsh_embed.js': jsh.getEJS('jsh_client_embed.js'), 'jsh_controls': jsh.getEJS('jsh_client_controls'), 'jsh_tabs_bottom': jsh.getEJS('jsh_client_tabs_bottom'), 'jsh_tabs_top': jsh.getEJS('jsh_client_tabs_top'), 'jsh_tabs_controls': jsh.getEJS('jsh_client_tabs_controls'), 'jsh_page.js': jsh.getEJS('jsh_client_page.js'), 'jsh_form.js': jsh.getEJS('jsh_client_form.js'), 'jsh_form.js.datamodel': jsh.getEJS('jsh_client_form.js.datamodel'), 'jsh_form.js.single': jsh.getEJS('jsh_client_form.js.single'), 'jsh_form.js.multiple': jsh.getEJS('jsh_client_form.js.multiple'), 'jsh_grid': jsh.getEJS('jsh_client_grid'), 'jsh_grid.js': jsh.getEJS('jsh_client_grid.js'), 'jsh_grid.js.datamodel': jsh.getEJS('jsh_client_grid.js.datamodel'), 'jsh_multisel': jsh.getEJS('jsh_client_multisel'), 'jsh_multisel.js': jsh.getEJS('jsh_client_multisel.js'), 'jsh_multisel.js.datamodel': jsh.getEJS('jsh_client_multisel.js.datamodel'), 'jsh_exec.js': jsh.getEJS('jsh_client_exec.js'), 'jsh_exec.js.datamodel': jsh.getEJS('jsh_client_exec.js.datamodel'), 'jsh_buttons': jsh.getEJS('jsh_client_buttons'), }; }; AppSrvModel.prototype.getSrcFiles = function(){ for (var sname in this.srcfiles) { if(!(sname in this.initialized_srcfiles)){ this.srcfiles[sname] = this.removeEmptyBytes(this.srcfiles[sname]); this.initialized_srcfiles[sname] = true; } } return this.srcfiles; }; AppSrvModel.prototype.removeEmptyBytes = function(str){ //var rslt = str.replace(/div/g, ""); //var rslt = str.replace(/\p{65279}/g, ""); if(str === null || (typeof str == 'undefined')) str = ''; var rslt = str.toString().replace(/\uFEFF/g, ''); return rslt; }; AppSrvModel.prototype.GetModel = function (req, res, fullmodelid) { var _this = this; var jsh = this.AppSrv.jsh; var model = jsh.getModel(req, fullmodelid); if (!Helper.hasModelAction(req, model, 'B')) { Helper.GenError(req, res, -11, jsh._tP('Invalid Model Access for @fullmodelid', { fullmodelid })); return; } req.curtabs = jsh.getTabs(req, model); req.TopModel = fullmodelid; if(!('action' in req.query) && (model.unbound || model.nokey)) req.query.action = 'update'; _this.genClientModel(req, res, fullmodelid, { topmost: true, parentBindings: null, sourceModel: null, onComplete: function(rslt){ if(_.isString(rslt)){ _this.genClientModel(req, res, 'jsHarmony/_BASE_HTML_MESSAGE', { topmost: true, parentBindings: null, sourceModel: null, onComplete: function(model){ if(_.isString(model)) return res.end(model); model = _.extend(model, { id: fullmodelid, caption: ['',fullmodelid,fullmodelid], title: fullmodelid, helpurl: '', helpurl_onclick: '', ejs: rslt }); res.type('json'); res.end(JSON.stringify(model)); } }); return; } else { res.type('json'); res.end(JSON.stringify(rslt)); } } }); }; AppSrvModel.prototype.GetChildModel = function (req, res, modelid, options) { options = _.extend({ topmost: undefined, parentBindings: undefined, sourceModel: undefined, targetperm: undefined, onComplete: undefined, }, options); var _this = this; var jsh = this.AppSrv.jsh; Helper.execif(!options.topmost && req && req.jshsite && req.jshsite.processCustomRouting, function(f){ req.jshsite.processCustomRouting('model_child', req, res, jsh, modelid, function(){ f(); }); }, function(){ _this.genClientModel(req, res, modelid, options); } ); }; AppSrvModel.prototype.genClientModel = function (req, res, modelid, options) { options = _.extend({ topmost: undefined, parentBindings: undefined, sourceModel: undefined, targetperm: undefined, onComplete: undefined, }, options); var _this = this; var jsh = this.AppSrv.jsh; var model = jsh.getModel(req, modelid, options.sourceModel); if(!model) throw new Error('Model ID not found: ' + modelid); var fullmodelid = model.id; var onComplete = options.onComplete; var targetperm = ''; if(typeof options.targetperm != 'undefined') targetperm = options.targetperm; else { targetperm = 'B'; if(model.unbound) targetperm = 'U'; if ('action' in req.query) { if (req.query.action == 'insert') targetperm = 'I'; else if (req.query.action == 'update') targetperm = 'U'; else if (req.query.action == 'browse') targetperm = 'B'; else return onComplete('Invalid "action" in querystring'); } if (!Helper.hasModelAction(req, model, 'B'+targetperm)) { return onComplete('<div>***NO_ACCESS*** You do not have access to this form.</div>'); } } //Check if the bindings are based on the key value var allConstantBindings = true; _.each(options.parentBindings, function(value, key){ if(typeof jsh.getStaticBinding(value) == 'undefined') allConstantBindings = false; }); //If insert, and the model has any dynamic bindings, show message that the user needs to save first to edit the data if((targetperm=='I') && !Helper.hasModelAction(req, model, 'I')){ if(!allConstantBindings) { if(!options.topmost && _.includes(['exec','report','multisel'], model.layout)){ /* Do nothing */ } else { return onComplete('<div>Please save to manage '+model.caption[1]+' data.</div>'); } } } if(!req.query.action){ if(model.layout=='form'){ if(!model.nokey && !model.unbound){ var keys = _this.AppSrv.getKeys(model.fields); if(keys && keys.length){ return onComplete(jsh.RenderFormStarter(req, fullmodelid)); } } } else if((model.layout=='multisel')||(model.layout=='form-m')){ return onComplete(jsh.RenderFormStarter(req, fullmodelid)); } } var rslt = {}; copyValues(rslt, model, [ 'id', 'namespace', 'class', 'layout', 'caption', 'oninit', 'onload', 'onloadimmediate', 'oninsert', 'onupdate', 'oncommit', 'onvalidate', 'onloadstate', 'ongetstate', 'onrowbind', 'onrowunbind', 'getapi', 'ondestroy', 'onchange', 'js', 'jslib', 'hide_system_buttons', 'popup', 'rowclass', 'rowstyle', 'tabpanelstyle', 'tablestyle', 'tableclass', 'formstyle', 'formclass', 'sort', 'querystring', 'disableautoload', 'tabpos', 'templates', 'basetemplate', 'unbound', 'reselectafteredit','newrowposition','validationlevel','default_search','grid_expand_search','grid_rowcount', 'grid_require_search','grid_static','noresultsmessage','ejs','header','css','onecolumn','disable_csv_export', //Commit Level function(){ if(model.commitlevel){ if(model.commitlevel=='auto'){ if(!Helper.hasModelAction(req, model, 'IUD')) rslt.commitlevel = 'none'; else if(options.topmost) rslt.commitlevel = 'row'; else rslt.commitlevel = 'page'; } else rslt.commitlevel = model.commitlevel; } }, //Add Bindings function() { if(options.parentBindings) rslt.bindings = options.parentBindings; else if(model.bindings) rslt.bindings = model.bindings; }, //General Data function () { var module_namespace = ''; if(model.module){ var module = jsh.Modules[model.module]; if(module) module_namespace = module.namespace; } return { 'actions': ejsext.getActions(req, model, 'BIUD'), 'breadcrumbs': ejsext.BreadCrumbs(req, jsh, fullmodelid), 'module_namespace': module_namespace, 'module': model.module, }; }, //Generate Buttons function () { var rsltbuttons = []; var buttons = jsh.parseButtons(model.buttons); if (typeof buttons != 'undefined') for (var i = 0; i < buttons.length; i++) { var button = buttons[i]; var link_target = button['link']; var link_bindings = button['bindings']; var link_actions = button['actions']; var link_text = button['text'] || ''; var link_icon = button['icon']; var link_style = button['style']; var link_class = button['class']; var link_newline = button['nl'] ? 1 : 0; var link_group = button['group'] || ''; if (!ejsext.hasAction(req, model, link_actions)) continue; if('roles' in button) if (!ejsext.hasFieldAction(req, button, link_actions)) continue; //Helper.hasModelAction(req, model, 'B'+targetperm) var link_url = ''; var link_onclick = ''; if (link_target && link_target.substr(0, 3) == 'js:') { link_url = '#'; link_onclick = "var modelid = '" + fullmodelid + "'; "+link_target.substr(3)+'; return false;'; } else { var link_parsed = jsh.parseLink(link_target); var link_targetmodelid = link_parsed.modelid; var link_targetmodel = link_targetmodelid ? jsh.getModel(req, link_targetmodelid, model) : null; if(link_targetmodel){ link_targetmodelid = link_targetmodel.id; //Hide the button if the user does not have target access to the model var link_targetperm = 'B'; if(link_parsed.action=='insert') link_targetperm = 'I'; if(button.hide_when_target_inaccessible && !Helper.hasModelAction(req, link_targetmodel, link_targetperm)) continue; //Apply text in button caption link_text = link_text.replace(new RegExp('%%%CAPTION%%%', 'g'), link_targetmodel.caption[1]); link_text = link_text.replace(new RegExp('%%%CAPTIONS%%%', 'g'), link_targetmodel.caption[2]); //Add bindings, if applicable if(!link_bindings && _.isEmpty(link_parsed.keys)){ var link_bindingObj = { target: link_targetmodelid }; var link_binding_additionalFields = _.keys(req.forcequery).concat(_.keys(rslt.bindings)).concat(_.pullAll(_.keys(req.query),['action','tabs'])); if(link_parsed.action=='insert'){ link_bindings = jsh.AddAutomaticBindings(model, link_bindingObj, 'Button '+(link_text||link_target), { req: req, bindType: 'nonKeyFields', additionalFields: link_binding_additionalFields }); } else { link_bindings = jsh.AddAutomaticBindings(model, link_bindingObj, 'Button '+(link_text||link_target), { req: req, bindType: 'childKey' }); } } } //Generate link link_url = jsh.getURL(req, model, link_target, undefined, undefined, link_bindings); link_onclick = jsh.getURL_onclick(req, model, link_target); } link_text = link_text.replace(new RegExp('%%%CAPTION%%%', 'g'), model.caption[1]); link_text = link_text.replace(new RegExp('%%%CAPTIONS%%%', 'g'), model.caption[2]); var rsltbutton = { 'name': button.name, 'link': link_url, 'onclick': link_onclick, 'actions': link_actions, 'icon': link_icon, 'text': link_text, 'style': link_style, 'class': link_class, 'nl' : link_newline, 'group': link_group, }; rsltbuttons.push(rsltbutton); } return { 'buttons': rsltbuttons }; }, ]); if (jsh.Config.use_sample_data) rslt['sample_data'] = 1; if (!model._inherits || (model._inherits.length == 0)) rslt._basemodel = model.id; else rslt._basemodel = model._inherits[0]; //Define whether the model definition needs to be reselected after update rslt.modeltype = 'static'; if(req.jshlocal && (fullmodelid in req.jshlocal.Models)) rslt.modeltype = 'dynamic'; //Model uses onroute to customize properties else if(model.tabcode) rslt.modeltype = 'dynamic'; //Model has tabs calculated server-side var tabcode = null; var helpid = model.helpid; async.waterfall([ //Get tabcode, if applicable function(cb){ if(model.tabcode && (targetperm!='I')){ _this.AppSrv.getTabCode(req, res, fullmodelid, function(_tabcode){ tabcode = _tabcode; return cb(); }); } else return cb(); }, function(cb){ //Generate Tabs tabcode = (tabcode || '').toString(); if(('tabpos' in model) && model.tabs) { var tabbindings = {}; var basetab = req.curtabs[model.id]; if (!basetab) basetab = ''; var showtabs = []; var showmodels = []; for(let i=0; i<model.tabs.length;i++){ let tab = model.tabs[i]; let tabname = tab.name; //Initially, thought to disable hiding based on role, because // a. Roles should define effective permissions // b. "Hiding" based on roles is confusing to the developer and user. // This should be handled via inheritance instead //Then, added it back, because it is such a hassle to need to //redeclare inheritance for nested forms just to remove one tab //var tabmodel = jsh.getModel(req, tab.target, model); //if (!ejsext.hasAction(req, model, targetperm, tab.actions)) continue; var tabtargetperm = targetperm; if(targetperm=='U') tabtargetperm = 'BU'; if('roles' in tab) if (!ejsext.hasAction(req, tab, tabtargetperm)) continue; if('actions' in tab) if (!ejsext.hasAction(req, model, tab.actions, tabtargetperm)) continue; //if(!Helper.hasModelAction(req, tabmodel, 'B')) continue; if (tab.showcode) { if (_.includes(tab.showcode, tabcode)) { showtabs.push(tabname); showmodels.push(tab.target); } } else { showtabs.push(tabname); showmodels.push(tab.target); } } if(showtabs.length == 0) { //return Helper.GenError(req, res, -9, "No tabs available for display"); rslt.tabs = []; delete rslt.tabpos; return cb(); } if (!(model.id in req.curtabs) || !(_.includes(showmodels, req.curtabs[model.id]))) req.curtabs[model.id] = showmodels[0]; for(let i=0; i<model.tabs.length;i++){ let tab = model.tabs[i]; if (req.curtabs[model.id] == tab.target) { tabbindings = tab.bindings; break; } } //Override Help URL to that of first tab if(model.tabpos == 'top'){ var firsttabmodel = jsh.getModel(req, req.curtabs[model.id], model); if (firsttabmodel){ helpid = firsttabmodel.helpid; } } var rslttabs = []; for(let i=0; i<model.tabs.length;i++){ let tab = model.tabs[i]; let tabname = tab.name; if (!_.includes(showtabs, tabname)) continue; var acss = 'xtab xtab' + model.class; if (i == (model.tabs.length-1)) acss += ' last'; var linktabs = new Object(); var tabmodelid = tab.target; linktabs[model.id] = tabmodelid; var link = jsh.getURL(req, model, '', linktabs); var caption = tab.caption; var tab_selected = false; if (!caption) caption = tabname; if (req.curtabs[model.id] == tabmodelid){ acss += ' selected'; tab_selected = true; } else if (('action' in req.query) && (req.query.action == 'insert')) { link = '#'; acss += ' disabled'; } var rslttab = { 'acss': acss, 'link': link, 'name': tabname, 'caption': caption, 'selected': tab_selected, 'modelid': tabmodelid }; rslttabs.push(rslttab); } //Get value of current tab _this.GetChildModel(req, res, req.curtabs[model.id], { topmost: false, parentBindings: tabbindings, sourceModel: model, onComplete: function(curtabmodel){ rslt.tabs = rslttabs; rslt.curtabmodel = curtabmodel; return cb(); } }); } else return cb(); }, function(cb){ //Duplicate Model if (model.duplicate && ejsext.hasAction(req, model, 'I')) { var dmodelid = model.duplicate.target; var dmodel = jsh.getModel(req, dmodelid, model); if (!dmodel) { throw new Error('Duplicate Model ID not found: ' + dmodelid); } _this.GetChildModel(req, res, dmodelid, { topmost: false, parentBindings: model.duplicate.bindings, sourceModel: model, onComplete: function(dclientmodel){ if (!_.isString(dclientmodel)) { rslt.duplicate = {}; rslt.duplicate.target = dmodelid; rslt.duplicate.bindings = model.duplicate.bindings; rslt.duplicate.model = dclientmodel; rslt.duplicate.popupstyle = ''; if('button_text' in model.duplicate) rslt.duplicate.button_text = model.duplicate.button_text; if ('popup' in dmodel) rslt.duplicate.popupstyle = 'width: ' + dmodel.popup[0] + 'px; height: ' + dmodel.popup[1] + 'px;'; if(model.layout != 'grid') rslt.buttons.push({ 'link': '#', 'onclick': "\ if(jsh.XPage.HasUpdates()){ XExt.Alert('Please save changes before duplicating.'); return false; }\ jsh.XPage.promptDuplicate('" + dmodelid + "', '" + model.class + "_duplicate','Duplicate " + model.caption[1] + "', this);\ return false;", 'actions': 'I', 'icon': 'copy', 'text': (model.duplicate.button_text || 'Duplicate'), 'style': 'display:none;', 'class': 'duplicate' }); if ('link_on_success' in model.duplicate) { rslt.duplicate.link_on_success = jsh.getURL(req, model, model.duplicate.link_on_success, undefined, dmodel.fields); var ptarget = jsh.parseLink(model.duplicate.link_on_success); var duplicateparams = { resizable: 1, scrollbars: 1 }; if(ptarget.url){ /* Do nothing */ } else { var link_model = jsh.getModel(req, ptarget.modelid, model); if (!link_model) throw new Error('Link Model ' + ptarget.modelid + ' not found.'); if('popup' in link_model){ duplicateparams.width = link_model['popup'][0]; duplicateparams.height = link_model['popup'][1]; } } duplicateparams = _.extend(duplicateparams, ptarget.actionParams); var duplicateparamsarr = []; for(var key in duplicateparams) duplicateparamsarr.push(key+'='+duplicateparams[key]); rslt.duplicate.link_options_on_success = duplicateparamsarr.join(','); } } return cb(); } }); } else return cb(); }, //Resolve title, if applicable function(cb){ _this.AppSrv.getTitle(req, res, fullmodelid, (targetperm=='U'?'BU':targetperm), function(err, title){ if(typeof title !== 'undefined') rslt.title = (title||''); return cb(); }); }, //Help System function(cb){ req.jshsite.help(req, res, jsh, helpid, function(helpurl, helpurl_onclick){ rslt.helpurl = helpurl; rslt.helpurl_onclick = helpurl_onclick; return cb(); }); }, //SysConfig, using function(cb){ rslt._sysconfig = {}; copyValues(rslt._sysconfig, model._sysconfig, ['unbound_meta']); //Model Using rslt.using = []; if(model.using) rslt.using = rslt.using.concat(model.using); //Module Using if(model.module){ var module = jsh.Modules[model.module]; if(module && module.using) rslt.using = rslt.using.concat(module.using); } return cb(); }, //Set up fields function(cb){ if (options.topmost) { rslt['topmost'] = 1; rslt['menu'] = ''; copyValues(rslt, model, ['menu']); rslt['toptitle'] = model.id; if ('title' in rslt) rslt['toptitle'] = rslt.title; rslt['forcequery'] = req.forcequery; } if ('fields' in model) _this.copyModelFields(req,res,rslt,model,targetperm,function(fields){ rslt.fields = fields; return cb(); }); else return cb(); }, function (cb) { if(model.layout == 'grid'){ if(model.display_layouts === false){ rslt.current_display_layout_name = undefined; rslt.display_layouts = undefined; } else { var field_names = []; _.each(rslt.fields, function(field){ if(field.name) field_names.push(field.name); }); if(model.display_layouts){ rslt.display_layouts = {}; for(var display_layout_name in model.display_layouts){ var rslt_display_layout = { columns: [] }; var display_layout = model.display_layouts[display_layout_name]; rslt_display_layout.title = ('title' in display_layout) ? display_layout.title : display_layout_name; //Add only the fields that are in the rslt.fields array for(var i=0;i<display_layout.columns.length;i++){ var field_name = display_layout.columns[i].name; if(_.includes(field_names, field_name)){ rslt_display_layout.columns.push({ name: field_name }); } } rslt.display_layouts[display_layout_name] = rslt_display_layout; if(!rslt.current_display_layout_name) rslt.current_display_layout_name = display_layout_name; } } else { //Generate a "standard" current_display_layout_name var default_columns = []; _.each(rslt.fields,function(field){ if(Helper.hasAction(field.actions,'B') && (field.control !== 'hidden')) default_columns.push({'name':field.name}); }); rslt.display_layouts = { 'standard': { 'title': 'Standard', 'columns': default_columns } }; rslt.current_display_layout_name = 'standard'; } } } return cb(); }, ],function(err){ if(!rslt.fields) rslt.fields = []; //Return result if(onComplete) onComplete(rslt); }); }; AppSrvModel.prototype.copyModelFields = function (req, res, rslt, srcobj, targetperm, onComplete) { var jsh = this.AppSrv.jsh; var model = srcobj; var rsltfields = []; var firstsort = ((('sort' in model)&&model.sort.length)?model['sort'][0].substring(1):''); var _this = this; async.eachOfSeries(srcobj.fields, function(srcfield,i,cb){ var dstfield = {}; copyValues(dstfield, srcfield, [ 'name', 'key', 'control', 'caption', 'caption_ext', 'captionstyle', 'captionclass', 'captioncolon', 'nl', 'eol', 'type', 'length', 'value', 'controlclass', 'target', 'bindings', 'format', 'readonly', 'hints', 'always_editable', 'focus', 'locked_by_querystring', 'ongetvalue', 'onenabled', 'ondisabled', 'unbound', 'onchange', 'onclick', 'hidden', 'html', 'cellstyle', 'cellclass', 'lovkey', 'controlstyle', 'block', 'blockstyle', 'blockclass', 'nowrap', 'disable_sort', 'disable_search' ]); if (srcfield.popuplov) dstfield.popuplov = 1; if (srcfield.sqlsearchsound) dstfield.search_sound = 1; if (jsh.Config.use_sample_data && ('sample' in srcfield)) dstfield.sample = srcfield.sample; dstfield.controlparams = {}; if ('controlparams' in srcfield) { copyValues(dstfield.controlparams, srcfield.controlparams, [ 'download_button', 'preview_button', 'upload_button', 'delete_button', 'dateformat', 'item_context_menu', 'item_dropdown', 'expand_all', 'onmove', 'ondrop', 'ongetnodecontent', 'drag_anchor_settings', 'expand_to_selected', 'lazy_render', 'value_true', 'value_false', 'value_hidden', 'code_val', 'popupstyle', 'popupiconstyle', 'popup_copy_results', 'onpopup','base_readonly','grid_save_before_update','update_when_blank','htmlarea_config','show_thumbnail','preview_on_click','panelstyle','placeholder','icon', ]); if ('thumbnails' in srcfield.controlparams) for (var tname in srcfield.controlparams.thumbnails) { var thumb = srcfield.controlparams.thumbnails[tname]; if (thumb.resize) dstfield.controlparams.thumbnail_width = thumb.resize[0]; else if (thumb.crop) dstfield.controlparams.thumbnail_width = thumb.crop[0]; break; } else if('image' in srcfield.controlparams) { if (srcfield.controlparams.image.resize) dstfield.controlparams.thumbnail_width = srcfield.controlparams.image.resize[0]; else if (srcfield.controlparams.image.crop) dstfield.controlparams.thumbnail_width = srcfield.controlparams.image.crop[0]; } if (('insert_link' in srcfield.controlparams) && (srcfield.controlparams.insert_link)) { dstfield.controlparams.insert_link = jsh.getURL(req, model, srcfield.controlparams.insert_link, undefined, undefined, srcfield.bindings); dstfield.controlparams.insert_link_onclick = jsh.getURL_onclick(req, model, srcfield.controlparams.insert_link); } } if (('serverejs' in srcfield) && (srcfield.serverejs)) { dstfield.value = ejs.render(dstfield.value, { ejsext: ejsext, req: req, res: res, _: _, model: model, jsh: jsh }); } if (('link' in srcfield) && (srcfield.link)) { if (srcfield.link == 'select') { dstfield.link = jsh.getURL(req, model, srcfield.link + ':' + model.id, undefined, model.fields); dstfield.link_onclick = req.jshsite.instance+".XExt.popupSelect('"+ Helper.escapeJS(model.id) + "',this);return false;"; } else if (srcfield.link.substr(0,3)=='js:') { dstfield.link = '#'; dstfield.link_onclick = srcfield.link.substr(3) + '; return false;'; } else { dstfield.link = jsh.getURL(req, model, srcfield.link, undefined, model.fields); dstfield.link_onclick = jsh.getURL_onclick(req, model, srcfield.link); } if (((srcfield.control=='button') || (srcfield.control=='linkbutton')) && !('onclick' in srcfield)) { dstfield.onclick = dstfield.link_onclick; } } if ((model.layout == 'grid') || (model.layout == 'multisel')) { dstfield.sortclass = ((srcfield.name == firstsort)?((model['sort'][0].substring(0, 1) == '^')?'sortAsc':'sortDesc'):''); } if (srcfield.lov) { dstfield.lov = {}; copyValues(dstfield.lov, srcfield.lov, ['parent','parents','blank','showcode']); if (('code2' in srcfield.lov) || ('code2_sys' in srcfield.lov) || ('code2_app' in srcfield.lov) || ('sql2' in srcfield.lov)) dstfield.lov.duallov = 1; else if ('sqlmp' in srcfield.lov) dstfield.lov.multilov = 1; } if('default' in srcfield){ if(_.isString(srcfield.default) && (srcfield.default.substr(0,3)=='js:')) dstfield.default = srcfield.default; else if (_.isString(srcfield.default) || _.isNumber(srcfield.default) || _.isBoolean(srcfield.default)) dstfield.default = srcfield.default; } if ('actions' in srcfield) { if(srcfield.always_editable){ dstfield.actions = ejsext.getActions(req, { actions: 'BIUD', roles: model.roles }, 'BIUD'); if ('roles' in srcfield) dstfield.actions = ejsext.getActions(req, { actions: 'BIUD', roles: srcfield.roles }, dstfield.actions); } else { dstfield.actions = ejsext.getActions(req, model, srcfield.actions); if ('roles' in srcfield) dstfield.actions = ejsext.getActions(req, srcfield, dstfield.actions); } } dstfield.validate = jsh.GetClientValidator(req, model, srcfield, dstfield.actions); if (('control' in dstfield) && ((dstfield.control == 'subform') || (dstfield.popuplov))) { _this.GetChildModel(req, res, srcfield.target, { topmost: false, parentBindings: srcfield.bindings, sourceModel: model, targetperm: (dstfield.popuplov ? 'B' : undefined), onComplete: function(subform){ if(srcfield.control=='subform'){ //targetperm //field.actions (dstfield.actions //field.roles (applied to dstfield.actions) //subformModel.actions //subformModel.roles //subformModel.layout // //If targetperm==I // If dstfield.actions has I // If subformModel.layout == multisel, exec, report // If subformModel.actions && subformModel.roles has B/U // If subformModel.layout == form, form-m, grid // If subformModel.actions && subformModel.roles has B/I //If targetperm==B // If dstfield.actions has B // If subformModel.actions && subformModel.roles has B //If targetperm==U // If dstfield.actions has B/U // If subformModel.actions && subformModel.roles has B/U var parentFieldActions = srcfield.actions; if ('roles' in srcfield) parentFieldActions = ejsext.getActions(req, srcfield, parentFieldActions); if(!Helper.hasAction(parentFieldActions, (targetperm=='U'?'BU':targetperm))) return cb(); var subformmodel = jsh.getModel(req, srcfield.target, model.id); var subformtargetperm = targetperm; if(targetperm=='U') subformtargetperm = 'BU'; else if(targetperm=='I'){ if(_.includes(['multisel','exec','report'],subformmodel.layout)) subformtargetperm = 'BU'; else subformtargetperm = 'BI'; } if(!ejsext.hasAction(req, subformmodel, subformtargetperm)) return cb(); } dstfield.model = subform; rsltfields.push(dstfield); return cb(); } }); } else { rsltfields.push(dstfield); return cb(); } }, function(err){ if(onComplete) onComplete(rsltfields); }); }; function copyValues(destobj, srcobj, values) { for (var i = 0; i < values.length; i++) { var value = values[i]; if (_.isFunction(value)) { var fval = value(); if (typeof fval != 'undefined') _.extend(destobj, fval); } else if (value in srcobj) destobj[value] = srcobj[value]; } } module.exports = AppSrvModel;