From 586f3d60b389f7fe48ccae12ba49a217f852fd42 Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Wed, 15 Apr 2015 22:46:45 -0600 Subject: [PATCH] * ButtonView is now MCI BT. BN is pre-defined "Board Name" * Client current theme info loaded & used for e.g. passwordChar * Code cleanup --- core/bbs.js | 25 +++++-- core/client.js | 4 ++ core/config.js | 2 + core/connect.js | 1 - core/mci_view_factory.js | 57 ++++++++++++++- core/menu_module.js | 2 +- core/text_view.js | 2 +- core/theme.js | 5 +- core/user.js | 17 +++++ mods/apply.js | 10 ++- mods/art/LOGIN1.ANS | Bin 348 -> 348 bytes mods/art/themes/NU-MAYA/APPLY1.ANS | Bin 3954 -> 3924 bytes mods/art/themes/NU-MAYA/theme_info.json | 2 +- mods/login.js | 18 ++++- mods/menu.json | 50 ++++++++++---- mods/standard_menu.js | 88 +----------------------- 16 files changed, 169 insertions(+), 114 deletions(-) diff --git a/core/bbs.js b/core/bbs.js index a61d10fa..be72ae9b 100644 --- a/core/bbs.js +++ b/core/bbs.js @@ -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) { - client.user.properties.art_theme_id = themeId || ''; - cb(null); - }); + + async.waterfall( + [ + 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 { client.user.properties.art_theme_id = conf.config.preLoginTheme; cb(null); diff --git a/core/client.js b/core/client.js index f3a57047..913e4868 100644 --- a/core/client.js +++ b/core/client.js @@ -224,6 +224,10 @@ Client.prototype.gotoMenuModule = function(options, cb) { }); }; +Client.prototype.fallbackMenuModule = function(cb) { + +}; + /////////////////////////////////////////////////////////////////////////////// // Default error handlers /////////////////////////////////////////////////////////////////////////////// diff --git a/core/config.js b/core/config.js index b8129993..26a7cd39 100644 --- a/core/config.js +++ b/core/config.js @@ -31,6 +31,8 @@ module.exports = { usernameMin : 2, usernameMax : 22, passwordMin : 6, + requireActivation : true, // require SysOp activation? + defaultTheme : 'NU-MAYA', }, paths : { diff --git a/core/connect.js b/core/connect.js index 016c6258..a07fa25e 100644 --- a/core/connect.js +++ b/core/connect.js @@ -86,7 +86,6 @@ function connectEntry(client) { term.write(ansi.clearScreen()); client.gotoMenuModule({ name : Config.entryMod } ); - //moduleUtil.goto(Config.entryMod, client); }, timeout); }); }, 500); diff --git a/core/mci_view_factory.js b/core/mci_view_factory.js index aa1b6306..cdb77e28 100644 --- a/core/mci_view_factory.js +++ b/core/mci_view_factory.js @@ -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; diff --git a/core/menu_module.js b/core/menu_module.js index e0845eac..8450a767 100644 --- a/core/menu_module.js +++ b/core/menu_module.js @@ -27,7 +27,7 @@ function MenuModule(options) { self.beforeArt(); callback(null); }, - function displayArt(callback) { + function displayArt(callback) { 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 if(err) { diff --git a/core/text_view.js b/core/text_view.js index 9bd290b0..1e393237 100644 --- a/core/text_view.js +++ b/core/text_view.js @@ -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); } } diff --git a/core/theme.js b/core/theme.js index 7ca68a27..e4e6955e 100644 --- a/core/theme.js +++ b/core/theme.js @@ -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 = {}; } diff --git a/core/user.js b/core/user.js index a8415d8a..761a59d2 100644 --- a/core/user.js +++ b/core/user.js @@ -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); } } diff --git a/mods/apply.js b/mods/apply.js index f3e652b3..f6d27d33 100644 --- a/mods/apply.js +++ b/mods/apply.js @@ -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 } ); + } } }); } diff --git a/mods/art/LOGIN1.ANS b/mods/art/LOGIN1.ANS index 5b6e6dc5398fabed9ef9d5ecec256a5e26925098..6aabb29226b036cb0e65b16034fa537f3ce49be7 100644 GIT binary patch delta 54 vcmcb^bcboe3Y`#R4RakGXHDs7!(3IT5M$|RgIom#>1Y!$*91j!;*|~nqz4Z+ delta 54 vcmcb^bcboe3LQUV4RakGXHDs7!(3G-KV#`=gIom#>1Y!$*91j!;*|~np`#BT diff --git a/mods/art/themes/NU-MAYA/APPLY1.ANS b/mods/art/themes/NU-MAYA/APPLY1.ANS index ea1b3e496e0ffb8402ee79aa98fe809d9276a176..1a7f05c5e68609df7c2145944e2d958f4d00a217 100644 GIT binary patch delta 123 zcmew)cSUYP2P>O-u5`4a;pAzoR?3Fv&eGAwX1Vw811XTW1sdPba5D=VFC(h~NY7>| z_BE_*Af~aw=8HVaj65NRM$*xy)`q#NP9c;3@hSiXjNt;CHTYB*881)vaZu5`4SvvjnvIgnvGxsTP##1zB@i5nV$SZ2BR??VMF(D(?An_si? zGIAOk02LSjb#DI4wuY74&>YG&G~T?PN12h!&(H{HYObo2-{gC|3P7$gl)IUWPlb{3 J<79n)RRFF^E5ZN( diff --git a/mods/art/themes/NU-MAYA/theme_info.json b/mods/art/themes/NU-MAYA/theme_info.json index e8ae536b..0760612a 100644 --- a/mods/art/themes/NU-MAYA/theme_info.json +++ b/mods/art/themes/NU-MAYA/theme_info.json @@ -2,6 +2,6 @@ "name" : "Nu Mayan", "author" : "NuSkooler", "config" : { - "passwordChar" : "*" + "passwordChar" : "φ" } } \ No newline at end of file diff --git a/mods/login.js b/mods/login.js index a6c1ff4a..93177a78 100644 --- a/mods/login.js +++ b/mods/login.js @@ -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 } ); + } + ); } }); }; diff --git a/mods/menu.json b/mods/menu.json index 3943ab02..37b5f3a5 100644 --- a/mods/menu.json +++ b/mods/menu.json @@ -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" : { + + } + } + } + } } } \ No newline at end of file diff --git a/mods/standard_menu.js b/mods/standard_menu.js index bb02f155..ddddc2ff 100644 --- a/mods/standard_menu.js +++ b/mods/standard_menu.js @@ -43,91 +43,5 @@ StandardMenuModule.prototype.mciReady = function(mciMap) { 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); - } - */ + }); };