@@ -26,13 +26,14 @@ const aescrypto = require(path.join(__dirname, 'aescrypto'));
2626const https = require ( 'https' ) ;
2727
2828var modules = { } ;
29+ var moduleVer = 0 ;
2930var challengeDefinitions = [ ] ;
3031var challengeNames = [ ] ;
3132var solutions = [ ] ;
3233var descriptions = [ ] ;
3334var masterSalt = "" ;
3435
35- loadModules = function ( ) {
36+ loadModules = ( ) => {
3637 let modsPath ;
3738 if ( ! util . isNullOrUndefined ( process . env . DATA_DIR ) ) {
3839 modsPath = path . join ( process . env . DATA_DIR , "modules.json" ) ;
@@ -44,13 +45,18 @@ loadModules = function(){
4445 let localModules = { } ;
4546 let moduleIds = Object . keys ( moduleDefs ) ;
4647 for ( let moduleId of moduleIds ) {
48+ if ( moduleId === "version" ) {
49+ moduleVer = moduleDefs [ moduleId ] ;
50+ continue ;
51+ }
4752 let disabled = config . disabledModules ;
4853 if ( util . isNullOrUndefined ( disabled ) || ! disabled . includes ( moduleId ) ) {
4954 localModules [ moduleId ] = moduleDefs [ moduleId ] ;
5055 }
5156 }
52- return localModules ;
53- }
57+ return Object . freeze ( localModules ) ;
58+ }
59+
5460
5561function getModulePath ( moduleId ) {
5662 return path . join ( 'static/lessons/' , moduleId ) ;
@@ -71,8 +77,8 @@ function getDefinitionsForModule(moduleId){
7177/**
7278 * Initializes challenges when this module is loaded
7379 */
74- function init ( ) {
75- modules = Object . freeze ( loadModules ( ) ) ;
80+ let init = async ( ) => {
81+ modules = loadModules ( ) ;
7682 for ( let moduleId in modules ) {
7783 let moduleDefinitions = getDefinitionsForModule ( moduleId ) ;
7884 var modulePath = getModulePath ( moduleId ) ;
@@ -97,15 +103,22 @@ function init(){
97103 else {
98104 masterSalt = process . env . CHALLENGE_MASTER_SALT ;
99105 }
106+
107+ let dbModuleVersion = await db . getModuleVersion ( ) ;
108+ if ( dbModuleVersion < moduleVer ) {
109+ util . log ( "New training modules version, updating module completion for all users." )
110+ recreateBadgesOnModulesUpdate ( ) ;
111+ db . updateModuleVersion ( moduleVer ) ;
112+ }
100113}
101114
102115init ( ) ;
103116
104- exports . getModules = function ( ) { return modules ; }
105- exports . getChallengeNames = function ( ) { return challengeNames ; }
106- exports . getChallengeDefinitions = function ( ) { return challengeDefinitions ; }
117+ getModules = function ( ) { return modules ; }
118+ getChallengeNames = function ( ) { return challengeNames ; }
119+ getChallengeDefinitions = function ( ) { return challengeDefinitions ; }
107120
108- exports . isPermittedModule = async ( user , moduleId ) => {
121+ isPermittedModule = async ( user , moduleId ) => {
109122 let badges = await db . fetchBadges ( user . id ) ;
110123 if ( util . isNullOrUndefined ( modules [ moduleId ] ) ) {
111124 return false ;
@@ -131,9 +144,9 @@ exports.isPermittedModule = async (user, moduleId) => {
131144/**
132145 * Get the user level based on the amount of passed challenges
133146 */
134- exports . getUserLevelForModule = async ( user , moduleId ) => {
147+ getUserLevelForModule = async ( user , moduleId ) => {
135148 let moduleDefinitions = getDefinitionsForModule ( moduleId ) ;
136- let passedChallenges = await db . getPromise ( db . fetchChallengeEntriesForUser , user ) ;
149+ let passedChallenges = await db . fetchChallengeEntriesForUser ( user ) ;
137150 let userLevel = - 1 ;
138151 for ( let level of moduleDefinitions ) {
139152 let passCount = 0 ;
@@ -157,11 +170,11 @@ exports.getUserLevelForModule = async (user,moduleId) => {
157170/**
158171 * Get permitted challenges for module
159172 */
160- exports . getPermittedChallengesForUser = async ( user , moduleId ) => {
173+ getPermittedChallengesForUser = async ( user , moduleId ) => {
161174 if ( util . isNullOrUndefined ( moduleId ) ) return [ ] ;
162175 if ( util . isNullOrUndefined ( modules [ moduleId ] ) ) return [ ] ;
163176
164- var permittedLevel = await exports . getUserLevelForModule ( user , moduleId ) + 1 ;
177+ var permittedLevel = await getUserLevelForModule ( user , moduleId ) + 1 ;
165178
166179 var moduleDefinitions = getDefinitionsForModule ( moduleId ) ;
167180
@@ -179,7 +192,7 @@ exports.getPermittedChallengesForUser = async (user, moduleId) => {
179192 * @param {Object } user The session user object
180193 * @param {Array } moduleIds The lesson module ids
181194 */
182- exports . getChallengeDefinitionsForUser = async ( user , moduleId ) => {
195+ getChallengeDefinitionsForUser = async ( user , moduleId ) => {
183196 var returnChallenges = [ ] ;
184197
185198 if ( util . isNullOrUndefined ( moduleId ) ) return [ ] ;
@@ -214,7 +227,7 @@ exports.getChallengeDefinitionsForUser = async (user, moduleId) => {
214227 * Returns the solution html (converted from markdown)
215228 * @param {The challenge id } challengeId
216229 */
217- exports . getSolution = function ( challengeId ) {
230+ getSolution = function ( challengeId ) {
218231 var solution = solutions [ challengeId ] ;
219232 var solutionHtml = "" ;
220233 if ( ! util . isNullOrUndefined ( solution ) ) {
@@ -229,7 +242,7 @@ exports.getSolution = function (challengeId) {
229242 * Returns the description html (converted from markdown if applicable)
230243 * @param {The challenge id } challengeId
231244 */
232- exports . getDescription = function ( challengeId ) {
245+ getDescription = function ( challengeId ) {
233246 var description = descriptions [ challengeId ] ;
234247 var descriptionHtml = "" ;
235248 if ( util . isNullOrUndefined ( description ) ) return "" ;
@@ -252,8 +265,8 @@ exports.getDescription = function (challengeId) {
252265/**
253266 * Checks if the user has completed the module and issue a badge
254267 */
255- exports . verifyModuleCompletion = async ( user , moduleId ) => {
256- var userLevel = await exports . getUserLevelForModule ( user , moduleId ) ;
268+ verifyModuleCompletion = async ( user , moduleId ) => {
269+ var userLevel = await getUserLevelForModule ( user , moduleId ) ;
257270 let moduleDefinitions = getDefinitionsForModule ( moduleId ) ;
258271 var lastLevel = moduleDefinitions [ moduleDefinitions . length - 1 ] ;
259272
@@ -268,7 +281,7 @@ exports.verifyModuleCompletion = async (user, moduleId) => {
268281 }
269282 }
270283 if ( ! found ) {
271- util . log ( " WARN: Fixed badge for user." , user ) ;
284+ util . log ( ` WARN: Fixed badge for module ${ moduleId } for user.` , user ) ;
272285 await db . insertBadge ( user . id , moduleId ) ;
273286 }
274287 return true ;
@@ -277,11 +290,37 @@ exports.verifyModuleCompletion = async (user, moduleId) => {
277290 return false ;
278291}
279292
293+ /**
294+ * Iterates through the entire list of users to insert badges where needed
295+ */
296+ recreateBadgesOnModulesUpdate = async ( ) => {
297+
298+ let users = await db . fetchUsersWithId ( ) ;
299+
300+ for ( let user of users ) {
301+ let entries = await db . fetchChallengeEntriesForUser ( user ) ;
302+
303+ var passedChallenges = [ ] ;
304+ for ( let entry of entries ) {
305+ passedChallenges . push ( entry . challengeId ) ;
306+ }
307+
308+ user . passedChallenges = passedChallenges ;
309+ for ( let moduleId in modules ) {
310+ try {
311+ await verifyModuleCompletion ( user , moduleId ) ;
312+ } catch ( error ) {
313+ util . log ( "Error with badge verification." , user ) ;
314+ }
315+ }
316+ }
317+ }
318+
280319/**
281320 * Retrieves a code to verify completion of the level
282321 * @param {Badge } badge
283322 */
284- exports . getBadgeCode = ( badge , user ) => {
323+ getBadgeCode = ( badge , user ) => {
285324 let module = modules [ badge . moduleId ] ;
286325
287326 if ( util . isNullOrUndefined ( module ) || util . isNullOrUndefined ( module . badgeInfo ) ) return null ;
@@ -308,7 +347,7 @@ exports.getBadgeCode = (badge, user) => {
308347 * Verifies a badge code and returns parsed info
309348 * @param {Base64 } badgeCode
310349 */
311- exports . verifyBadgeCode = ( badgeCode ) => {
350+ verifyBadgeCode = ( badgeCode ) => {
312351 urlDecoded = decodeURIComponent ( badgeCode ) ;
313352 let parts = urlDecoded . split ( "." ) ;
314353 if ( parts . length !== 2 ) return null ;
@@ -331,7 +370,7 @@ exports.verifyBadgeCode = (badgeCode) => {
331370 * @param {* } badgrInfo
332371 * @param {* } user
333372 */
334- module . exports . badgrCall = function ( badgrInfo , user ) {
373+ badgrCall = function ( badgrInfo , user ) {
335374 if ( ! util . isNullOrUndefined ( badgrInfo ) && ! util . isNullOrUndefined ( config . encBadgrToken ) ) {
336375 if ( user . email === null ) {
337376 util . log ( "Cannot issue badge for this user. E-mail is null." , user ) ;
@@ -379,7 +418,7 @@ module.exports.badgrCall = function(badgrInfo, user){
379418/**
380419 * Logic to for the api challenge code
381420 */
382- module . exports . apiChallengeCode = async ( req ) => {
421+ apiChallengeCode = async ( req ) => {
383422 if ( util . isNullOrUndefined ( req . body . challengeId ) ||
384423 util . isNullOrUndefined ( req . body . challengeCode ) ||
385424 util . isNullOrUndefined ( req . body . moduleId ) ) {
@@ -408,7 +447,7 @@ module.exports.apiChallengeCode = async (req) => {
408447 var curChallengeObj = null ;
409448
410449 //identify the current challenge object and also the available challenges for the current user level
411- var availableChallenges = await module . exports . getPermittedChallengesForUser ( req . user , moduleId ) ;
450+ var availableChallenges = await getPermittedChallengesForUser ( req . user , moduleId ) ;
412451
413452 //search for the current challenge id
414453 for ( let availableChallenge of availableChallenges ) {
@@ -436,24 +475,24 @@ module.exports.apiChallengeCode = async (req) => {
436475 }
437476 //success update challenge
438477 curChallengeObj . moduleId = moduleId ;
439- return module . exports . insertChallengeEntry ( req . user , curChallengeObj , moduleId ) ;
478+ return insertChallengeEntry ( req . user , curChallengeObj , moduleId ) ;
440479
441480}
442481
443482/**
444483 * Inserts a challenge entry
445484 */
446- module . exports . insertChallengeEntry = async ( user , curChallengeObj , moduleId ) => {
485+ insertChallengeEntry = async ( user , curChallengeObj , moduleId ) => {
447486 await db . getPromise ( db . insertChallengeEntry , [ user . id , curChallengeObj . id ] ) ;
448487 //issue badgr badge if enabled
449- module . exports . badgrCall ( curChallengeObj . badgrInfo , user ) ;
450- let isModuleComplete = await module . exports . verifyModuleCompletion ( user , moduleId ) ;
488+ badgrCall ( curChallengeObj . badgrInfo , user ) ;
489+ let isModuleComplete = await verifyModuleCompletion ( user , moduleId ) ;
451490 //check to see if the user levelled up
452491 curChallengeObj . moduleComplete = isModuleComplete ;
453492 if ( isModuleComplete ) {
454493 util . log ( `User has solved the challenge ${ curChallengeObj . name } and completed the module!` , user ) ;
455494 //issue badgr badge if enabled for module
456- module . exports . badgrCall ( modules [ moduleId ] . badgrInfo , user ) ;
495+ badgrCall ( modules [ moduleId ] . badgrInfo , user ) ;
457496 return {
458497 "message" :"Congratulations you solved the challenge and completed the module! You can now get your badge of completion." ,
459498 "data" :curChallengeObj
@@ -466,4 +505,26 @@ module.exports.insertChallengeEntry = async (user,curChallengeObj, moduleId) =>
466505 "data" : curChallengeObj
467506 }
468507 }
508+ }
509+
510+
511+
512+
513+ module . exports = {
514+ apiChallengeCode,
515+ badgrCall,
516+ getBadgeCode,
517+ getChallengeNames,
518+ getChallengeDefinitions,
519+ getChallengeDefinitionsForUser,
520+ getDescription,
521+ getModules,
522+ getPermittedChallengesForUser,
523+ getUserLevelForModule,
524+ getSolution,
525+ insertChallengeEntry,
526+ isPermittedModule,
527+ verifyBadgeCode,
528+ verifyModuleCompletion,
529+ recreateBadgesOnModulesUpdate
469530}
0 commit comments