json-object-editor
Version:
JOE the Json Object Editor | Platform Edition
597 lines (538 loc) • 28.1 kB
JavaScript
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();
});*/