Files
WebGoat/src/main/resources/webgoat/static/js/goatApp/view/LessonContentView.js
Nanne Baars 711649924b Refactoring (#1201)
* Some initial refactoring

* Make it one application

* Got it working

* Fix problem on Windows

* Move WebWolf

* Move first lesson

* Moved all lessons

* Fix pom.xml

* Fix tests

* Add option to initialize a lesson

This way we can create content for each user inside a lesson. The initialize method will be called when a new user is created or when a lesson reset happens

* Clean up pom.xml files

* Remove fetching labels based on language.

We only support English at the moment, all the lesson explanations are written in English which makes it very difficult to translate. If we only had labels it would make sense to support multiple languages

* Fix SonarLint issues

* And move it all to the main project

* Fix for documentation paths

* Fix pom warnings

* Remove PMD as it does not work

* Update release notes about refactoring

Update release notes about refactoring

Update release notes about refactoring

* Fix lesson template

* Update release notes

* Keep it in the same repo in Dockerhub

* Update documentation to show how the connection is obtained.

Resolves: #1180

* Rename all integration tests

* Remove command from Dockerfile

* Simplify GitHub actions

Currently, we use a separate actions for pull-requests and branch build.
This is now consolidated in one action.
The PR action triggers always, it now only trigger when the PR is
opened and not in draft.
Running all platforms on a branch build is a bit too much, it is better
 to only run all platforms when someone opens a PR.

* Remove duplicate entry from release notes

* Add explicit registry for base image

* Lesson scanner not working when fat jar

When running the fat jar we have to take into account we
are reading from the jar file and not the filesystem. In
this case you cannot use `getFile` for example.

* added info in README and fixed release docker

* changed base image and added ignore file

Co-authored-by: Zubcevic.com <rene@zubcevic.com>
2022-04-09 14:56:12 +02:00

234 lines
11 KiB
JavaScript

//LessonContentView
define(['jquery',
'underscore',
'backbone',
'libs/jquery.form',
'goatApp/view/ErrorNotificationView',
'goatApp/view/PaginationControlView'],
function (
$,
_,
Backbone,
JQueryForm,
ErrorNotificationView,
PaginationControlView) {
return Backbone.View.extend({
el: '#lesson-content-wrapper', //TODO << get this fixed up in DOM
initialize: function (options) {
options = options || {};
new ErrorNotificationView();
var self = this;
Backbone.on('assignment:navTo', function (assignment) {
var page = self.findPage(assignment);
if (page != -1) {
self.navToPage(page);
}
});
setInterval(function () {
this.updatePagination();
}.bind(this), 5000);
},
findPage: function (assignment) {
for (var i = 0; i < this.$contentPages.length; i++) {
var contentPage = this.$contentPages[i];
var form = $('form.attack-form', contentPage);
var action = form.attr('action')
if (action !== undefined && action.includes(assignment.assignment)) {
return i;
}
}
return -1;
},
/* initial rendering */
render: function () {
this.$el.find('.lesson-content').html(this.model.get('content'));
this.$el.find('.attack-feedback').hide();
this.$el.find('.attack-output').hide();
this.makeFormsAjax();
$(window).scrollTop(0); //work-around til we get the scroll down sorted out
var startPageNum = this.model.get('pageNum');
this.initPagination(startPageNum);
},
initPagination: function (startPageNum) {
//get basic pagination info
this.$contentPages = this.$el.find('.lesson-page-wrapper');
var currentPage = (!isNaN(startPageNum) && startPageNum && startPageNum < this.$contentPages) ? startPageNum : 0;
//init views & pagination
this.showCurContentPage(currentPage);
this.paginationControlView = new PaginationControlView(this.$contentPages, this.model.get('lessonUrl'), startPageNum);
},
updatePagination: function () {
if (this.paginationControlView != undefined) {
this.paginationControlView.updateCollection();
}
},
getCurrentPage: function () {
return this.currentPage;
},
makeFormsAjax: function () {
this.$form = $('form.attack-form');
// turn off standard submit
var self = this;
// each submit handled per form
this.$form.each(function () {
$(this).submit(self.onFormSubmit.bind(self));
});
},
/* form submission handling */
onFormSubmit: function (e) {
var curForm = e.currentTarget; // the form from which the
var self = this;
// TODO custom Data prep for submission
var prepareDataFunctionName = $(curForm).attr('prepareData');
var callbackFunctionName = $(curForm).attr('callback');
var submitData = (typeof webgoat.customjs[prepareDataFunctionName] === 'function') ? webgoat.customjs[prepareDataFunctionName]() : $(curForm).serialize();
var additionalHeadersFunctionName = $(curForm).attr('additionalHeaders');
var additionalHeaders = (typeof webgoat.customjs[additionalHeadersFunctionName] === 'function') ? webgoat.customjs[additionalHeadersFunctionName]() : function () {
};
var successCallBackFunctionName = $(curForm).attr('successCallback');
var failureCallbackFunctionName = $(curForm).attr('failureCallback');
var informationalCallbackFunctionName = $(curForm).attr('informationalCallback');
var callbackFunction = (typeof webgoat.customjs[callbackFunctionName] === 'function') ? webgoat.customjs[callbackFunctionName] : function () {
};
this.curForm = curForm;
this.$curFeedback = $(curForm).closest('.attack-container').find('.attack-feedback');
this.$curOutput = $(curForm).closest('.attack-container').find('.attack-output');
var formUrl = $(curForm).attr('action');
var formMethod = $(curForm).attr('method');
var contentType = ($(curForm).attr('contentType')) ? $(curForm).attr('contentType') : 'application/x-www-form-urlencoded; charset=UTF-8';
var encType = $(curForm).attr('enctype')
$.ajax({
url: formUrl,
headers: additionalHeaders,
method: formMethod,
processData: 'multipart/form-data' !== encType,
contentType: 'multipart/form-data' === encType ? false : contentType,
data: submitData,
}).then(function (data) {
self.onSuccessResponse(data, failureCallbackFunctionName, successCallBackFunctionName, informationalCallbackFunctionName)
}, self.onErrorResponse.bind(self));
return false;
},
onSuccessResponse: function (data, failureCallbackFunctionName, successCallBackFunctionName, informationalCallbackFunctionName) {
this.renderFeedback(data.feedback);
this.renderOutput(data.output || "");
//var submitData = (typeof webgoat.customjs[prepareDataFunctionName] === 'function') ? webgoat.customjs[prepareDataFunctionName]() : $(curForm).serialize();
var successCallbackFunction = (typeof webgoat.customjs[successCallBackFunctionName] === 'function') ? webgoat.customjs[successCallBackFunctionName] : function () {
};
var failureCallbackFunction = (typeof webgoat.customjs[failureCallbackFunctionName] === 'function') ? webgoat.customjs[failureCallbackFunctionName] : function () {
};
var informationalCallbackFunction = (typeof webgoat.customjs[informationalCallbackFunctionName] === 'function') ? webgoat.customjs[informationalCallbackFunctionName] : function () {
};
if (data.attemptWasMade) {
if (data.lessonCompleted || data.assignmentCompleted) {
this.markAssignmentComplete();
successCallbackFunction(data); //data is likely not useful, except maybe the output ...
this.trigger('assignment:complete');
} else {
this.markAssignmentIncomplete(data); //again, data might be useful, especially the output
failureCallbackFunction();
}
} else {
informationalCallbackFunction();
}
return false;
},
markAssignmentComplete: function () {
this.curForm.reset();
$(this.curForm).siblings('.assignment-success').find('i').removeClass('hidden');
this.paginationControlView.updateCollection();
},
markAssignmentIncomplete: function () {
$(this.curForm).siblings('.assignment-success').find('i').addClass('hidden');
},
onErrorResponse: function (data, b, c) {
console.error(data);
if (data.status == 403) {
this.renderFeedback(data.responseText);
}
console.error(b);
console.error(c);
return false;
},
removeSlashesFromJSON: function (str) {
// slashes are leftover escapes from JSON serialization by server
// for every two char sequence starting with backslash,
// replace them in the text with second char only
return str.replace(/\\(.)/g, "$1");
},
renderFeedback: function (feedback) {
var s = this.removeSlashesFromJSON(feedback);
this.$curFeedback.html(polyglot.t(s) || "");
this.$curFeedback.show(400)
},
renderOutput: function (output) {
var s = this.removeSlashesFromJSON(output);
this.$curOutput.html(polyglot.t(s) || "");
this.$curOutput.show(400)
},
showCurContentPage: function (pageNum) {
this.$contentPages.hide();
this.$el.find(this.$contentPages[pageNum]).show();
},
findAssigmentEndpointsOnPage: function (pageNumber) {
var contentPage = this.$contentPages[pageNumber];
var endpoints = []; //going to assume uniqueness since these are assignments
var pageForms = $(contentPage).find('form.attack-form');
for (var i = 0; i < pageForms.length; i++) {
endpoints.push(pageForms[i].action);
}
return endpoints;
},
onNavToPage: function (pageNum) {
var assignmentPaths = this.findAssigmentEndpointsOnPage(pageNum);
this.trigger('endpoints:filtered', assignmentPaths);
},
navToPage: function (pageNum) {
this.paginationControlView.setCurrentPage(pageNum);//provides validation
this.showCurContentPage(this.paginationControlView.currentPage);
this.paginationControlView.render();
this.paginationControlView.hideShowNavButtons();
this.onNavToPage(pageNum);
//var assignmentPaths = this.findAssigmentEndpointsOnPage(pageNum);
//this.trigger('endpoints:filtered',assignmentPaths);
},
/* for testing */
showTestParam: function (param) {
this.$el.find('.lesson-content').html('test:' + param);
},
resetLesson: function () {
this.$el.find('.attack-feedback').hide();
this.$el.find('.attack-output').hide();
this.markAssignmentIncomplete();
}
});
});