Hints per lesson (#314)
Squashing and merging ... * Each assigment should have the options to have its own set of hints #278 * Updating lessons due to changes from #278 * Enable i18n client side #312 * IDOR move hints to assignment and enable i18n #312
This commit is contained in:
@ -72,6 +72,7 @@ define(['jquery',
|
||||
|
||||
if (this.name === name) {
|
||||
this.lessonContentView.navToPage(pageNum);
|
||||
this.lessonHintView.hideHints();
|
||||
this.titleView.render(this.lessonInfoModel.get('lessonTitle'));
|
||||
return;
|
||||
}
|
||||
@ -80,9 +81,7 @@ define(['jquery',
|
||||
if (typeof(name) === 'undefined' || name === null) {
|
||||
//TODO: implement lesson not found or return to welcome page?
|
||||
}
|
||||
this.lessonContent.loadData({
|
||||
'name':name
|
||||
});
|
||||
this.lessonContent.loadData({'name':name});
|
||||
this.planView = {};
|
||||
this.solutionView = {};
|
||||
this.sourceView = {};
|
||||
@ -94,23 +93,20 @@ define(['jquery',
|
||||
this.helpControlsView = new HelpControlsView({
|
||||
hasPlan:this.lessonInfoModel.get('hasPlan'),
|
||||
hasSolution:this.lessonInfoModel.get('hasSolution'),
|
||||
hasSource:this.lessonInfoModel.get('hasSource'),
|
||||
hasHints:(this.lessonInfoModel.get('numberHints') > 0)
|
||||
//hasAttack:this.lessonInfo.get('hasAttack') // TODO: add attack options
|
||||
hasSource:this.lessonInfoModel.get('hasSource')
|
||||
});
|
||||
|
||||
this.listenTo(this.helpControlsView,'hints:show',this.showHints);
|
||||
this.listenTo(this.helpControlsView,'lessonOverview:show',this.showLessonOverview)
|
||||
this.listenTo(this.helpControlsView,'attack:show',this.hideShowAttack);
|
||||
this.listenTo(this.helpControlsView,'solution:show',this.hideShowHelps);
|
||||
this.listenTo(this.helpControlsView,'source:show',this.hideShowHelps);
|
||||
this.listenTo(this.helpControlsView,'lesson:restart',this.restartLesson);
|
||||
this.listenTo(this.developerControlsView, 'dev:labels', this.restartLesson);
|
||||
this.listenTo(this,'hints:show',this.onShowHints);
|
||||
|
||||
this.helpControlsView.render();
|
||||
this.lessonOverview.hideLessonOverview();
|
||||
this.titleView.render(this.lessonInfoModel.get('lessonTitle'));
|
||||
this.helpControlsView.showHideHintsButton({});
|
||||
};
|
||||
|
||||
this.updateMenu = function() {
|
||||
@ -191,19 +187,6 @@ define(['jquery',
|
||||
this.lessonOverviewModel.fetch().then(this.lessonOverview.render());
|
||||
};
|
||||
|
||||
this.hideShowAttack = function (options) { // will likely expand this to encompass
|
||||
if (options.show) {
|
||||
$('#attack-container').show();
|
||||
$('#attack-container div.modal-header button.close, #about-modal div.modal-footer button').unbind('click').on('click', function() {
|
||||
$('#attack-container').hide(200);
|
||||
});
|
||||
if (this.lessonInfoModel.get('numberHints') > 0) {
|
||||
|
||||
this.lessonContentView.$el.find('#show-hints-button').unbind().on('click',_.bind(this.showHints,this)).show();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.restartLesson = function() {
|
||||
var self=this;
|
||||
$.ajax({
|
||||
|
@ -1,13 +1,15 @@
|
||||
define(['jquery','underscore','backbone','goatApp/view/GoatRouter', 'goatApp/support/goatAsyncErrorHandler'],
|
||||
function($,_,Backbone,Router, asyncErrorHandler){
|
||||
'use strict'
|
||||
//var goatRouter = new Router();
|
||||
return {
|
||||
initApp: function() {
|
||||
asyncErrorHandler.init();
|
||||
//TODO: add query/ability to load from where they left off
|
||||
var goatRouter = new Router();
|
||||
goatRouter.init();
|
||||
}
|
||||
};
|
||||
});
|
||||
define(['jquery', 'underscore', 'backbone', 'polyglot', 'goatApp/view/GoatRouter', 'goatApp/support/goatAsyncErrorHandler'],
|
||||
function ($, _, Backbone, Polyglot, Router, asyncErrorHandler) {
|
||||
'use strict'
|
||||
return {
|
||||
initApp: function () {
|
||||
var locale = localStorage.getItem('locale') || 'en';
|
||||
$.getJSON('service/labels.mvc?lang=' + locale, function(data) {
|
||||
window.polyglot = new Polyglot({phrases: data});
|
||||
asyncErrorHandler.init();
|
||||
var goatRouter = new Router();
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
});
|
@ -1,7 +1,7 @@
|
||||
define(['jquery',
|
||||
'underscore',
|
||||
'backbone',
|
||||
'goatApp/model/HintModel'],
|
||||
'goatApp/model/HintModel'],
|
||||
|
||||
function($,
|
||||
_,
|
||||
@ -25,8 +25,17 @@ define(['jquery',
|
||||
checkNullModel:function() {
|
||||
if (this.models[0].indexOf('There are no hints defined.') > -1) {
|
||||
this.reset([]);
|
||||
//return this.models;
|
||||
}
|
||||
},
|
||||
|
||||
getHintsForAssignment: function(assignmentPath) {
|
||||
var assignmentHints = new Array();
|
||||
this.models.forEach(function(hint) {
|
||||
if (assignmentPath.includes(hint.get('assignmentPath'))) {
|
||||
assignmentHints.push(hint);
|
||||
}
|
||||
});
|
||||
return assignmentHints;
|
||||
}
|
||||
});
|
||||
});
|
@ -17,11 +17,6 @@ define(['jquery',
|
||||
DeveloperControlsView,
|
||||
TitleView) {
|
||||
|
||||
var lessonContentView = new LessonContentView();
|
||||
var menuView = new MenuView();
|
||||
var developerControlsView = new DeveloperControlsView();
|
||||
var titleView = new TitleView();
|
||||
|
||||
function getContentElement() {
|
||||
return $('#main-content');
|
||||
};
|
||||
@ -38,7 +33,8 @@ define(['jquery',
|
||||
};
|
||||
|
||||
var GoatAppRouter = Backbone.Router.extend({
|
||||
routes: {
|
||||
|
||||
routes: {
|
||||
'welcome': 'welcomeRoute',
|
||||
'lesson/:name': 'lessonRoute',
|
||||
'lesson/:name/:pageNum': 'lessonPageRoute',
|
||||
@ -46,14 +42,9 @@ define(['jquery',
|
||||
'reportCard': 'reportCard'
|
||||
},
|
||||
|
||||
lessonController: new LessonController({
|
||||
lessonContentView: lessonContentView,
|
||||
titleView: titleView
|
||||
}),
|
||||
|
||||
menuController: new MenuController({
|
||||
menuView: menuView
|
||||
}),
|
||||
lessonController: null,
|
||||
menuController : null,
|
||||
titleView: null,
|
||||
|
||||
setUpCustomJS: function () {
|
||||
webgoat.customjs.jquery = $; //passing jquery into custom js scope ... still klunky, but works for now
|
||||
@ -75,54 +66,45 @@ define(['jquery',
|
||||
}
|
||||
},
|
||||
|
||||
init: function () {
|
||||
goatRouter = new GoatAppRouter();
|
||||
initialize: function () {
|
||||
this.menuController = new MenuController({menuView: new MenuView()});
|
||||
this.titleView = new TitleView();
|
||||
this.lessonController = new LessonController({lessonContentView: new LessonContentView(), titleView: this.titleView}),
|
||||
this.lessonController.start();
|
||||
// this.menuController.initMenu();
|
||||
webgoat = {};
|
||||
webgoat.customjs = {};
|
||||
|
||||
this.setUpCustomJS();
|
||||
|
||||
goatRouter.on('route:lessonRoute', function (name) {
|
||||
render();
|
||||
this.lessonController.loadLesson(name, 0);
|
||||
//TODO - update menu code from below
|
||||
this.menuController.updateMenu(name);
|
||||
});
|
||||
|
||||
goatRouter.on('route:lessonPageRoute', function (name, pageNum) {
|
||||
render();
|
||||
pageNum = (_.isNumber(parseInt(pageNum))) ? parseInt(pageNum) : 0;
|
||||
this.lessonController.loadLesson(name, pageNum);
|
||||
//TODO - update menu code from below
|
||||
this.menuController.updateMenu(name);
|
||||
});
|
||||
|
||||
goatRouter.on('route:welcomeRoute', function () {
|
||||
render();
|
||||
this.lessonController.loadWelcome();
|
||||
});
|
||||
|
||||
goatRouter.on('route:testRoute', function (param) {
|
||||
render();
|
||||
this.lessonController.testHandler(param);
|
||||
});
|
||||
|
||||
goatRouter.on("route", function (route, params) {
|
||||
});
|
||||
|
||||
Backbone.history.start();
|
||||
this.listenTo(this.lessonController, 'menu:reload', this.reloadMenu)
|
||||
},
|
||||
|
||||
lessonRoute: function(name) {
|
||||
render();
|
||||
this.lessonController.loadLesson(name, 0);
|
||||
this.menuController.updateMenu(name);
|
||||
},
|
||||
|
||||
lessonPageRoute: function (name, pageNum) {
|
||||
render();
|
||||
pageNum = (_.isNumber(parseInt(pageNum))) ? parseInt(pageNum) : 0;
|
||||
this.lessonController.loadLesson(name, pageNum);
|
||||
this.menuController.updateMenu(name);
|
||||
},
|
||||
|
||||
welcomeRoute: function () {
|
||||
render();
|
||||
this.lessonController.loadWelcome();
|
||||
},
|
||||
|
||||
reloadMenu: function (curLesson) {
|
||||
this.menuController.updateMenu();
|
||||
},
|
||||
|
||||
reportCard : function () {
|
||||
var self = this;
|
||||
require(['goatApp/view/ReportCardView'], function (ReportCardView) {
|
||||
titleView.render('Report card');
|
||||
self.titleView.render('Report card');
|
||||
render(new ReportCardView());
|
||||
});
|
||||
},
|
||||
|
@ -12,33 +12,34 @@ function($,_,Backbone) {
|
||||
this.hasPlan = options.hasPlan;
|
||||
this.hasSolution = options.hasSolution;
|
||||
this.hasSource = options.hasSource;
|
||||
this.hasHints = options.hasHints;
|
||||
var self = this;
|
||||
Backbone.on('navigatedToPage', function(nav) {
|
||||
self.showHideHintsButton(nav)
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
showHideHintsButton: function(nav) {
|
||||
if (typeof nav['assignmentPath'] !== 'undefined') {
|
||||
this.$el.find('#show-hints-button').unbind().on('click',this.showHints.bind(this)).show();
|
||||
} else {
|
||||
$('#show-hints-button').hide();
|
||||
}
|
||||
},
|
||||
|
||||
render:function(title) {
|
||||
//this.$el.html();
|
||||
// if still showing, hide
|
||||
$('#show-source-button').hide();
|
||||
$('#show-solution-button').hide();
|
||||
$('#show-plan-button').hide();
|
||||
$('#show-hints-button').hide();
|
||||
|
||||
if (this.hasSource) {
|
||||
this.$el.find('#show-source-button').unbind().on('click',_.bind(this.showSource,this)).show();
|
||||
}
|
||||
if (this.hasHints) {
|
||||
this.$el.find('#show-hints-button').unbind().on('click',this.showHints.bind(this)).show();
|
||||
}
|
||||
if (this.hasSolution) {
|
||||
this.$el.find('#show-solution-button').unbind().on('click',_.bind(this.showSolution,this)).show();
|
||||
}
|
||||
if (true) { //FIXME: change to this.hasAttack
|
||||
this.$el.find('#show-attack-button').unbind().on('click',_.bind(this.showAttack,this)).show();
|
||||
}
|
||||
|
||||
this.$el.find('#show-lesson-overview-button').unbind().on('click', _.bind(this.showLessonOverview, this)).show();
|
||||
this.$el.find('#restart-lesson-button').unbind().on('click',_.bind(this.restartLesson,this)).show();
|
||||
//this.$el.append(this.helpButtons.restartLesson);
|
||||
},
|
||||
|
||||
showHints: function() {
|
||||
@ -53,10 +54,6 @@ function($,_,Backbone) {
|
||||
this.trigger('solution:show','solution');
|
||||
},
|
||||
|
||||
showAttack: function() {
|
||||
this.trigger('attack:show',{show:true});
|
||||
},
|
||||
|
||||
restartLesson: function() {
|
||||
this.trigger('lesson:restart');
|
||||
},
|
||||
|
@ -15,8 +15,13 @@ function($,
|
||||
initialize: function() {
|
||||
this.curHint=0;
|
||||
this.collection = new HintCollection();
|
||||
this.hintsToShow = new Array();
|
||||
this.listenTo(this.collection,'loaded',this.onModelLoaded);
|
||||
this.hideHints();
|
||||
var self = this;
|
||||
Backbone.on('navigatedToPage', function(nav){
|
||||
self.selectHints(nav)
|
||||
});
|
||||
},
|
||||
|
||||
isVisible: function() {
|
||||
@ -34,19 +39,38 @@ function($,
|
||||
render:function() {
|
||||
if (this.isVisible()) {
|
||||
this.$el.hide(350);
|
||||
} else {
|
||||
} else if (this.hintsToShow.length > 0) {
|
||||
this.$el.show(350);
|
||||
}
|
||||
|
||||
this.toggleLabel()
|
||||
|
||||
if (this.collection.length > 0) {
|
||||
if (this.hintsToShow.length > 0) {
|
||||
this.hideShowPrevNextButtons();
|
||||
}
|
||||
this.displayHint(this.curHint);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Select the hints, we get '/WebGoat/HttpBasics/attack1' in the json (nav) we need to select all the hints
|
||||
* from the model where the assignment name is contained in the assignmentPath. We do this not to mess
|
||||
* with contextRoots etc and try to select the name from the url.
|
||||
*
|
||||
* @todo we can of course try to add the assigment name to the html form as attribute.
|
||||
*
|
||||
* @param nav the json structure for navigating
|
||||
*/
|
||||
selectHints: function(nav) {
|
||||
this.curHint = 0;
|
||||
var assignmentPath = nav['assignmentPath'];
|
||||
if (assignmentPath != null) {
|
||||
this.hintsToShow = this.collection.getHintsForAssignment(assignmentPath);
|
||||
} else {
|
||||
this.hintsToShow = new Array();
|
||||
}
|
||||
},
|
||||
|
||||
onModelLoaded: function() {
|
||||
this.trigger('hints:loaded',{'helpElement':'hints','value':true})
|
||||
},
|
||||
@ -58,7 +82,7 @@ function($,
|
||||
},
|
||||
|
||||
showNextHint: function() {
|
||||
this.curHint = (this.curHint < this.collection.length -1) ? this.curHint+1 : this.curHint;
|
||||
this.curHint = (this.curHint < this.hintsToShow.length -1) ? this.curHint+1 : this.curHint;
|
||||
this.hideShowPrevNextButtons();
|
||||
this.displayHint(this.curHint);
|
||||
},
|
||||
@ -70,11 +94,15 @@ function($,
|
||||
},
|
||||
|
||||
displayHint: function(curHint) {
|
||||
this.$el.find('#lesson-hint-content').html(this.collection.models[curHint].get('hint'));
|
||||
if(this.hintsToShow.length == 0) {
|
||||
this.hideHints();
|
||||
} else {
|
||||
this.$el.find('#lesson-hint-content').html(polyglot.t(this.hintsToShow[curHint].get('hint')));
|
||||
}
|
||||
},
|
||||
|
||||
hideShowPrevNextButtons: function() {
|
||||
if (this.curHint === this.collection.length -1) {
|
||||
if (this.curHint === this.hintsToShow.length -1) {
|
||||
this.$el.find('#show-next-hint').css('visibility','hidden');
|
||||
} else {
|
||||
this.$el.find('#show-next-hint').css('visibility','visible');
|
||||
|
@ -189,7 +189,7 @@ define(['jquery',
|
||||
if (this.currentPage < this.numPages -1) {
|
||||
this.currentPage++;
|
||||
window.location.href = this.model.get('lessonUrl') + '/' + this.currentPage;
|
||||
//this.showCurContentPage(true);
|
||||
//this.showCurContentPage(true);Con
|
||||
}
|
||||
|
||||
if (this.currentPage > 0) {
|
||||
@ -225,10 +225,21 @@ define(['jquery',
|
||||
this.$el.find(this.$contentPages[this.currentPage]).show();
|
||||
},
|
||||
|
||||
findAssigmentEndpointOnPage: function(pageNumber) {
|
||||
var contentPage = this.$contentPages[this.currentPage];
|
||||
var form = $('form.attack-form', contentPage);
|
||||
var action = form.attr('action')
|
||||
if (action !== undefined) {
|
||||
return action;
|
||||
}
|
||||
},
|
||||
|
||||
navToPage: function (pageNum) {
|
||||
this.setCurrentPage(pageNum);//provides validation
|
||||
this.showCurContentPage(this.currentPage);
|
||||
this.hideShowNavButtons();
|
||||
var assignmentPath = this.findAssigmentEndpointOnPage(pageNum);
|
||||
Backbone.trigger('navigatedToPage',{'pageNumber':pageNum, 'assignmentPath' : assignmentPath});
|
||||
},
|
||||
|
||||
hideShowNavButtons: function () {
|
||||
|
@ -7,13 +7,13 @@ function($,
|
||||
_,
|
||||
Backbone) {
|
||||
return Backbone.View.extend({
|
||||
el:'#toggle-menu', //Check this,
|
||||
el:'#toggle-menu',
|
||||
|
||||
initialize: function() {
|
||||
this.$el.on('click',this.toggleMenu);
|
||||
},
|
||||
|
||||
toggleMenu: function() {
|
||||
events: {
|
||||
"click": "toggleMenu"
|
||||
},
|
||||
|
||||
toggleMenu: function(e) {
|
||||
//left
|
||||
if (!$('.sidebarRight').hasClass('.sidebar-toggle-right')) {
|
||||
$('.sidebarRight').removeClass('sidebar-toggle-right');
|
||||
@ -21,7 +21,7 @@ function($,
|
||||
}
|
||||
$('.sidebar').toggleClass('sidebar-toggle');
|
||||
$('.main-content-wrapper').toggleClass('main-content-toggle-left');
|
||||
//e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
});
|
||||
});
|
@ -60,7 +60,7 @@ define(['jquery',
|
||||
var categoryLessonList = $('<ul>',{class:'slideDown lessonsAndStages',id:catId}); //keepOpen
|
||||
for (var j=0; j < lessons.length;j++) {
|
||||
var lessonItem = $('<li>',{class:'lesson'});
|
||||
var lessonName = lessons[j].name;
|
||||
var lessonName = polyglot.t(lessons[j].name);
|
||||
var lessonId = catId + '-' + GoatUtils.makeId(lessonName);
|
||||
if (this.curLessonLinkId === lessonId) {
|
||||
lessonItem.addClass('selected');
|
||||
|
Reference in New Issue
Block a user