diff --git a/core/bbs.js b/core/bbs.js index d668717b..c7973973 100644 --- a/core/bbs.js +++ b/core/bbs.js @@ -107,6 +107,9 @@ function initialize(cb) { database.initializeDatabases(); callback(null); }, + function initSystemProperties(callback) { + require('./system_property.js').loadSystemProperties(callback); + }, function initThemes(callback) { // Have to pull in here so it's after Config init var theme = require('./theme.js'); diff --git a/core/client_connections.js b/core/client_connections.js index ec066d40..ae099a9b 100644 --- a/core/client_connections.js +++ b/core/client_connections.js @@ -3,12 +3,17 @@ var logger = require('./logger.js'); +exports.getActiveConnections = getActiveConnections; exports.addNewClient = addNewClient; exports.removeClient = removeClient; var clientConnections = []; exports.clientConnections = clientConnections; +function getActiveConnections() { + return clientConnections.length; +} + function addNewClient(client) { var id = client.session.id = clientConnections.push(client) - 1; diff --git a/core/database.js b/core/database.js index be697168..e0bde8e8 100644 --- a/core/database.js +++ b/core/database.js @@ -17,11 +17,13 @@ function getDatabasePath(name) { } function initializeDatabases() { - // :TODO: this will need to change if more DB's are added + // :TODO: this will need to change if more DB's are added ... why? dbs.user = new sqlite3.Database(getDatabasePath('user')); dbs.message = new sqlite3.Database(getDatabasePath('message')); + dbs.system = new sqlite3.Database(getDatabasePath('system')); dbs.user.serialize(function serialized() { + createSystemTables(); createUserTables(); createInitialUserValues(); }); @@ -32,6 +34,15 @@ function initializeDatabases() { }); } +function createSystemTables() { + dbs.system.run( + 'CREATE TABLE IF NOT EXISTS system_property (' + + ' prop_name VARCHAR PRIMARY KEY NOT NULL,' + + ' prop_value VARCHAR NOT NULL' + + ');' + ); +} + function createUserTables() { dbs.user.run( 'CREATE TABLE IF NOT EXISTS user (' + diff --git a/core/mci_view_factory.js b/core/mci_view_factory.js index d7a254ef..d5092074 100644 --- a/core/mci_view_factory.js +++ b/core/mci_view_factory.js @@ -36,7 +36,7 @@ MCIViewFactory.UserViewCodes = [ 'XY', ]; -MCIViewFactory.prototype.createFromMCI = function(mci) { +MCIViewFactory.prototype.createFromMCI = function(mci, cb) { assert(mci.code); assert(mci.id > 0); assert(mci.position); @@ -54,9 +54,7 @@ MCIViewFactory.prototype.createFromMCI = function(mci) { function setOption(pos, name) { if(mci.args.length > pos && mci.args[pos].length > 0) { options[name] = mci.args[pos]; - return true; } - return false; } function setWidth(pos) { @@ -65,16 +63,13 @@ MCIViewFactory.prototype.createFromMCI = function(mci) { options.dimens = {}; } options.dimens.width = parseInt(mci.args[pos], 10); - return true; } - return false; } function setFocusOption(pos, name) { if(mci.focusArgs && mci.focusArgs.length > pos && mci.focusArgs[pos].length > 0) { options[name] = mci.focusArgs[pos]; } - return false; } // diff --git a/core/message_area.js b/core/message_area.js index a54032d2..2653291c 100644 --- a/core/message_area.js +++ b/core/message_area.js @@ -22,7 +22,15 @@ function getAvailableMessageAreas() { } function getDefaultMessageArea() { - return getAvailableMessageAreas()[0]; + // + // Return first non-private/etc. area name. This will be from config.hjson + // + var avail = getAvailableMessageAreas(); + for(var i = 0; i < avail.length; ++i) { + if(Message.WellKnownAreaNames.Private !== avail[i].name) { + return avail[i]; + } + } } function getMessageAreaByName(areaName) { diff --git a/core/predefined_mci.js b/core/predefined_mci.js index 5fe57652..65d688ff 100644 --- a/core/predefined_mci.js +++ b/core/predefined_mci.js @@ -4,6 +4,8 @@ var Config = require('./config.js').config; var Log = require('./logger.js').log; var getMessageAreaByName = require('./message_area.js').getMessageAreaByName; +var clientConnections = require('./client_connections.js'); +var sysProp = require('./system_property.js'); var packageJson = require('../package.json'); var assert = require('assert'); @@ -21,7 +23,12 @@ function getPredefinedMCIValue(client, code) { try { return { + // + // Board + // BN : function boardName() { return Config.general.boardName; }, + + // ENiGMA VL : function versionLabel() { return 'ENiGMA½ v' + packageJson.version; }, VN : function version() { return packageJson.version; }, @@ -29,6 +36,9 @@ function getPredefinedMCIValue(client, code) { // :TODO: SysOp real name + // + // Current user / session + // UN : function userName() { return client.user.username; }, UI : function userId() { return client.user.userId.toString(); }, UG : function groups() { return _.values(client.user.groups).join(', '); }, @@ -42,6 +52,8 @@ function getPredefinedMCIValue(client, code) { UF : function affils() { return client.user.properties.affiliation; }, UT : function themeId() { return client.user.properties.theme_id; }, UC : function loginCount() { return client.user.properties.login_count.toString(); }, + ND : function connectedNode() { return client.node.toString(); }, + IP : function clientIpAddress() { return client.address().address; }, MS : function accountCreated() { return moment(client.user.properties.account_created).format(client.currentTheme.helpers.getDateFormat()); }, CS : function currentStatus() { return client.currentStatus; }, @@ -58,13 +70,16 @@ function getPredefinedMCIValue(client, code) { SH : function termHeight() { return client.term.termHeight.toString(); }, SW : function termWidth() { return client.term.termWidth.toString(); }, - ND : function connectedNode() { return client.node.toString(); }, - + // + // Date/Time + // // :TODO: change to CD for 'Current Date' DT : function date() { return moment().format(client.currentTheme.helpers.getDateFormat()); }, CT : function time() { return moment().format(client.currentTheme.helpers.getTimeFormat()) ;}, - + // + // OS/System Info + // OS : function operatingSystem() { return { linux : 'Linux', @@ -78,7 +93,12 @@ function getPredefinedMCIValue(client, code) { OA : function systemArchitecture() { return os.arch(); }, SC : function systemCpuModel() { return os.cpus()[0].model; }, - IP : function clientIpAddress() { return client.address().address; }, + // :TODO: cpu load average (over N seconds): http://stackoverflow.com/questions/9565912/convert-the-output-of-os-cpus-in-node-js-to-percentage + + AN : function activeNodes() { return clientConnections.getActiveConnections().toString(); }, + + TC : function totalCalls() { return sysProp.getSystemProperty('login_count').toString(); }, + }[code](); } catch(e) { diff --git a/core/stats.js b/core/stats.js index 7c92f479..0280b437 100644 --- a/core/stats.js +++ b/core/stats.js @@ -3,9 +3,7 @@ var userDb = require('./database.js').dbs.user; -var async = require('async'); - -exports.getUserLoginHistory = getUserLoginHistory; +exports.getUserLoginHistory = getUserLoginHistory; function getUserLoginHistory(numRequested, cb) { @@ -30,4 +28,3 @@ function getUserLoginHistory(numRequested, cb) { } ); } - diff --git a/core/system_menu_method.js b/core/system_menu_method.js index 36783213..07874034 100644 --- a/core/system_menu_method.js +++ b/core/system_menu_method.js @@ -5,6 +5,7 @@ var theme = require('./theme.js'); var clientConnections = require('./client_connections.js').clientConnections; var ansi = require('./ansi_term.js'); var userDb = require('./database.js').dbs.user; +var sysProp = require('./system_property.js'); var async = require('async'); var _ = require('lodash'); @@ -89,6 +90,11 @@ function login(callingMenu, formData, extraArgs) { callback(null); // always non-fatal }); }, + function updateSystemLoginCount(callback) { + var sysLoginCount = sysProp.getSystemProperty('login_count') || 0; + sysLoginCount = parseInt(sysLoginCount, 10) + 1; + sysProp.persistSystemProperty('login_count', sysLoginCount, callback); + }, function recordLastLogin(callback) { user.persistProperty('last_login_timestamp', now.toISOString(), function persisted(err) { callback(err); @@ -106,7 +112,6 @@ function login(callingMenu, formData, extraArgs) { }); }, function recordLoginHistory(callback) { - userDb.serialize(function serialized() { userDb.run( 'INSERT INTO user_login_history (user_id, user_name, timestamp) ' + diff --git a/core/user_config.js b/core/user_config.js index 0b93a35f..30a43842 100644 --- a/core/user_config.js +++ b/core/user_config.js @@ -42,6 +42,12 @@ function UserConfigModule(options) { } }; + this.menuMethods = { + exitKeyPressed : function(formData, extraArgs) { + // :TODO: save/etc. + self.client.fallbackMenuModule(); + } + }; } require('util').inherits(UserConfigModule, MenuModule); diff --git a/core/view_controller.js b/core/view_controller.js index 16deb4a8..c97178ae 100644 --- a/core/view_controller.js +++ b/core/view_controller.js @@ -172,24 +172,6 @@ function ViewController(options) { } } break; - /*case 'method' : - case 'systemMethod' : - if(_.isString(actionAsset.location)) { - callModuleMenuMethod(paths.join(Config.paths.mods, actionAsset.location)); - } else { - if('systemMethod' === actionAsset.type) { - // :TODO: Need to pass optional args here -- conf.extraArgs and args between e.g. () - // :TODO: Probably better as system_method.js - callModuleMenuMethod(paths.join(__dirname, 'system_menu_method.js')); - } else { - // local to current module - var currentModule = client.currentMenuModule; - if(_.isFunction(currentModule.menuMethods[actionAsset.asset])) { - currentModule.menuMethods[actionAsset.asset](formData, conf.extraArgs); - } - } - }*/ - break; default : propValue = propValue = conf[propName]; @@ -224,7 +206,7 @@ function ViewController(options) { highestId = viewId; } - var view = self.getView(viewId); + var view = self.getView(viewId); if(!view) { self.client.log.warn( { viewId : viewId }, 'Cannot find view'); @@ -573,6 +555,7 @@ ViewController.prototype.loadFromMenuConfig = function(options, cb) { }, function applyThemeCustomization(callback) { formConfig = formConfig || {}; + formConfig.mci = formConfig.mci || {}; //self.client.currentMenuModule.menuConfig.config = self.client.currentMenuModule.menuConfig.config || {}; //console.log('menu config.....'); diff --git a/mods/last_callers.js b/mods/last_callers_old.js similarity index 97% rename from mods/last_callers.js rename to mods/last_callers_old.js index bb7a29a9..a25cdd63 100644 --- a/mods/last_callers.js +++ b/mods/last_callers_old.js @@ -25,7 +25,18 @@ exports.getModule = LastCallersModule; // :TODO: // * config.evenRowSGR (optional) + // :TODO: convert to using %XY system for finding row count +// ..or, better, use %VM1 with listFormat and noInput + +/* + Available listFormat object members: + who + location + affils + ts + +*/ function LastCallersModule(options) { MenuModule.call(this, options); diff --git a/mods/menu.hjson b/mods/menu.hjson index 040cfc3b..54f47ae1 100644 --- a/mods/menu.hjson +++ b/mods/menu.hjson @@ -163,6 +163,7 @@ /* nua -> send sysop mail -> { active } -> matrix -> you must active -> matrix + TODO: display PRINT before this (Obv/2) */ newUserApplication: { art: NUA @@ -367,23 +368,28 @@ } } } + fullLoginSequenceLoginArt: { + desc: Logging In art: LOGIN options: { pause: true } next: fullLoginSequenceLastCallers } fullLoginSequenceLastCallers: { + desc: Last Callers module: last_callers art: LASTCALL options: { pause: true } - next: fullLoginSequenceSysStats + next: fullLoginSequenceSysStats } fullLoginSequenceSysStats: { + desc: System Stats art: SYSSTAT options: { pause: true } next: fullLoginSequenceUserStats } fullLoginSequenceUserStats: { + desc: User Stats art: STATUS options: { pause: true } next: mainMenu @@ -437,6 +443,10 @@ value: { command: "C" } action: @menu:mainMenuUserConfig } + { + value: { command: "S" } + action: @menu:mainMenuSystemStats + } { "value" : 1, "action" : "@menu:mainMenu" @@ -452,6 +462,10 @@ art: STATUS options: { pause: true } } + mainMenuSystemStats: { + art: SYSSTAT + options: { pause: true } + } mainMenuUserConfig: { module: @systemModule:user_config art: CONFSCR @@ -479,6 +493,12 @@ argName: termSize } } + actionKeys: [ + { + keys: [ "escape" ] + action: @method:exitKeyPressed + } + ] } } } diff --git a/mods/themes/luciano_blocktronics/LASTCALL.ANS b/mods/themes/luciano_blocktronics/LASTCALL.ANS index a12267de..7d94b170 100644 Binary files a/mods/themes/luciano_blocktronics/LASTCALL.ANS and b/mods/themes/luciano_blocktronics/LASTCALL.ANS differ diff --git a/mods/themes/luciano_blocktronics/SYSSTAT.ANS b/mods/themes/luciano_blocktronics/SYSSTAT.ANS new file mode 100644 index 00000000..1dfa0026 Binary files /dev/null and b/mods/themes/luciano_blocktronics/SYSSTAT.ANS differ diff --git a/mods/themes/luciano_blocktronics/theme.hjson b/mods/themes/luciano_blocktronics/theme.hjson index fc48632c..1f317d15 100644 --- a/mods/themes/luciano_blocktronics/theme.hjson +++ b/mods/themes/luciano_blocktronics/theme.hjson @@ -78,9 +78,28 @@ } } + mainMenuSystemStats: { + mci: { + BN1: { width: 17 } + VL2: { width: 17 } + OS3: { width: 33 } + SC4: { width: 33 } + DT5: { width: 33 } + CT6: { width: 33 } + AN7: { width: 6 } + ND8: { width: 6 } + TC9: { width: 6 } + } + } + + mainMenuLastCallers: { config: { - dateTimeFormat: MMM Do H:mm a + listFormat: "|00|01|36{userName:<17.17}{location:<20.20}{affils:<18.18}{ts:<15}" + dateTimeFormat: MMM Do h:mma + } + mci: { + VM1: { height: 10 } } } @@ -181,7 +200,11 @@ fullLoginSequenceLastCallers: { config: { - dateTimeFormat: MMM Do H:mm a + listFormat: "|00|01|36{userName:<17.17}{location:<20.20}{affils:<18.18}{ts:<15}" + dateTimeFormat: MMM Do h:mma + } + mci: { + VM1: { height: 10 } } }