* ButtonView is now MCI BT. BN is pre-defined "Board Name"

* Client current theme info loaded & used for e.g. passwordChar
* Code cleanup
This commit is contained in:
Bryan Ashby 2015-04-15 22:46:45 -06:00
parent 5eee568586
commit 586f3d60b3
16 changed files with 169 additions and 114 deletions

View File

@ -12,6 +12,7 @@ var iconv = require('iconv-lite');
var paths = require('path'); var paths = require('path');
var async = require('async'); var async = require('async');
var util = require('util'); var util = require('util');
var async = require('async');
exports.bbsMain = bbsMain; exports.bbsMain = bbsMain;
@ -194,10 +195,26 @@ function prepareClient(client, cb) {
// :TODO: it feels like this should go somewhere else... and be a bit more elegant. // :TODO: it feels like this should go somewhere else... and be a bit more elegant.
if('*' === conf.config.preLoginTheme) { if('*' === conf.config.preLoginTheme) {
var theme = require('./theme.js'); var theme = require('./theme.js');
theme.getRandomTheme(function onRandTheme(err, themeId) {
client.user.properties.art_theme_id = themeId || ''; async.waterfall(
cb(null); [
}); function getRandTheme(callback) {
theme.getRandomTheme(function randTheme(err, themeId) {
client.user.properties.art_theme_id = themeId || '';
callback(null);
});
},
function setCurrentThemeInfo(callback) {
theme.getThemeInfo(client.user.properties.art_theme_id, function themeInfo(err, info) {
client.currentThemeInfo = info;
callback(null);
});
}
],
function complete(err) {
cb(err);
}
);
} else { } else {
client.user.properties.art_theme_id = conf.config.preLoginTheme; client.user.properties.art_theme_id = conf.config.preLoginTheme;
cb(null); cb(null);

View File

@ -224,6 +224,10 @@ Client.prototype.gotoMenuModule = function(options, cb) {
}); });
}; };
Client.prototype.fallbackMenuModule = function(cb) {
};
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Default error handlers // Default error handlers
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////

View File

@ -31,6 +31,8 @@ module.exports = {
usernameMin : 2, usernameMin : 2,
usernameMax : 22, usernameMax : 22,
passwordMin : 6, passwordMin : 6,
requireActivation : true, // require SysOp activation?
defaultTheme : 'NU-MAYA',
}, },
paths : { paths : {

View File

@ -86,7 +86,6 @@ function connectEntry(client) {
term.write(ansi.clearScreen()); term.write(ansi.clearScreen());
client.gotoMenuModule({ name : Config.entryMod } ); client.gotoMenuModule({ name : Config.entryMod } );
//moduleUtil.goto(Config.entryMod, client);
}, timeout); }, timeout);
}); });
}, 500); }, 500);

View File

@ -7,7 +7,11 @@ var ButtonView = require('./button_view.js').ButtonView;
var VerticalMenuView = require('./vertical_menu_view.js').VerticalMenuView; var VerticalMenuView = require('./vertical_menu_view.js').VerticalMenuView;
var Config = require('./config.js').config; var Config = require('./config.js').config;
var packageJson = require('../package.json'); var packageJson = require('../package.json');
var assert = require('assert'); var assert = require('assert');
var os = require('os');
var _ = require('lodash');
exports.MCIViewFactory = MCIViewFactory; exports.MCIViewFactory = MCIViewFactory;
@ -15,12 +19,30 @@ function MCIViewFactory(client) {
this.client = client; this.client = client;
} }
MCIViewFactory.prototype.getPredefinedViewLabel = function(name) { MCIViewFactory.prototype.getPredefinedViewLabel = function(code) {
var label; var label;
switch(name) { switch(code) {
// :TODO: Fix conflict with ButtonView (BN); chagne to BT
case 'BN' : label = Config.bbsName; break; case 'BN' : label = Config.bbsName; break;
case 'VL' : label = 'ENiGMA½ v' + packageJson.version; break; case 'VL' : label = 'ENiGMA½ v' + packageJson.version; break;
case 'VN' : label = packageJson.version; break; case 'VN' : label = packageJson.version; break;
case 'UN' : label = this.client.user.username; break;
case 'UR' : label = this.client.user.properties.real_name; break;
case 'LO' : label = this.client.user.properties.location; break;
case 'OS' :
switch(os.platform()) {
case 'linux' : label = 'Linux'; break;
case 'darwin' : label = 'OS X'; break;
case 'win32' : label = 'Windows'; break;
case 'sunos' : label = 'SunOS'; break;
default : label = os.type(); break;
}
break;
case 'OA' : label = os.arch(); break;
case 'SC' : label = os.cpus()[0].model; break;
} }
return label; return label;
@ -56,6 +78,7 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
} }
switch(mci.code) { switch(mci.code) {
// Text Label (Text View)
case 'TL' : case 'TL' :
setOption(0, 'textStyle'); setOption(0, 'textStyle');
setOption(1, 'justify'); setOption(1, 'justify');
@ -67,6 +90,7 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
view = new TextView(options); view = new TextView(options);
break; break;
// Edit Text
case 'ET' : case 'ET' :
if(setOption(0, 'maxLength')) { if(setOption(0, 'maxLength')) {
options.maxLength = parseInt(options.maxLength, 10); options.maxLength = parseInt(options.maxLength, 10);
@ -75,11 +99,29 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
setOption(1, 'textStyle'); setOption(1, 'textStyle');
if(options.textStyle === 'P') {
// Supply from theme, if available
// :TODO: Move this logic elsewhere
if(this.client.currentThemeInfo && this.client.currentThemeInfo.config) {
var themePwChar = this.client.currentThemeInfo.config.passwordChar;
if(_.isString(themePwChar)) {
options.textMaskChar = themePwChar.substr(0, 1);
} else if(_.isNumber(themePwChar)) {
options.textMaskChar = String.fromCharCode(themePwChar);
} else {
options.textMaskChar = '*';
}
} else {
options.textMaskChar = '*';
}
}
setFocusOption(0, 'focusTextStyle'); setFocusOption(0, 'focusTextStyle');
view = new EditTextView(options); view = new EditTextView(options);
break; break;
// Pre-defined Label (Text View)
case 'PL' : case 'PL' :
if(mci.args.length > 0) { if(mci.args.length > 0) {
options.text = this.getPredefinedViewLabel(mci.args[0]); options.text = this.getPredefinedViewLabel(mci.args[0]);
@ -97,7 +139,8 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
} }
break; break;
case 'BN' : // Button
case 'BT' :
if(mci.args.length > 0) { if(mci.args.length > 0) {
options.dimens = { width : parseInt(mci.args[0], 10) }; options.dimens = { width : parseInt(mci.args[0], 10) };
} }
@ -110,6 +153,7 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
view = new ButtonView(options); view = new ButtonView(options);
break; break;
// Vertial Menu
case 'VM' : case 'VM' :
setOption(0, 'itemSpacing'); setOption(0, 'itemSpacing');
setOption(1, 'justify'); setOption(1, 'justify');
@ -119,6 +163,13 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
view = new VerticalMenuView(options); view = new VerticalMenuView(options);
break; break;
default :
options.text = this.getPredefinedViewLabel(mci.code);
if(options.text) {
view = new TextView(options);
}
break;
} }
return view; return view;

View File

@ -27,7 +27,7 @@ function MenuModule(options) {
self.beforeArt(); self.beforeArt();
callback(null); callback(null);
}, },
function displayArt(callback) { function displayArt(callback) {
theme.displayThemeArt(self.menuConfig.art, self.client, function onArt(err, mciMap) { theme.displayThemeArt(self.menuConfig.art, self.client, function onArt(err, mciMap) {
// :TODO: If the art simply is not found, or failed to load... we need to continue // :TODO: If the art simply is not found, or failed to load... we need to continue
if(err) { if(err) {

View File

@ -60,7 +60,7 @@ function TextView(options) {
this.setText(options.text || ''); this.setText(options.text || '');
if(this.isPasswordTextStyle) { if(this.isPasswordTextStyle) {
this.textMaskChar = miscUtil.valueWithDefault(this.textMaskChar, '*').substr(0, 1); this.textMaskChar = miscUtil.valueWithDefault(options.textMaskChar, '*').substr(0, 1);
} }
} }

View File

@ -5,9 +5,11 @@ var Config = require('./config.js').config;
var art = require('./art.js'); var art = require('./art.js');
var miscUtil = require('./misc_util.js'); var miscUtil = require('./misc_util.js');
var Log = require('./logger.js').log; var Log = require('./logger.js').log;
var fs = require('fs'); var fs = require('fs');
var paths = require('path'); var paths = require('path');
var async = require('async'); var async = require('async');
var _ = require('lodash');
exports.getThemeInfo = getThemeInfo; exports.getThemeInfo = getThemeInfo;
exports.getThemeArt = getThemeArt; exports.getThemeArt = getThemeArt;
@ -23,6 +25,7 @@ function getThemeInfo(themeID, cb) {
cb(err); cb(err);
} else { } else {
try { try {
// :TODO: strip comments/etc. ala menu.json
var info = JSON.parse(data); var info = JSON.parse(data);
cb(null, info); cb(null, info);
} catch(e) { } catch(e) {
@ -85,7 +88,7 @@ function getRandomTheme(cb) {
function getThemeArt(name, themeID, options, cb) { function getThemeArt(name, themeID, options, cb) {
// allow options to be optional // allow options to be optional
if(typeof cb === 'undefined') { if(_.isUndefined(cb)) {
cb = options; cb = options;
options = {}; options = {};
} }

View File

@ -2,6 +2,8 @@
'use strict'; 'use strict';
var userDb = require('./database.js').dbs.user; var userDb = require('./database.js').dbs.user;
var Config = require('./config.js').config;
var crypto = require('crypto'); var crypto = require('crypto');
var assert = require('assert'); var assert = require('assert');
var async = require('async'); var async = require('async');
@ -55,6 +57,12 @@ User.StandardPropertyGroups = {
password : [ 'pw_pbkdf2_salt', 'pw_pbkdf2_dk' ], password : [ 'pw_pbkdf2_salt', 'pw_pbkdf2_dk' ],
}; };
User.AccountStatus = {
disabled : -1,
inactive : 0,
active : 1,
};
User.prototype.authenticate = function(username, password, cb) { User.prototype.authenticate = function(username, password, cb) {
var self = this; var self = this;
@ -154,6 +162,9 @@ User.prototype.create = function(options, cb) {
var self = this; var self = this;
// :TODO: set various defaults, e.g. default activation status, etc.
self.properties.account_status = Config.users.requireActivation ? User.AccountStatus.inactive : User.AccountStatus.active;
async.series( async.series(
[ [
function beginTransaction(callback) { function beginTransaction(callback) {
@ -171,6 +182,12 @@ User.prototype.create = function(options, cb) {
callback(err); callback(err);
} else { } else {
self.userId = this.lastID; self.userId = this.lastID;
// Do not SGRValuesre activation for userId 1 (root/admin)
if(1 === self.userId) {
self.properties.account_status = User.AccountStatus.active;
}
callback(null); callback(null);
} }
} }

View File

@ -56,6 +56,8 @@ function ApplyModule(menuConfig) {
affiliation : args.affils, affiliation : args.affils,
email_address : args.email, email_address : args.email,
web_address : args.web, web_address : args.web,
art_theme_id : Config.users.defaultTheme, // :TODO: allow '*' = random
account_status : user.User.AccountStatus.inactive,
// :TODO: Other defaults // :TODO: Other defaults
// :TODO: should probably have a place to create defaults/etc. // :TODO: should probably have a place to create defaults/etc.
@ -64,9 +66,15 @@ function ApplyModule(menuConfig) {
newUser.create({ password : args.pw }, function created(err) { newUser.create({ password : args.pw }, function created(err) {
if(err) { if(err) {
console.log(err) self.client.gotoMenuModule( { name : args.next.error } );
} else { } else {
Log.info( { username : args.username, userId : newUser.userId }, 'New user created'); Log.info( { username : args.username, userId : newUser.userId }, 'New user created');
if(user.User.AccountStatus.inactive === self.client.user.properties.account_status) {
self.client.gotoMenuModule( { name : args.next.inactive } );
} else {
self.client.gotoMenuModule( { name : args.next.active } );
}
} }
}); });
} }

Binary file not shown.

Binary file not shown.

View File

@ -2,6 +2,6 @@
"name" : "Nu Mayan", "name" : "Nu Mayan",
"author" : "NuSkooler", "author" : "NuSkooler",
"config" : { "config" : {
"passwordChar" : "*" "passwordChar" : "φ"
} }
} }

View File

@ -9,7 +9,7 @@ var Log = require('../core/logger.js').log;
var MenuModule = require('../core/menu_module.js').MenuModule; var MenuModule = require('../core/menu_module.js').MenuModule;
var ViewController = require('../core/view_controller.js').ViewController; var ViewController = require('../core/view_controller.js').ViewController;
//var async = require('async'); var async = require('async');
// :TODO: clean up requires // :TODO: clean up requires
@ -33,6 +33,8 @@ function LoginModule(menuConfig) {
this.menuMethods.attemptLogin = function(args) { this.menuMethods.attemptLogin = function(args) {
self.client.user.authenticate(args.username, args.password, function onAuth(err) { self.client.user.authenticate(args.username, args.password, function onAuth(err) {
if(err) { if(err) {
// :TODO: change to simple login/username prompts - no buttons.
Log.info( { username : args.username }, 'Failed login attempt %s', err); Log.info( { username : args.username }, 'Failed login attempt %s', err);
// :TODO: localize: // :TODO: localize:
@ -52,6 +54,20 @@ function LoginModule(menuConfig) {
Log.info( { username : self.client.user.username }, 'Successful login'); Log.info( { username : self.client.user.username }, 'Successful login');
// :TODO: persist information about login to user // :TODO: persist information about login to user
async.parallel(
[
function loadThemeConfig(callback) {
theme.getThemeInfo(self.client.user.properties.art_theme_id, function themeInfo(err, info) {
self.client.currentThemeInfo = info;
callback(null);
});
}
],
function complete(err, results) {
self.client.gotoMenuModule( { name : args.next.success } );
}
);
} }
}); });
}; };

View File

@ -4,7 +4,7 @@
Some concept/ideas: Some concept/ideas:
"mci" : { "mci" : {
"BN1" : { "BT1" : {
... ...
"draw" : "@script:blah.js/drawButton" "draw" : "@script:blah.js/drawButton"
// @method:scriptName[.js]/methodName (foreign .js) // @method:scriptName[.js]/methodName (foreign .js)
@ -16,6 +16,7 @@
} }
..note that script/methods should be part of a *theme* - or at least checked first with fallback ..note that script/methods should be part of a *theme* - or at least checked first with fallback
.....why?
} }
} }
*/ */
@ -60,16 +61,16 @@
"module" : "login", "module" : "login",
"form" : { "form" : {
"0" : { "0" : {
"BN3BN4ET1ET2TL5" :{ "BT3BT4ET1ET2TL5" :{
"mci" :{ "mci" :{
"ET1" : { "ET1" : {
"focus" : true "focus" : true
}, },
"BN3" : { "BT3" : {
"submit" : true, "submit" : true,
"text" : "Login" "text" : "Login"
}, },
"BN4" : { "BT4" : {
"submit" : true, "submit" : true,
"text" : "Cancel" "text" : "Cancel"
} }
@ -79,10 +80,16 @@
{ {
"value" : { "3" : null }, "value" : { "3" : null },
"action" : "@method:attemptLogin", "action" : "@method:attemptLogin",
"args" : { "username" : "{1}", "password" : "{2}" } // :TODO: rename to actionArgs ? "args" : {
"next" : {
"success" : "newUserActive"
},
"username" : "{1}",
"password" : "{2}"
} // :TODO: rename to actionArgs ?
} }
], ],
"4" : [ // cancel "4" : [ // Cancel
{ {
"value" : { "4" : null }, "value" : { "4" : null },
"action" : "@menu:matrix" "action" : "@menu:matrix"
@ -105,16 +112,16 @@
"module" : "apply", "module" : "apply",
"form" : { "form" : {
"0" : { "0" : {
"BN12BN13ET1ET10ET2ET3ET4ET5ET6ET7ET8ET9TL11" : { "BT12BT13ET1ET10ET2ET3ET4ET5ET6ET7ET8ET9TL11" : {
"mci" : { "mci" : {
"ET1" : { "ET1" : {
"focus" : true "focus" : true
}, },
"BN12" : { "BT12" : {
"submit" : true, "submit" : true,
"text" : "Apply" "text" : "Apply"
}, },
"BN13" : { "BT13" : {
"submit" : true, "submit" : true,
"text" : "Cancel" "text" : "Cancel"
} }
@ -124,10 +131,12 @@
{ {
"value" : { "12" : null }, "value" : { "12" : null },
"action" : "@method:submitApplication", "action" : "@method:submitApplication",
// :TODO: Need a way to supply next menu based on data from @method
// @method:submitApplication|nextMenu1|nextMenu2|nextMenu3 ? method could cb(indexValue)
"args" : { "args" : {
"next" : {
"inactive" : "userNeedsActivated",
"active" : "newUserActive",
"error" : "newUserCreateError"
},
"username" : "{1}", "username" : "{1}",
"realName" : "{2}", "realName" : "{2}",
"age" : "{3}", "age" : "{3}",
@ -143,7 +152,7 @@
], ],
"13" : [ // Cancel "13" : [ // Cancel
{ {
"value" : { "13" : null }, "value" : { "13" : null }, // :TODO: allow just "13" (number)
"action" : "@menu:matrix" "action" : "@menu:matrix"
} }
] ]
@ -154,5 +163,20 @@
"options" : { "options" : {
"clearScreen" : true "clearScreen" : true
} }
},
"newUserActive" : {
"art" : "NEWACT",
"options" : {
"clearScreen" : true
},
"form" : {
"0" : {
"UN1UR2" : {
"mci" : {
}
}
}
}
} }
} }

View File

@ -43,91 +43,5 @@ StandardMenuModule.prototype.mciReady = function(mciMap) {
console.log(formData); console.log(formData);
});*/ });*/
} }
}); });
/*
menuUtil.getFormConfig(self.menuConfig, mciMap, function onFormConfig(err, formConfig) {
console.log(formConfig);
var vc = self.addViewController(new ViewController(self.client));
vc.loadFromMCIMap(mciMap);
vc.setViewOrder();
Object.keys(formConfig.mci).forEach(function onFormMci(mci) {
var viewId = parseInt(mci[2]);
if(formConfig.mci[mci].items && formConfig.mci[mci].items.length > 0) {
vc.getView(viewId).setItems(formConfig.mci[mci].items);
}
});
//vc.getView(1).setItems(['Login', 'New User', 'Goodbye!']);
vc.getView(1).submit = true;
vc.switchFocus(1);
});
*/
/*
{
"menuName" : {
"form" : [
{
"mciReq" : [ "MC1", "MC2", ... ],
"MC1" : {
"text" : "...",
"focus" : true,
"submit" : true,
},
}
]
}
}*/
/*
if(mciMap.ET1 && mciMap.ET2 && mciMap.BN1 && mciMap.BN2 && mciMap.BN3) {
//
// Form via EditTextViews and ButtonViews
// * ET1 - userName
// * ET2 - password
// * BN1 - Login
// * BN2 - New
// * BN3 - Bye!
//
} else if(mciMap.VM1) {
//
// Menu via VerticalMenuView
//
// * VM1 - menu with the following items:
// 0 - Login
// 1 - New
// 2 - Bye!
//
//var vc = new ViewController(client);
var vc = self.addViewController(new ViewController(self.client));
vc.on('submit', function onSubmit(form) {
console.log(form);
var viewModuleMap = {
'0' : 'login',
'1' : 'new',
'2' : 'logoff',
};
if(0 === form.id && 1 === form.submitId) {
console.log(viewModuleMap[form.value[1]]);
self.client.gotoMenuModule(viewModuleMap[form.value[1]]);
}
});
vc.loadFromMCIMap(mciMap);
vc.setViewOrder();
// :TODO: Localize
vc.getView(1).setItems(['Login', 'New User', 'Goodbye!']);
vc.getView(1).submit = true;
vc.switchFocus(1);
}
*/
}; };