* 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 async = require('async');
var util = require('util');
var async = require('async');
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.
if('*' === conf.config.preLoginTheme) {
var theme = require('./theme.js');
theme.getRandomTheme(function onRandTheme(err, themeId) {
async.waterfall(
[
function getRandTheme(callback) {
theme.getRandomTheme(function randTheme(err, themeId) {
client.user.properties.art_theme_id = themeId || '';
cb(null);
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 {
client.user.properties.art_theme_id = conf.config.preLoginTheme;
cb(null);

View File

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

View File

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

View File

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

View File

@ -7,7 +7,11 @@ var ButtonView = require('./button_view.js').ButtonView;
var VerticalMenuView = require('./vertical_menu_view.js').VerticalMenuView;
var Config = require('./config.js').config;
var packageJson = require('../package.json');
var assert = require('assert');
var os = require('os');
var _ = require('lodash');
exports.MCIViewFactory = MCIViewFactory;
@ -15,12 +19,30 @@ function MCIViewFactory(client) {
this.client = client;
}
MCIViewFactory.prototype.getPredefinedViewLabel = function(name) {
MCIViewFactory.prototype.getPredefinedViewLabel = function(code) {
var label;
switch(name) {
switch(code) {
// :TODO: Fix conflict with ButtonView (BN); chagne to BT
case 'BN' : label = Config.bbsName; break;
case 'VL' : label = 'ENiGMA½ v' + 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;
@ -56,6 +78,7 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
}
switch(mci.code) {
// Text Label (Text View)
case 'TL' :
setOption(0, 'textStyle');
setOption(1, 'justify');
@ -67,6 +90,7 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
view = new TextView(options);
break;
// Edit Text
case 'ET' :
if(setOption(0, 'maxLength')) {
options.maxLength = parseInt(options.maxLength, 10);
@ -75,11 +99,29 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
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');
view = new EditTextView(options);
break;
// Pre-defined Label (Text View)
case 'PL' :
if(mci.args.length > 0) {
options.text = this.getPredefinedViewLabel(mci.args[0]);
@ -97,7 +139,8 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
}
break;
case 'BN' :
// Button
case 'BT' :
if(mci.args.length > 0) {
options.dimens = { width : parseInt(mci.args[0], 10) };
}
@ -110,6 +153,7 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
view = new ButtonView(options);
break;
// Vertial Menu
case 'VM' :
setOption(0, 'itemSpacing');
setOption(1, 'justify');
@ -119,6 +163,13 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
view = new VerticalMenuView(options);
break;
default :
options.text = this.getPredefinedViewLabel(mci.code);
if(options.text) {
view = new TextView(options);
}
break;
}
return view;

View File

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

View File

@ -2,6 +2,8 @@
'use strict';
var userDb = require('./database.js').dbs.user;
var Config = require('./config.js').config;
var crypto = require('crypto');
var assert = require('assert');
var async = require('async');
@ -55,6 +57,12 @@ User.StandardPropertyGroups = {
password : [ 'pw_pbkdf2_salt', 'pw_pbkdf2_dk' ],
};
User.AccountStatus = {
disabled : -1,
inactive : 0,
active : 1,
};
User.prototype.authenticate = function(username, password, cb) {
var self = this;
@ -154,6 +162,9 @@ User.prototype.create = function(options, cb) {
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(
[
function beginTransaction(callback) {
@ -171,6 +182,12 @@ User.prototype.create = function(options, cb) {
callback(err);
} else {
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);
}
}

View File

@ -56,6 +56,8 @@ function ApplyModule(menuConfig) {
affiliation : args.affils,
email_address : args.email,
web_address : args.web,
art_theme_id : Config.users.defaultTheme, // :TODO: allow '*' = random
account_status : user.User.AccountStatus.inactive,
// :TODO: Other defaults
// :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) {
if(err) {
console.log(err)
self.client.gotoMenuModule( { name : args.next.error } );
} else {
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",
"author" : "NuSkooler",
"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 ViewController = require('../core/view_controller.js').ViewController;
//var async = require('async');
var async = require('async');
// :TODO: clean up requires
@ -33,6 +33,8 @@ function LoginModule(menuConfig) {
this.menuMethods.attemptLogin = function(args) {
self.client.user.authenticate(args.username, args.password, function onAuth(err) {
if(err) {
// :TODO: change to simple login/username prompts - no buttons.
Log.info( { username : args.username }, 'Failed login attempt %s', err);
// :TODO: localize:
@ -52,6 +54,20 @@ function LoginModule(menuConfig) {
Log.info( { username : self.client.user.username }, 'Successful login');
// :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:
"mci" : {
"BN1" : {
"BT1" : {
...
"draw" : "@script:blah.js/drawButton"
// @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
.....why?
}
}
*/
@ -60,16 +61,16 @@
"module" : "login",
"form" : {
"0" : {
"BN3BN4ET1ET2TL5" :{
"BT3BT4ET1ET2TL5" :{
"mci" :{
"ET1" : {
"focus" : true
},
"BN3" : {
"BT3" : {
"submit" : true,
"text" : "Login"
},
"BN4" : {
"BT4" : {
"submit" : true,
"text" : "Cancel"
}
@ -79,10 +80,16 @@
{
"value" : { "3" : null },
"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 },
"action" : "@menu:matrix"
@ -105,16 +112,16 @@
"module" : "apply",
"form" : {
"0" : {
"BN12BN13ET1ET10ET2ET3ET4ET5ET6ET7ET8ET9TL11" : {
"BT12BT13ET1ET10ET2ET3ET4ET5ET6ET7ET8ET9TL11" : {
"mci" : {
"ET1" : {
"focus" : true
},
"BN12" : {
"BT12" : {
"submit" : true,
"text" : "Apply"
},
"BN13" : {
"BT13" : {
"submit" : true,
"text" : "Cancel"
}
@ -124,10 +131,12 @@
{
"value" : { "12" : null },
"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" : {
"next" : {
"inactive" : "userNeedsActivated",
"active" : "newUserActive",
"error" : "newUserCreateError"
},
"username" : "{1}",
"realName" : "{2}",
"age" : "{3}",
@ -143,7 +152,7 @@
],
"13" : [ // Cancel
{
"value" : { "13" : null },
"value" : { "13" : null }, // :TODO: allow just "13" (number)
"action" : "@menu:matrix"
}
]
@ -154,5 +163,20 @@
"options" : {
"clearScreen" : true
}
},
"newUserActive" : {
"art" : "NEWACT",
"options" : {
"clearScreen" : true
},
"form" : {
"0" : {
"UN1UR2" : {
"mci" : {
}
}
}
}
}
}

View File

@ -44,90 +44,4 @@ StandardMenuModule.prototype.mciReady = function(mciMap) {
});*/
}
});
/*
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);
}
*/
};