diff --git a/core/stats.js b/core/stats.js index 0280b437..ecc472de 100644 --- a/core/stats.js +++ b/core/stats.js @@ -3,9 +3,9 @@ var userDb = require('./database.js').dbs.user; -exports.getUserLoginHistory = getUserLoginHistory; +exports.getSystemLoginHistory = getSystemLoginHistory; -function getUserLoginHistory(numRequested, cb) { +function getSystemLoginHistory(numRequested, cb) { numRequested = Math.max(1, numRequested); diff --git a/core/user.js b/core/user.js index 73a092ae..c39a8435 100644 --- a/core/user.js +++ b/core/user.js @@ -15,6 +15,7 @@ exports.User = User; exports.getUserIdAndName = getUserIdAndName; exports.getUserName = getUserName; exports.loadProperties = loadProperties; +exports.getUserList = getUserList; function User() { var self = this; @@ -482,3 +483,40 @@ function loadProperties(options, cb) { cb(null, properties); }); } + +function getUserList(options, cb) { + var userList = []; + + var orderClause = 'ORDER BY ' + (options.order || 'user_name'); + + userDb.each( + 'SELECT id, user_name ' + + 'FROM user ' + + orderClause + ';', + function userRow(err, row) { + userList.push({ + userId : row.id, + userName : row.user_name, + }); + }, + function usersComplete(err) { + options.properties = options.properties || []; + async.map(userList, function iter(user, callback) { + userDb.each( + 'SELECT prop_name, prop_value ' + + 'FROM user_property ' + + 'WHERE user_id=? AND prop_name IN ("' + options.properties.join('","') + '");', + [ user.userId ], + function propRow(err, row) { + user[row.prop_name] = row.prop_value; + }, + function complete(err) { + callback(err, user); + } + ); + }, function propsComplete(err, transformed) { + cb(err, transformed); + }); + } + ); +} diff --git a/mods/last_callers.js b/mods/last_callers.js index d0c7d68a..71d5036e 100644 --- a/mods/last_callers.js +++ b/mods/last_callers.js @@ -4,7 +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 getUserLoginHistory = require('../core/stats.js').getUserLoginHistory; +var getSystemLoginHistory = require('../core/stats.js').getSystemLoginHistory; var colorCodes = require('../core/color_codes.js'); var moment = require('moment'); @@ -65,7 +65,7 @@ LastCallersModule.prototype.mciReady = function(mciData, cb) { function fetchHistory(callback) { callersView = vc.getView(MciCodeIds.CallerList); - getUserLoginHistory(callersView.dimens.height, function historyRetrieved(err, lh) { + getSystemLoginHistory(callersView.dimens.height, function historyRetrieved(err, lh) { loginHistory = lh; callback(err); }); diff --git a/mods/menu.hjson b/mods/menu.hjson index 0e32a615..f64e7fdc 100644 --- a/mods/menu.hjson +++ b/mods/menu.hjson @@ -427,6 +427,10 @@ "action" : "@menu:doorLORD" }, */ + { + value: { command: "U" } + action: @menu:mainMenuUserList + } { value: { command: "E" } action: @menu:doorTestExample @@ -458,18 +462,42 @@ ] } mainMenuLastCallers: { + desc: Last Callers module: last_callers art: LASTCALL options: { pause: true } } mainMenuUserStats: { + desc: User Stats art: STATUS - options: { pause: true } + options: { pause: true } } mainMenuSystemStats: { + desc: System Stats art: SYSSTAT options: { pause: true } } + mainMenuUserList: { + desc: User Listing + module: user_list + art: USERLST + form: { + 0: { + mci: { + VM1: { + focus: true + submit: true + } + } + actionKeys: [ + { + keys: [ "escape" ] + action: @systemMethod:fallbackMenu + } + ] + } + } + } mainMenuUserConfig: { module: @systemModule:user_config art: CONFSCR diff --git a/mods/msg_list.js b/mods/msg_list.js index bf5cfdd9..6f42b029 100644 --- a/mods/msg_list.js +++ b/mods/msg_list.js @@ -135,7 +135,7 @@ MessageListModule.prototype.mciReady = function(mciData, cb) { // :TODO: fix default format var listFormat = self.menuConfig.config.listFormat || '{msgNum:>4} - {subj:>35} |{to:>15}'; - var focusListFormat = self.menuConfig.config.focusListFormat; + var focusListFormat = self.menuConfig.config.focusListFormat || listFormat; // :TODO: default change color here var msgNum = 1; var newMark = '*'; // :TODO: Make configurable diff --git a/mods/themes/luciano_blocktronics/IDLELOG.ANS b/mods/themes/luciano_blocktronics/IDLELOG.ANS index 062f99a1..8397ffbe 100644 Binary files a/mods/themes/luciano_blocktronics/IDLELOG.ANS and b/mods/themes/luciano_blocktronics/IDLELOG.ANS differ diff --git a/mods/themes/luciano_blocktronics/USERLST.ANS b/mods/themes/luciano_blocktronics/USERLST.ANS new file mode 100644 index 00000000..48ee5b69 Binary files /dev/null and b/mods/themes/luciano_blocktronics/USERLST.ANS differ diff --git a/mods/themes/luciano_blocktronics/theme.hjson b/mods/themes/luciano_blocktronics/theme.hjson index 1f317d15..ecde3903 100644 --- a/mods/themes/luciano_blocktronics/theme.hjson +++ b/mods/themes/luciano_blocktronics/theme.hjson @@ -103,6 +103,17 @@ } } + mainMenuUserList: { + config: { + listFormat: "|00|01|36{userName:<17.17}{affils:<21.21}{note:<21.21}{lastLoginTs}" + focusListFormat: "|00|42|30{userName:<17.17}{affils:<21.21}{note:<21.21}{lastLoginTs}" + dateTimeFormat: MMM Do h:mma + } + mci: { + VM1: { height: 15 } + } + } + messageAreaMessageList: { config: { listFormat: "|00|01|37{msgNum:>4} |00|37- |36{subj:<29.29} {from:<20.20} {ts} |01|31{newMark}" diff --git a/mods/user_list.js b/mods/user_list.js index 9f2acdc8..53216313 100644 --- a/mods/user_list.js +++ b/mods/user_list.js @@ -3,6 +3,7 @@ var MenuModule = require('../core/menu_module.js').MenuModule; var userDb = require('../core/database.js').dbs.user; +var getUserList = require('../core/user.js').getUserList; var ViewController = require('../core/view_controller.js').ViewController; var moment = require('moment'); @@ -14,11 +15,11 @@ var _ = require('lodash'); Available listFormat object members: userId userName - lastCall + lastLoginTs status location affiliation - timestamp + note */ exports.moduleInfo = { @@ -29,6 +30,10 @@ exports.moduleInfo = { exports.getModule = UserListModule; +var MciCodeIds = { + UserList : 1, +}; + function UserListModule(options) { MenuModule.call(this, options); } @@ -39,10 +44,63 @@ UserListModule.prototype.mciReady = function(mciData, cb) { var self = this; var vc = self.viewControllers.allViews = new ViewController( { client : self.client } ); + var userList = []; + + var USER_LIST_OPTS = { + properties : [ 'location', 'affiliation', 'last_login_timestamp' ], + }; + async.series( [ + // :TODO: These two functions repeated all over -- need DRY + function callParentMciReady(callback) { + UserListModule.super_.prototype.mciReady.call(self, mciData, callback); + }, + function loadFromConfig(callback) { + var loadOpts = { + callingMenu : self, + mciMap : mciData.menu, + }; - ], + vc.loadFromMenuConfig(loadOpts, callback); + }, + function fetchUserList(callback) { + // :TODO: Currently fetching all users - probably always OK, but this could be paged + getUserList(USER_LIST_OPTS, function got(err, ul) { + userList = ul; + callback(err); + }); + }, + function populateList(callback) { + var userListView = vc.getView(MciCodeIds.UserList); + + var listFormat = self.menuConfig.config.listFormat || '{userName} - {affils}'; + var focusListFormat = self.menuConfig.config.focusListFormat || listFormat; // :TODO: default changed color! + var dateTimeFormat = self.menuConfig.config.dateTimeFormat || 'ddd MMM DD'; + + function getUserFmtObj(ue) { + return { + userId : ue.userId, + userName : ue.userName, + affils : ue.affiliation, + // :TODO: the rest! + note : ue.note || '', + lastLoginTs : moment(ue.last_login_timestamp).format(dateTimeFormat), + } + } + + userListView.setItems(_.map(userList, function formatUserEntry(ue) { + return listFormat.format(getUserFmtObj(ue)); + })); + + userListView.setFocusItems(_.map(userList, function formatUserEntry(ue) { + return focusListFormat.format(getUserFmtObj(ue)); + })); + + userListView.redraw(); + callback(null); + } + ], function complete(err) { if(err) { self.client.log.error( { error : err.toString() }, 'Error loading user list');