diff --git a/core/mci_view_factory.js b/core/mci_view_factory.js index 330d777c..35b21e48 100644 --- a/core/mci_view_factory.js +++ b/core/mci_view_factory.js @@ -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]; diff --git a/core/system_menu_method.js b/core/system_menu_method.js index 69258256..d13c2352 100644 --- a/core/system_menu_method.js +++ b/core/system_menu_method.js @@ -40,29 +40,21 @@ function login(callingMenu, formData, extraArgs) { }); }, function recordLoginHistory(callback) { - userDb.run( - 'INSERT INTO user_login_history (user_id, user_name, timestamp) ' + - 'VALUES(?, ?, ?);', [ user.userId, user.username, now.toISOString() ], function inserted(err) { - callback(err); - }); + userDb.serialize(function serialized() { + userDb.run( + 'INSERT INTO user_login_history (user_id, user_name, timestamp) ' + + 'VALUES(?, ?, ?);', [ user.userId, user.username, now.toISOString() ] + ); - /* - userDb.run( - 'DELETE FROM last_caller ' + - 'WHERE id NOT IN (' + - ' SELECT id ' + - ' FROM last_caller ' + - ' ORDER BY timestamp DESC ' + - ' LIMIT 100);'); + // 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 user_id IN (' + - ' SELECT user_id ' + - ' ORDER BY timestamp DESC ' + - 'LIMIT 1;') - */ + callback(null); } ], function complete(err, results) { diff --git a/core/text_view.js b/core/text_view.js index 0d1425cb..861f2cb3 100644 --- a/core/text_view.js +++ b/core/text_view.js @@ -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); diff --git a/core/view.js b/core/view.js index 0ae3bd4a..f2ae6306 100644 --- a/core/view.js +++ b/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; } diff --git a/mods/last_callers.js b/mods/last_callers.js index a09d3f21..e64dcf0c 100644 --- a/mods/last_callers.js +++ b/mods/last_callers.js @@ -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'); @@ -12,18 +13,16 @@ var assert = require('assert'); var _ = require('lodash'); exports.moduleInfo = { - name : 'Last Callers', - desc : 'Last 10 callers to the system', - author : 'NuSkooler', + name : 'Last Callers', + 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; - } - }, function complete(err) { - if(!err) { - self.lastCallers.push(caller); - } - }); - } - ); + // 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'); + } }; -*/ - -/* -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... = 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.setText(caller.who) + 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)); + } + + 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 { - - } - } - }); - } - ); -}; -*/ \ No newline at end of file diff --git a/mods/menu.json b/mods/menu.json index b50682fb..ffb5aca1 100644 --- a/mods/menu.json +++ b/mods/menu.json @@ -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" : { - "TL1" : { - //"text" : "@method:getLastCaller" + // :TODO: Bug: Without any keys here, theme customization does not apply!!!! + "TL1" : { + "styleSGR1" : "|00|24" }, "TL2" : { - //"text" : "@method:getLastCaller" + "styleSGR1" : "|00|24" + }, + "TL3" : { + "styleSGR1" : "|00|24" + }, + "TL4" : { + "styleSGR1" : "|00|24" } + } } } diff --git a/mods/themes/NU-MAYA/LASTCALL.ANS b/mods/themes/NU-MAYA/LASTCALL.ANS index 874cce36..ce09facd 100644 Binary files a/mods/themes/NU-MAYA/LASTCALL.ANS and b/mods/themes/NU-MAYA/LASTCALL.ANS differ diff --git a/mods/themes/NU-MAYA/theme.json b/mods/themes/NU-MAYA/theme.json index a62a3a24..e5ce669c 100644 --- a/mods/themes/NU-MAYA/theme.json +++ b/mods/themes/NU-MAYA/theme.json @@ -29,7 +29,7 @@ "focusTextStyle" : "l33t" } }, - "apply" : { + "apply" : { "ET1" : { "width" : 21 }, "ET2" : { "width" : 21 }, //"ET3" : { "width" : 21 }, @@ -45,7 +45,24 @@ "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" : { "userCredentials" : {