UNPKG

json-object-editor

Version:

JOE the Json Object Editor | Platform Edition

597 lines (538 loc) 28.1 kB
var fs = require('fs'); function Apps(){ var moduleName = JOE.Utils.color('[apps] ','module'); var self = this; function formatBytes(bytes,pretty) { let mbs = bytes / (1024 * 1024); let gbs = null; let pref="mbs"; if(mbs > 1000){ gbs = (mbs/1000); pretty && (gbs = gbs.toFixed(2)); pref="gbs" } pretty && (mbs = mbs.toFixed(2)); return {mbs,gbs,pref}; } this.Cards = { _generate : function(cardtype,specs,cardtype_specs){ //console.log('generating card '+cardtype); var specs = $c.merge({ left:0, top:0, cssclass:'' }, specs||{}); var config_base = { cssclass:specs.cssclass, left:specs.left, top:specs.top }; var config; switch(cardtype){ case 'system_stats': config = { title:specs.title||'<small>JOE Platform v'+JOE.VERSION+'</small>', cssclass:'w2 h1', content:function(){ if(!_joe.Data || !_joe.Data.user){return 'loading data';} //console.log(_joe.Data.user); var uptime_message = ''; var uptimeMins = (Math.floor((new Date(__jsc.startDate)-new Date())/1000/60*-1)); if(uptimeMins < 120){ uptime_message = `uptime: <b>`+uptimeMins+` mins</b>`; }else if(uptimeMins < 60*24){ uptime_message = `uptime: <b>`+(uptimeMins/60).toFixed(2)+` hours</b>`; }else{ uptime_message = `uptime: <b>`+Math.floor(uptimeMins/60/24)+` days</b>`; } var message = '<div class="joe-half-col">'+ _joe.Data.user.length+' users <br/>'+ // ((__jsu && __jsu.schemas && __jsu.schemas.length)|| $c.itemCount(_joe.Data)+' schemas <br/>'+ (_joe.search({}).length)+ ' objects' +'</div>' +'<div class="joe-half-col">' +uptime_message //+(Math.floor((new Date(__jsc.startDate)-new Date())/1000/60*-1))+' mins uptime' +'<small><br/>using '+Math.round(__jsc.memory.heapTotal/1000000)+'mb | '+Math.round(__jsc.memory.free/1000000)+'mb |' +Math.round(__jsc.memory.total/1000000)+'mb' +'</div><joe-clear></joe-clear>' +'<div class="joe-half-col">' +'<joe-button onclick="_joe.Export.csv()" class="joe-button joe-iconed-button joe-grey-button joe-blue-button"> export CSV </joe-button>' +'</div>' +'<div class="joe-half-col">' if(_joe.User && _joe.User.role == 'super'){ message+='<joe-button onclick="_joe.SERVER.Cache.reload()" class="joe-button joe-iconed-button joe-grey-button joe-red-button joe-reload-button"> re-cache </joe-button>'; } +'</div><joe-clear></joe-clear>'; return message; } } break; case 'quick_add': config = { title:specs.title||'Quick Add', content:(specs.content||'')+cardtype_specs.schema.split(',').map(ct=>{ return `<joe-button color="orange" icon="plus" class="" schema="${ct}" style="float:none" action="new"> </joe-button> `; }).join('') /*+((_joe && _joe.schemas[cardtype_specs.schema] && _joe.schemas[cardtype_specs.schema].menuicon)||'')*/ , cssclass:specs.cssclass||'w1 h1' } break; case 'schema_links': config = { title:specs.title||'Quick Links', content:'<joe-button class="joe-button joe-orange-button joe-plus-button joe-iconed-button " style="float:none" \ onclick="_joe.Object.create(\''+cardtype_specs.schema+'\')">add '+cardtype_specs.schema+'</joe-button>', cssclass:'w3 h3' } break; case 'tabbed_panel': config = { title:specs.title||'Tabbed Panel', attributes:cardtype_specs, content:function(card){ var st = new Date().getTime(); try { var attributes = card.attributes || {}; var specs = { schemas:attributes.schemas, limit:attributes.limit, sortBy:attributes.sort|| attributes.sortBy, filters:attributes.filter || attributes.filters|| attributes.query } var html = "<capp-toggler>"; var objects = _joe.getData(specs); var toggles_html ='<capp-toggles>'; var toggled_html = '<capp-toggled>'; var tabs = _joe.propAsFuncOrValue(attributes.tabs); tabs.map(tab=>{ var objs = objects; let tabId = tab.id || tab.name; let tabName = tab.display||tab.name; let onclick = "capp.Toggle.toggle('"+tabId+"')" if(tab.filters){ objs=objs.where(tab.filters); } let count = objs.length; let objHtml = renderObjects(objs); let active = _joe.propAsFuncOrValue(tab.active)?'active':''; toggles_html+= '<capp-button style="width:calc(100% / '+tabs.length+');" class="'+active+'" data-toggle="'+tabId+'" onclick="'+onclick+'">' +tabName+' <capp-count>'+count+'<capp-count>' +'</capp-button>'; toggled_html+= '<capp-content class="'+active+'" data-toggle="'+tabId+'">' +objHtml +'</capp-content>' }) toggles_html+='</capp-toggles>'; toggled_html+='</capp-toggled>' html+=toggles_html + toggled_html; function renderObjects(objs){ var h = ''; objs.map(function(object){ let schemaName = object.itemtype; let joeSchema = _joe.schemas[schemaName]; onclick = 'onclick="goJoeItem(\''+object._id+'\',\''+schemaName+'\')"'; let coloring={stripecolor:joeSchema.stripeColor, bgcolor:joeSchema.bgColor, }; if(attributes.stripeOnly){ coloring={stripecolor:joeSchema.bgColor}; } h+=_joe.Render.fieldListItem(object, joeSchema.menuicon +'<joe-subtext>'+_joe.Utils.prettyPrintDTS(object.joeUpdated)+'</joe-subtext>' +'<joe-title>'+object.name+'</joe-title>'+object.info ,schemaName,coloring ); }); return h; } html += "</capp-toggler>"; var el = new Date().getTime() - st; return html; }catch(e){ return 'loading:'+e; } }, cssclass:specs.cssclass||'w3 h4' } break; case 'recently_updated': config = { title:specs.title||'Recently Updated Items', attributes:cardtype_specs, content:function(card){ var st = new Date().getTime(); try { var attributes = card.attributes || {}; var objects = []; if(attributes.schemas){ attributes.schemas.map(sc=>{ objects = objects.concat(_joe.Data[sc]); }) }else{ for(var d in _joe.Data){ objects = objects.concat(_joe.Data[d]); } } var monthago = moment().subtract(1,'months').format(); objects = objects.sortBy('!joeUpdated'); var html ='',onclick =''; var limit = attributes.limit || 10; var query = {joeUpdated:{$gt:monthago}}; if(attributes.filters){ Object.assign(query,_joe.propAsFuncOrValue(attributes.filters)); } console.log(query); var timedObjects = objects.where(query) || []; timedObjects.slice(0,limit).map(function(object){ let schema = object.itemtype; let joeSchema = _joe.schemas[schema]; onclick = 'onclick="goJoeItem(\''+object._id+'\',\''+schema+'\')"'; let coloring={stripecolor:joeSchema.stripeColor, bgcolor:joeSchema.bgColor, }; if(attributes.stripeOnly){ coloring={stripecolor:joeSchema.bgColor}; } html+=_joe.Render.fieldListItem(object, joeSchema.menuicon +'<joe-subtext>'+_joe.Utils.prettyPrintDTS(object.joeUpdated)+'</joe-subtext>' +'<joe-title>'+ (object.name || object.itemtype + (object.date ? ' ['+_joe.Utils.toDateString(new Date(object.date))+']':'') ) +'</joe-title>'+(object.info||'') ,schema,coloring ); }); var el = new Date().getTime() - st; return (html || 'no recent updates'); }catch(e){ return 'loading:'+e; } }, cssclass:specs.cssclass||'w3 h4' } break; case 'app_home': config = { //title:specs.title || 'schemas', title:specs.title || function(){ return (APPINFO && APPINFO.title+' schemas')||'App Home'; }, content:function(card){ console.log('running app_home'); var app = APPINFO || {}; var platform = (app.title == "Platform"); try{ var html = ((app.info && '<p>'+app.info+'</p>')||''); var hasdescription = (app.description)?'active':''; html+='<capp-toggles>' +'<capp-button class="" data-toggle="app" onclick="capp.Toggle.toggle(\'app\')">App</capp-button>'+ '<capp-button class="active" data-toggle="schemas" onclick="capp.Toggle.toggle(\'schemas\')">Schemas</capp-button>'+ '<capp-button class="" data-toggle="default_schemas" onclick="capp.Toggle.toggle(\'default_schemas\')">default schemas</capp-button>'+ '</capp-toggles>'; html+='<capp-toggled>' +'<capp-content class=" spaced" data-toggle="app">'+(app.description||'App description')+'</capp-content>'; var onclick,schemaObj,info,icon,schemas_html='',default_schemas_html='',temphtml,count,schemaname; __appCollections.map(function(s){ schemaObj = _joe.schemas[s]; if(!schemaObj){ console.log('schema '+s+ ' not found'); return; } icon = ''; schemaname = schemaObj.display || s; if(schemaObj.menuicon){ icon = '<capp-button-icon class="icon-80 fleft">'+schemaObj.menuicon+'</capp-button-icon>'; } info = (schemaObj.info && ('<div class="spaced">'+schemaObj.info+'</div>'))|| ''; onclick = 'onclick="goJoe(_joe.Data.'+s+',{schema:\''+s+'\'})"'; count = (_joe.Data[s] || []).length; temphtml = '<capp-button style="padding:0;" '+onclick+'>' +'<div style="float:left; text-align:center; width:80px;height:100px;line-height: 1em;">'+icon // +((count && '<div class="clear">'+count+'</div>')||'') +'</div>' +'<capp-content style="padding:0px 0px 5px 0;">' +'<joe-title class="striped">'+schemaname.replace('ion','ions').pluralize().replace('ionses','ions').capitalize() +((count &&' <span class="joe-subtext">['+count+']</span>')||'')+'</joe-title>' +info +'</capp-content></capp-button>'; if(//JOE.webconfig.default_schemas ['user','group','event','report','tag','status','workflow','list','notification','note','include','setting'] .indexOf(s) == -1){ schemas_html += temphtml; }else{ default_schemas_html += temphtml; } }) html+='<capp-content class="active no-padding" data-toggle="schemas">'+schemas_html+'</capp-content>'; html+='<capp-content data-toggle="default_schemas" class=" no-padding">'+default_schemas_html+'</capp-content>'; html+='</capp-toggled>'; return html; }catch(e){ console.log('app_home error: '+e); return e; } }, cssclass:specs.cssclass||'w3 h4' } /*config={ title:specs.title||'App Home', content:function(){ var html = '<h2>App Name</h2>App Description / Info'+ '<h3>Quick Links</h3>'; var onclick,schemaObj,info,icon; __collectionNames.map(function(s){ schemaObj = _joe.schemas[s]; icon = ''; if(schemaObj.menuicon){ icon = '<capp-button-icon>'+schemaObj.menuicon+'</capp-button-icon>'; } info = schemaObj.info || ''; onclick = 'onclick="goJoe(_joe.Data.'+s+',{schema:\''+s+'\'})"'; html += '<capp-button '+onclick+'><joe-title>'+s.pluralize().capitalize()+'</joe-title>' +icon +'<capp-content>'+info+'</capp-content></capp-button>'; }) return html; }, cssclass:'w3 h4' }*/ break; } return { type:'Card', config:$c.merge(config_base,config) }; }, systemStats: function(specs){ try{ return self.Cards._generate('system_stats',specs); }catch(e){ console.log('error rendering card for dashboard: '+e); } }, quickAdd: function(schema,specs,overwrites){ try{ return self.Cards._generate('quick_add',specs,{schema:schema,overwrites:overwrites}); }catch(e){ console.log('error rendering card for dashboard: '+e); } }, recentlyUpdated: function(specs,attributes){ try{ return self.Cards._generate('recently_updated',specs,attributes); }catch(e){ console.log('error rendering recently_updated card for dashboard: '+e); } }, tabbedPanel: function(specs,attributes){ try{ return self.Cards._generate('tabbed_panel',specs,attributes); }catch(e){ console.log('error rendering tabbed_panel card for dashboard: '+e); } }, schemaLinks: function(schemas,specs){ try{ return self.Cards._generate('schema_links',specs,{schemas:schemas}); }catch(e){ console.log('error rendering card for dashboard: '+e); } }, appHome:function(specs,schemas){ try{ return self.Cards._generate('app_home',specs,{schemas:schemas}); }catch(e){ console.log('error rendering card for dashboard: '+e); } } }; var apps = {}; this.collections=[]; var appFilePath = __dirname+'/../app-config.js'; //var appsModulePath = __dirname+'/Apps.js'; var internalPluginsDir = __dirname+'/../plugins/'; var internalAppsDir = __dirname+'/../apps/'; this.update = function(){ var uBM = new Benchmarker(); apps = {}; delete require.cache[require.resolve(appFilePath)]; var standard_apps = require(appFilePath); apps = standard_apps; //add internal folder of apps fs.readdirSync(internalAppsDir).map(function(sc){ logit(`${moduleName} ${sc} app file found`); self.load(sc); }) //add custom folder of apps fs.readdirSync(JOE.appsDir).map(function(sc){ self.load(sc); }) self.cache = apps; self.names = []; self.plugins = {} self.collections = []; var cols = []; for(var a in self.cache){ self.names.push[a]; //loadplugins var plugs = (self.cache[a].plugins || []); //console.log(a+' '+plugs.length); plugs.map(function(plug){ self.loadPlugin(plug); }) //get all collections cols = cols.concat(self.cache[a].collections); } cols.map(function(c){ if(this.collections.indexOf(c) == -1){ this.collections.push(c); } }) self.collections = self.collections.sort(); self.cache['joe'] = $c.merge ({},self.cache['joe']); self.cache['joe'].collections = self.collections; console.log(moduleName+$c.itemCount(self.cache)+' updated, '+$c.itemCount(self.plugins)+' plugins '+self.collections.length+' collections '+'in '+uBM.stop()+' secs'); } //app. this.get = function(appname){ if(!appname){ return apps; } return apps[appname] || {error:'no app: '+appname}; } this.load = function(a){ var app; var error = {error:'app: '+a+' not found'}; var apppath = null; if(a.indexOf('.js') == -1){a+= '.js';} app = error; try{ try{ //check user apps path apppath = JOE.appsDir + a; fs.accessSync(apppath, fs.F_OK); }catch(e){ //console.log(moduleName+'[error]'+e,app); apppath = null; try{ //check for internal app apppath = internalAppsDir + a; fs.accessSync(apppath, fs.F_OK); }catch(e){ //console.log('[apps][error]'+e,app); apppath = null; } } try{ delete require.cache[require.resolve(apppath)]; }catch(e){ console.log(moduleName+'ERROR DELETING APP: ') } if(!apppath){ console.log(moduleName+'[error] APP not found: ',app); } app = require(apppath); }catch(e){ console.log(e,app); } if(app && !app.error && app.collections){ apps[a.replace('.js','')] = app; }else{ console.log(moduleName+'error with APP: '+a); } return app; } this.loadPlugin = function(p){ //logit(moduleName+'loading plugin '+p); let _joeSrc=''; console.log(JOE.Utils.color('[plugin]', 'plugin'), 'loaded: '+p); var plugin; if(p.indexOf('.js') == -1){p+= '.js';} const candidateDirs = [ pluginsDir, internalPluginsDir ]; let pluginpath = null; let override = false; //for (let dir of candidateDirs) { try { let attempt = internalPluginsDir + p; fs.accessSync(attempt, fs.F_OK); pluginpath = attempt; } catch (e) { // continue } try { let attempt = pluginsDir + p; fs.accessSync(attempt, fs.F_OK); if(pluginpath){ override = true; } pluginpath = attempt; } catch (e) { // continue } // } // try{ // //first try the pluginsDir, then the internalPluginsDir // var pluginpath = internalPluginsDir + p; // fs.accessSync(pluginpath, fs.F_OK); // }catch(e){ // var pluginpath = pluginsDir + p; // } try{ try{ delete require.cache[require.resolve(pluginpath)]; }catch(e){ console.log(moduleName+'[error] '+p+' not deleted'); } plugin = require(pluginpath); _joeSrc = pluginpath; }catch(e){ plugin = {error:'plugin '+p+' not found'}; console.log(e,plugin); } plugin._pathname = _joeSrc; plugin._override = override; self.plugins[p.replace('.js','')] = plugin; return plugin; } // this.updateApps = function(what,file,more){ // console.log(file+ ' '+what+'!'); // delete require.cache[require.resolve(appsModulePath)]; // console.log(appsModulePath); // JOE.Apps = require(appsModulePath); // } JOE.appsDir = appsDir = JOE.appDir+'/'+JOE.webconfig.appsDir+'/'; JOE.Utils.setupFileFolder(appsDir,'apps',self.update); fs.watch(appFilePath,self.update); JOE.pluginsDir = pluginsDir = JOE.appDir+'/'+JOE.webconfig.pluginsDir+'/'; JOE.Utils.setupFileFolder(pluginsDir,'plugins',self.update); fs.watch(internalAppsDir,self.update); fs.watch(internalPluginsDir,self.update); JOE.Apps = this; this.update(); return this; } module.exports = Apps(); /*server.get('/API/app/:names',auth,function(req,res){ var appname = req.params.appname||'joe'; var appspath = serverPath+'Apps.js'; delete require.cache[require.resolve(appspath)]; JOE.Apps = require(appspath);//JOE.webconfig.apps; var Apps = JOE.Apps; res.json(); });*/