parent
99ea870ebc
commit
4a342ba2fa
|
@ -91,6 +91,7 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
|
|||
position : { row : mci.position[0], col : mci.position[1] },
|
||||
};
|
||||
|
||||
// :TODO: These should use setPropertyValue()!
|
||||
function setOption(pos, name) {
|
||||
if(mci.args.length > pos && mci.args[pos].length > 0) {
|
||||
options[name] = mci.args[pos];
|
||||
|
|
|
@ -40,29 +40,21 @@ function login(callingMenu, formData, extraArgs) {
|
|||
});
|
||||
},
|
||||
function recordLoginHistory(callback) {
|
||||
|
||||
userDb.serialize(function serialized() {
|
||||
userDb.run(
|
||||
'INSERT INTO user_login_history (user_id, user_name, timestamp) ' +
|
||||
'VALUES(?, ?, ?);', [ user.userId, user.username, now.toISOString() ], function inserted(err) {
|
||||
callback(err);
|
||||
'VALUES(?, ?, ?);', [ user.userId, user.username, now.toISOString() ]
|
||||
);
|
||||
|
||||
// keep 30 days of records
|
||||
userDb.run(
|
||||
'DELETE FROM user_login_history ' +
|
||||
'WHERE timestamp <= DATETIME("now", "-30 day");'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
userDb.run(
|
||||
'DELETE FROM last_caller ' +
|
||||
'WHERE id NOT IN (' +
|
||||
' SELECT id ' +
|
||||
' FROM last_caller ' +
|
||||
' ORDER BY timestamp DESC ' +
|
||||
' LIMIT 100);');
|
||||
|
||||
userDb.run(
|
||||
'DELETE FROM last_caller ' +
|
||||
'WHERE user_id IN (' +
|
||||
' SELECT user_id ' +
|
||||
' ORDER BY timestamp DESC ' +
|
||||
'LIMIT 1;')
|
||||
*/
|
||||
callback(null);
|
||||
}
|
||||
],
|
||||
function complete(err, results) {
|
||||
|
|
|
@ -114,7 +114,6 @@ TextView.prototype.getData = function() {
|
|||
};
|
||||
|
||||
TextView.prototype.setText = function(text) {
|
||||
|
||||
var widthDelta = 0;
|
||||
if(this.text && this.text !== text) {
|
||||
widthDelta = Math.abs(this.text.length - text.length);
|
||||
|
|
11
core/view.js
11
core/view.js
|
@ -67,7 +67,10 @@ function View(options) {
|
|||
this.setDimension(options.dimens);
|
||||
this.autoScale = { height : false, width : false };
|
||||
} else {
|
||||
this.dimens = { width : 0, height : 0 };
|
||||
this.dimens = {
|
||||
width : options.width || 0,
|
||||
height : 0
|
||||
};
|
||||
}
|
||||
|
||||
// :TODO: Just use styleSGRx for these, e.g. styleSGR0, styleSGR1 = norm/focus
|
||||
|
@ -214,6 +217,12 @@ View.prototype.setPropertyValue = function(propName, value) {
|
|||
*/
|
||||
break;
|
||||
|
||||
case 'resizable' :
|
||||
if(_.isBoolean(value)) {
|
||||
this.resizable = value;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'argName' : this.submitArgName = value; break;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
var MenuModule = require('../core/menu_module.js').MenuModule;
|
||||
var userDb = require('../core/database.js').dbs.user;
|
||||
var ViewController = require('../core/view_controller.js').ViewController;
|
||||
var TextView = require('../core/text_view.js').TextView;
|
||||
|
||||
var util = require('util');
|
||||
var moment = require('moment');
|
||||
|
@ -13,17 +14,15 @@ var _ = require('lodash');
|
|||
|
||||
exports.moduleInfo = {
|
||||
name : 'Last Callers',
|
||||
desc : 'Last 10 callers to the system',
|
||||
desc : 'Last callers to the system',
|
||||
author : 'NuSkooler',
|
||||
packageName : 'codes.l33t.enigma.lastcallers'
|
||||
};
|
||||
|
||||
exports.getModule = LastCallersModule;
|
||||
|
||||
// :TODO:
|
||||
// * Order should be menu/theme defined
|
||||
// * Text needs overflow defined (optional), e.g. "..."
|
||||
// * Date/time format should default to theme short date + short time
|
||||
// *
|
||||
// * config.evenRowSGR (optional)
|
||||
|
||||
function LastCallersModule(options) {
|
||||
MenuModule.call(this, options);
|
||||
|
@ -31,66 +30,29 @@ function LastCallersModule(options) {
|
|||
var self = this;
|
||||
this.menuConfig = options.menuConfig;
|
||||
|
||||
this.menuMethods = {
|
||||
getLastCaller : function(formData, extraArgs) {
|
||||
//console.log(self.lastCallers[self.lastCallerIndex])
|
||||
var lc = self.lastCallers[self.lastCallerIndex++];
|
||||
var when = moment(lc.timestamp).format(self.menuConfig.config.dateTimeFormat);
|
||||
return util.format('%s %s %s %s', lc.name, lc.location, lc.affiliation, when);
|
||||
this.rows = 10;
|
||||
|
||||
if(this.menuConfig.config) {
|
||||
if(_.isNumber(this.menuConfig.config.rows)) {
|
||||
this.rows = Math.max(1, this.menuConfig.config.rows);
|
||||
}
|
||||
if(_.isString(this.menuConfig.config.dateTimeFormat)) {
|
||||
this.dateTimeFormat = this.menuConfig.config.dateTimeFormat;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
util.inherits(LastCallersModule, MenuModule);
|
||||
|
||||
/*
|
||||
LastCallersModule.prototype.enter = function(client) {
|
||||
LastCallersModule.super_.prototype.enter.call(this, client);
|
||||
|
||||
var self = this;
|
||||
self.lastCallers = [];
|
||||
self.lastCallerIndex = 0;
|
||||
|
||||
var userInfoStmt = userDb.prepare(
|
||||
'SELECT prop_name, prop_value ' +
|
||||
'FROM user_property ' +
|
||||
'WHERE user_id=? AND (prop_name=? OR prop_name=?);');
|
||||
|
||||
var caller;
|
||||
|
||||
userDb.each(
|
||||
'SELECT user_id, user_name, timestamp ' +
|
||||
'FROM user_login_history ' +
|
||||
'ORDER BY timestamp DESC ' +
|
||||
'LIMIT 10;',
|
||||
function userRows(err, userEntry) {
|
||||
caller = {
|
||||
who : userEntry.user_name,
|
||||
when : userEntry.timestamp,
|
||||
};
|
||||
|
||||
userInfoStmt.each( [ userEntry.user_id, 'location', 'affiliation' ], function propRow(err, propEntry) {
|
||||
if(!err) {
|
||||
caller[propEntry.prop_name] = propEntry.prop_value;
|
||||
// we need the client to init this for theming
|
||||
if(!_.isString(this.dateTimeFormat)) {
|
||||
this.dateTimeFormat = this.client.currentTheme.helpers.getDateFormat('short') +
|
||||
this.client.currentTheme.helpers.getTimeFormat('short');
|
||||
}
|
||||
}, function complete(err) {
|
||||
if(!err) {
|
||||
self.lastCallers.push(caller);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
*/
|
||||
|
||||
/*
|
||||
LastCallersModule.prototype.mciReady = function(mciData) {
|
||||
LastCallersModule.super_.prototype.mciReady.call(this, mciData);
|
||||
|
||||
// we do this so other modules can be both customized and still perform standard tasks
|
||||
LastCallersModule.super_.prototype.standardMCIReadyHandler.call(this, mciData);
|
||||
};
|
||||
*/
|
||||
|
||||
LastCallersModule.prototype.mciReady = function(mciData) {
|
||||
LastCallersModule.super_.prototype.mciReady.call(this, mciData);
|
||||
|
@ -98,12 +60,7 @@ LastCallersModule.prototype.mciReady = function(mciData) {
|
|||
var self = this;
|
||||
var vc = self.viewControllers.lastCallers = new ViewController( { client : self.client } );
|
||||
var lc = [];
|
||||
var count = _.size(mciData.menu) / 4;
|
||||
|
||||
if(count < 1) {
|
||||
// :TODO: Log me!
|
||||
return;
|
||||
}
|
||||
var rows = self.rows;
|
||||
|
||||
async.series(
|
||||
[
|
||||
|
@ -118,12 +75,13 @@ LastCallersModule.prototype.mciReady = function(mciData) {
|
|||
callback(err);
|
||||
});
|
||||
},
|
||||
// :TODO: a public method of getLastCallers(count) would be better
|
||||
function fetchHistory(callback) {
|
||||
userDb.each(
|
||||
'SELECT user_id, user_name, timestamp ' +
|
||||
'FROM user_login_history ' +
|
||||
'ORDER BY timestamp DESC ' +
|
||||
'LIMIT ' + count + ';',
|
||||
'LIMIT ' + rows + ';',
|
||||
function historyRow(err, histEntry) {
|
||||
lc.push( {
|
||||
userId : histEntry.user_id,
|
||||
|
@ -132,7 +90,7 @@ LastCallersModule.prototype.mciReady = function(mciData) {
|
|||
} );
|
||||
},
|
||||
function complete(err, recCount) {
|
||||
count = recCount; // adjust to retrieved
|
||||
rows = recCount; // adjust to retrieved
|
||||
callback(err);
|
||||
}
|
||||
);
|
||||
|
@ -156,124 +114,65 @@ LastCallersModule.prototype.mciReady = function(mciData) {
|
|||
});
|
||||
},
|
||||
function createAndPopulateViews(callback) {
|
||||
assert(lc.length === count);
|
||||
|
||||
var rowsPerColumn = count / 4;
|
||||
|
||||
//
|
||||
// TL1...count = who
|
||||
// TL<count>...<count*2> = location
|
||||
// TL1 = who
|
||||
// TL2 = location
|
||||
// TL3 = affiliation
|
||||
// TL4 = when
|
||||
//
|
||||
var i;
|
||||
var v;
|
||||
for(i = 0; i < rowsPerColumn; ++i) {
|
||||
v = vc.getView(i + 1);
|
||||
v.setText(lc[i].who);
|
||||
}
|
||||
|
||||
for( ; i < rowsPerColumn * 2; ++i) {
|
||||
v = vc.getView(i + 1);
|
||||
v.setText(lc[i].location);
|
||||
}
|
||||
|
||||
// These form the order/layout for a row. Additional rows
|
||||
// will use them as a template.
|
||||
//
|
||||
|
||||
// 1..count/4 = who
|
||||
// count/10
|
||||
|
||||
/*
|
||||
var viewOpts = {
|
||||
client : self.client,
|
||||
var views = {
|
||||
who : vc.getView(1),
|
||||
location : vc.getView(2),
|
||||
affils : vc.getView(3),
|
||||
when : vc.getView(4),
|
||||
};
|
||||
|
||||
var rowViewId = 1;
|
||||
var v;
|
||||
lc.forEach(function lcEntry(caller) {
|
||||
v = vc.getView(rowViewId++);
|
||||
var row = views.who.position.row;
|
||||
|
||||
self.menuConfig.config.fields.forEach(function field(f) {
|
||||
switch(f.name) {
|
||||
case 'who' :
|
||||
var nextId = 5;
|
||||
|
||||
function addView(templateView, text) {
|
||||
// :TODO: Is there a better way to clone this when dealing with instances?
|
||||
var v = new TextView( {
|
||||
client : self.client,
|
||||
id : nextId++,
|
||||
position : { row : row, col : templateView.position.col },
|
||||
ansiSGR : templateView.ansiSGR,
|
||||
textStyle : templateView.textStyle,
|
||||
textOverflow : templateView.textOverflow,
|
||||
dimens : templateView.dimens,
|
||||
resizable : templateView.resizable,
|
||||
} );
|
||||
|
||||
v.id = nextId++;
|
||||
v.position.row = row;
|
||||
|
||||
v.setPropertyValue('text', text);
|
||||
vc.addView(v);
|
||||
};
|
||||
|
||||
lc.forEach(function lastCaller(c) {
|
||||
if(row === views.who.position.row) {
|
||||
views.who.setText(c.who);
|
||||
views.location.setText(c.location);
|
||||
views.affils.setText(c.affiliation);
|
||||
views.when.setText(moment(c.when).format(self.dateTimeFormat));
|
||||
} else {
|
||||
addView(views.who, c.who);
|
||||
addView(views.location, c.location);
|
||||
addView(views.affils, c.affiliation);
|
||||
addView(views.when, moment(c.when).format(self.dateTimeFormat));
|
||||
}
|
||||
});
|
||||
|
||||
v.setText(caller.who)
|
||||
row++;
|
||||
});
|
||||
*/
|
||||
|
||||
}
|
||||
],
|
||||
function complete(err) {
|
||||
console.log(lc)
|
||||
self.client.log.error(err);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
LastCallersModule.prototype.mciReady = function(mciData) {
|
||||
LastCallersModule.super_.prototype.mciReady.call(this, mciData);
|
||||
|
||||
var lastCallers = [];
|
||||
var self = this;
|
||||
|
||||
// :TODO: durp... need a table just for this so dupes are possible
|
||||
|
||||
var userInfoStmt = userDb.prepare(
|
||||
'SELECT prop_name, prop_value ' +
|
||||
'FROM user_property ' +
|
||||
'WHERE user_id=? AND (prop_name=? OR prop_name=?);');
|
||||
|
||||
var caller;
|
||||
|
||||
userDb.each(
|
||||
'SELECT id, user_name, timestamp ' +
|
||||
'FROM user_last_login ' +
|
||||
'ORDER BY timestamp DESC ' +
|
||||
'LIMIT 10;',
|
||||
function userRows(err, userEntry) {
|
||||
caller = { name : userEntry.user_name };
|
||||
|
||||
userInfoStmt.each(userEntry.id, 'location', 'affiliation', function propRow(err, propEntry) {
|
||||
console.log(propEntry)
|
||||
if(!err) {
|
||||
caller[propEntry.prop_name] = propEntry.prop_value;
|
||||
}
|
||||
}, function complete(err) {
|
||||
lastCallers.push(caller);
|
||||
});
|
||||
},
|
||||
function complete(err) {
|
||||
//
|
||||
// TL1=name, TL2=location, TL3=affils
|
||||
// TL4=name, TL5=location, ...
|
||||
// ...
|
||||
// TL28=name, TL29=location, TL30=affils
|
||||
//
|
||||
var lc = self.viewControllers.lastCallers = new ViewController( { client : self.client });
|
||||
|
||||
var loadOpts = {
|
||||
callingMenu : self,
|
||||
mciMap : mciData.menu,
|
||||
noInput : true,
|
||||
};
|
||||
|
||||
self.viewControllers.lastCallers.loadFromMenuConfig(loadOpts, function viewsReady(err) {
|
||||
console.log(lastCallers);
|
||||
var callerIndex = 0;
|
||||
for(var i = 1; i < 30; i += 3) {
|
||||
if(lastCallers.length > callerIndex) {
|
||||
lc.getView(i).setText(lastCallers[callerIndex].name);
|
||||
lc.getView(i + 1).setText(lastCallers[callerIndex].location);
|
||||
lc.getView(i + 2).setText(lastCallers[callerIndex].affiliation);
|
||||
++callerIndex;
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
*/
|
|
@ -123,12 +123,10 @@
|
|||
},
|
||||
"ME3" : {
|
||||
"argName" : "birthdate",
|
||||
//"width" : 8,
|
||||
"maskPattern" : "####/##/##"
|
||||
},
|
||||
"ET4" : {
|
||||
"argName" : "sex",
|
||||
//"width" : 1,
|
||||
"maxLength" : 1
|
||||
},
|
||||
"ET5" : {
|
||||
|
@ -212,37 +210,26 @@
|
|||
"art" : "LASTCALL.ANS",
|
||||
"options" : { "cls" : true },
|
||||
"config" : {
|
||||
"dateTimeFormat" : "ddd MMM Do H:mm a",
|
||||
"fields" : [
|
||||
{
|
||||
"name" : "who",
|
||||
"width" : 17
|
||||
},
|
||||
{
|
||||
"name" : "location",
|
||||
"width" : 20
|
||||
},
|
||||
{
|
||||
"name" : "affiliation",
|
||||
"width" : 20
|
||||
},
|
||||
{
|
||||
"name" : "when",
|
||||
"width" : 20
|
||||
}
|
||||
]
|
||||
|
||||
"dateTimeFormat" : "ddd MMM Do H:mm a"
|
||||
},
|
||||
"form" : {
|
||||
"0" : {
|
||||
"TLTLTLTLTLTLTLTLTLTLTLTLTLTLTLTLTLTLTLTL" : {
|
||||
"TLTLTLTL" : {
|
||||
"mci" : {
|
||||
// :TODO: Bug: Without any keys here, theme customization does not apply!!!!
|
||||
"TL1" : {
|
||||
//"text" : "@method:getLastCaller"
|
||||
"styleSGR1" : "|00|24"
|
||||
},
|
||||
"TL2" : {
|
||||
//"text" : "@method:getLastCaller"
|
||||
"styleSGR1" : "|00|24"
|
||||
},
|
||||
"TL3" : {
|
||||
"styleSGR1" : "|00|24"
|
||||
},
|
||||
"TL4" : {
|
||||
"styleSGR1" : "|00|24"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -45,6 +45,23 @@
|
|||
"ET8" : { "width" : 21 },
|
||||
"ET9" : { "width" : 21 },
|
||||
"ET10" : { "width" : 21 }
|
||||
},
|
||||
"lastCallers" : {
|
||||
"TL1" : {
|
||||
"resizable" : false,
|
||||
"width" : 16,
|
||||
"textOverflow" : "..."
|
||||
},
|
||||
"TL2" : {
|
||||
"resizable" : false,
|
||||
"width" : 19,
|
||||
"textOverflow" : "..."
|
||||
},
|
||||
"TL3" : {
|
||||
"resizable" : false,
|
||||
"width" : 17,
|
||||
"textOverflow" : "..."
|
||||
}
|
||||
}
|
||||
},
|
||||
"prompts" : {
|
||||
|
|
Loading…
Reference in New Issue