Index: lams_central/conf/security/Owasp.CsrfGuard.properties =================================================================== diff -u -r26a325f638a2262664350d6c6eef686769983040 -rca3fa00d5084315963b72ba5db58180fee898754 --- lams_central/conf/security/Owasp.CsrfGuard.properties (.../Owasp.CsrfGuard.properties) (revision 26a325f638a2262664350d6c6eef686769983040) +++ lams_central/conf/security/Owasp.CsrfGuard.properties (.../Owasp.CsrfGuard.properties) (revision ca3fa00d5084315963b72ba5db58180fee898754) @@ -128,6 +128,7 @@ org.owasp.csrfguard.protected.assessmentMonitoringAllocateUserAnswer=/lams/tool/laasse10/monitoring/allocateUserAnswer.do org.owasp.csrfguard.protected.assessmentMonitoringSetActivityEvaluation=/lams/tool/laasse10/monitoring/setActivityEvaluation.do org.owasp.csrfguard.protected.assessmentSaveUserGrade=/lams/tool/laasse10/monitoring/saveUserGrade.do +org.owasp.csrfguard.protected.assessmentTimeLimitControl=/lams/tool/laasse10/monitoring/timeLimitControl.do org.owasp.csrfguard.protected.chatAuthoringSave=/lams/tool/lachat11/authoring/updateContent.do org.owasp.csrfguard.protected.chatAuthoringDefineLater=/lams/tool/lachat11/authoring/definelater.do Index: lams_tool_assessment/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -r9007e50ca508c5ec3312cd880e6df2d1ec3dab1e -rca3fa00d5084315963b72ba5db58180fee898754 --- lams_tool_assessment/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 9007e50ca508c5ec3312cd880e6df2d1ec3dab1e) +++ lams_tool_assessment/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision ca3fa00d5084315963b72ba5db58180fee898754) @@ -85,7 +85,7 @@ label.authoring.essay.add.essay =Add question label.authoring.ordering.add.ordering =Add question label.authoring.advance.allow.students.overall.feedback =Display overall feedback at the end of each attempt -label.authoring.advance.time.limit =Time limit (minutes) +label.authoring.advance.time.limit =Relative time limit (minutes) label.authoring.advance.questions.per.page =Questions per page label.authoring.advance.all.in.one.page =All in one page label.authoring.advance.shuffle.questions =Shuffle questions @@ -392,4 +392,23 @@ label.not.yet.disclosed = The teacher has not disclosed other teams' answers yet error.resource.image.comment.blank =Comment can not be blank. +label.monitoring.summary.time.limit = Time limit +label.monitoring.summary.time.limit.relative = Relative for each learner +label.monitoring.summary.time.limit.absolute = Absolute for all learners +label.monitoring.summary.time.limit.individual = Individual adjustment +label.monitoring.summary.time.limit.minutes = minutes +label.monitoring.summary.time.limit.seconds = seconds +label.monitoring.summary.time.limit.enabled = Enabled +label.monitoring.summary.time.limit.disabled = Disabled +label.monitoring.summary.time.limit.running = Running +label.monitoring.summary.time.limit.start = Start +label.monitoring.summary.time.limit.cancel = Cancel +label.monitoring.summary.time.limit.plus.minute.1 = Plus 1 minute +label.monitoring.summary.time.limit.plus.minute.5 = Plus 5 minutes +label.monitoring.summary.time.limit.plus.minute.10 = Plus 10 minutes +label.monitoring.summary.time.limit.minus.minute.1 = Minus 1 minute +label.monitoring.summary.time.limit.minus.minute.5 = Minus 5 minutes +label.monitoring.summary.time.limit.minus.minute.10 = Minus 10 minutes + + #======= End labels: Exported 372 labels for en AU ===== Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/LearningWebsocketServer.java =================================================================== diff -u -r998ba383ec2a06647d309f910ebefe0a33fa30a4 -rca3fa00d5084315963b72ba5db58180fee898754 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/LearningWebsocketServer.java (.../LearningWebsocketServer.java) (revision 998ba383ec2a06647d309f910ebefe0a33fa30a4) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/LearningWebsocketServer.java (.../LearningWebsocketServer.java) (revision ca3fa00d5084315963b72ba5db58180fee898754) @@ -97,11 +97,13 @@ if (timeCache.relativeTimeLimit > 0) { AssessmentResult result = LearningWebsocketServer.getAssessmentService() .getLastAssessmentResult(assessmentUid, userId); - LocalDateTime existingLaunchDate = result == null ? null - : result.getTimeLimitLaunchedDate(); - if (existingLaunchDate == null) { + if (result == null) { continue; } + LocalDateTime existingLaunchDate = result.getTimeLimitLaunchedDate(); + if (existingLaunchDate == null) { + existingLaunchDate = assessmentService.launchTimeLimit(assessmentUid, userId); + } LocalDateTime launchedDate = timeCache.timeLimitLaunchedDate.get(userId); if (launchedDate == null || !launchedDate.equals(existingLaunchDate)) { Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java =================================================================== diff -u -r9007e50ca508c5ec3312cd880e6df2d1ec3dab1e -rca3fa00d5084315963b72ba5db58180fee898754 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java (.../MonitoringController.java) (revision 9007e50ca508c5ec3312cd880e6df2d1ec3dab1e) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java (.../MonitoringController.java) (revision ca3fa00d5084315963b72ba5db58180fee898754) @@ -24,6 +24,7 @@ package org.lamsfoundation.lams.tool.assessment.web.controller; import java.io.IOException; +import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -809,6 +810,19 @@ } } + @RequestMapping(path = "/timeLimitControl", method = RequestMethod.POST) + @ResponseStatus(HttpStatus.OK) + public void timeLimitControl(@RequestParam(name = AssessmentConstants.PARAM_TOOL_CONTENT_ID) long toolContentId, + @RequestParam int relativeTimeLimit) { + if (relativeTimeLimit < 0) { + throw new InvalidParameterException( + "Relative time limit must not be negative and it is " + relativeTimeLimit); + } + Assessment assessment = service.getAssessmentByContentId(toolContentId); + assessment.setTimeLimit(relativeTimeLimit); + service.saveOrUpdateAssessment(assessment); + } + @SuppressWarnings("unchecked") private SessionMap getSessionMap(HttpServletRequest request) { String sessionMapID = WebUtil.readStrParam(request, AssessmentConstants.ATTR_SESSION_MAP_ID); Index: lams_tool_assessment/web/pages/learning/learning.jsp =================================================================== diff -u -r998ba383ec2a06647d309f910ebefe0a33fa30a4 -rca3fa00d5084315963b72ba5db58180fee898754 --- lams_tool_assessment/web/pages/learning/learning.jsp (.../learning.jsp) (revision 998ba383ec2a06647d309f910ebefe0a33fa30a4) +++ lams_tool_assessment/web/pages/learning/learning.jsp (.../learning.jsp) (revision ca3fa00d5084315963b72ba5db58180fee898754) @@ -100,6 +100,8 @@ } }); }); + + initAssessmentTimeLimitWebsocket(); } //autocomplete for VSA @@ -121,160 +123,163 @@ } //boolean to indicate whether ok dialog is still ON so that autosave can't be run - var isWaitingForConfirmation = ${isTimeLimitEnabled && sessionMap.isTimeLimitNotLaunched}; + // var isWaitingForConfirmation = ${isTimeLimitEnabled && sessionMap.isTimeLimitNotLaunched}; + + //timelimit feature - - // websocket needs pinging and reconnection feature in case it fails - // it works pretty much the same as command websocket in Page.tag - var assessmentTimeLimitWebsocketInitTime = null, - assessmentTimeLimitWebsocket = null, - assessmentTimeLimitWebsocketPingTimeout = null, - assessmentTimeLimitWebsocketPingFunc = null, - assessmentTimeLimitWebsocketReconnectAttempts = 0, - counterInitialised = false; + + // websocket needs pinging and reconnection feature in case it fails + // it works pretty much the same as command websocket in Page.tag + var assessmentTimeLimitWebsocketInitTime = null, + assessmentTimeLimitWebsocket = null, + assessmentTimeLimitWebsocketPingTimeout = null, + assessmentTimeLimitWebsocketPingFunc = null, + assessmentTimeLimitWebsocketReconnectAttempts = 0, + counterInitialised = false; + + assessmentTimeLimitWebsocketPingFunc = function(skipPing){ + if (assessmentTimeLimitWebsocket.readyState == assessmentTimeLimitWebsocket.CLOSING + || assessmentTimeLimitWebsocket.readyState == assessmentTimeLimitWebsocket.CLOSED){ + return; + } - assessmentTimeLimitWebsocketPingFunc = function(skipPing){ - if (assessmentTimeLimitWebsocket.readyState == assessmentTimeLimitWebsocket.CLOSING - || assessmentTimeLimitWebsocket.readyState == assessmentTimeLimitWebsocket.CLOSED){ - return; + // check and ping every 3 minutes + assessmentTimeLimitWebsocketPingTimeout = setTimeout(assessmentTimeLimitWebsocketPingFunc, 3*60*1000); + // initial set up does not send ping + if (!skipPing) { + assessmentTimeLimitWebsocket.send("ping"); + } + }; + + function initAssessmentTimeLimitWebsocket(){ + assessmentTimeLimitWebsocketInitTime = Date.now(); + assessmentTimeLimitWebsocket = new WebSocket(''.replace('http', 'ws') + + 'learningWebsocket?toolContentID=' + ${sessionMap.assessment.contentId}); + + assessmentTimeLimitWebsocket.onclose = function(e){ + // check reason and whether the close did not happen immediately after websocket creation + // (possible access denied, user logged out?) + if (e.code === 1006 && + Date.now() - assessmentTimeLimitWebsocketInitTime > 1000 && + assessmentTimeLimitWebsocketReconnectAttempts < 20) { + assessmentTimeLimitWebsocketReconnectAttempts++; + // maybe iPad went into sleep mode? + // we need this websocket working, so init it again after delay + setTimeout(initAssessmentTimeLimitWebsocket, 3000); } - - // check and ping every 3 minutes - assessmentTimeLimitWebsocketPingTimeout = setTimeout(assessmentTimeLimitWebsocketPingFunc, 3*60*1000); - // initial set up does not send ping - if (!skipPing) { - assessmentTimeLimitWebsocket.send("ping"); - } }; - - function initAssessmentTimeLimitWebsocket(){ - assessmentTimeLimitWebsocketInitTime = Date.now(); - assessmentTimeLimitWebsocket = new WebSocket(''.replace('http', 'ws') - + 'learningWebsocket?toolContentID=' + ${sessionMap.assessment.contentId}); - assessmentTimeLimitWebsocket.onclose = function(e){ - // check reason and whether the close did not happen immediately after websocket creation - // (possible access denied, user logged out?) - if (e.code === 1006 && - Date.now() - assessmentTimeLimitWebsocketInitTime > 1000 && - assessmentTimeLimitWebsocketReconnectAttempts < 20) { - assessmentTimeLimitWebsocketReconnectAttempts++; - // maybe iPad went into sleep mode? - // we need this websocket working, so init it again after delay - setTimeout(initAssessmentTimeLimitWebsocket, 3000); - } - }; - - // set up timer for the first time - assessmentTimeLimitWebsocketPingFunc(true); + // set up timer for the first time + assessmentTimeLimitWebsocketPingFunc(true); + + // when the server pushes new inputs + assessmentTimeLimitWebsocket.onmessage = function(e){ - // when the server pushes new inputs - assessmentTimeLimitWebsocket.onmessage = function(e){ - // read JSON object - var input = JSON.parse(e.data); - - if (input.clearTimer == true) { - // teacher has stopped the timer, destroy it - $('#countdown').countdown('destroy').remove(); - counterInitialised = false; + // read JSON object + var input = JSON.parse(e.data); + + if (input.clearTimer == true) { + // teacher has stopped the timer, destroy it + $('#countdown').countdown('destroy').remove(); + counterInitialised = false; + } else { + // teacher has updated the timer + var secondsLeft = +input.secondsLeft; + if (counterInitialised) { + // just set the new time + $('#countdown').countdown('option', 'until', secondsLeft + 'S'); } else { - // teacher has updated the timer - var secondsLeft = +input.secondsLeft; - if (counterInitialised) { - // just set the new time - $('#countdown').countdown('option', 'until', secondsLeft + 'S'); - } else { - // initialise the timer - displayCountdown(secondsLeft); - } + // initialise the timer + displayCountdown(secondsLeft); } + } - // reset ping timer - clearTimeout(assessmentTimeLimitWebsocketPingTimeout); - assessmentTimeLimitWebsocketPingFunc(true); - }; - } + // reset ping timer + clearTimeout(assessmentTimeLimitWebsocketPingTimeout); + assessmentTimeLimitWebsocketPingFunc(true); + }; + } + + function displayCountdown(secondsLeft){ + counterIntialised = true; + var countdown = '
'; - function displayCountdown(secondsLeft){ - counterIntialised = true; - var countdown = '
'; + $.blockUI({ + message: countdown, + showOverlay: false, + focusInput: false, + css: { + top: '40px', + left: '', + right: '0%', + opacity: '.8', + width: '230px', + cursor: 'default', + border: 'none' + } + }); + + $('#countdown').countdown({ + until: '+' + secondsLeft +'S', + format: 'hMS', + compact: true, + alwaysExpire : true, + onTick: function(periods) { + //check for 30 seconds + if ((periods[4] == 0) && (periods[5] == 0) && (periods[6] <= 30)) { + $('#countdown').css('color', '#FF3333'); + } + }, + onExpiry: function(periods) { + $.blockUI({ message: '

' }); + + setTimeout(function() { + submitAll(true); + }, 4000); + }, + description: "
" + }); + } + + + /* + $(document).ready(function(){ + //show timelimit-start-dialog in order to start countdown + if (${sessionMap.isTimeLimitNotLaunched}) { - $.blockUI({ - message: countdown, - showOverlay: false, - focusInput: false, - css: { - top: '40px', - left: '', - right: '0%', - opacity: '.8', - width: '230px', - cursor: 'default', - border: 'none' - } + $.blockUI({ + message: $('#timelimit-start-dialog'), + css: { width: '325px', height: '120px'}, + overlayCSS: { opacity: '.98'} }); - $('#countdown').countdown({ - until: '+' + secondsLeft +'S', - format: 'hMS', - compact: true, - alwaysExpire : true, - onTick: function(periods) { - //check for 30 seconds - if ((periods[4] == 0) && (periods[5] == 0) && (periods[6] <= 30)) { - $('#countdown').css('color', '#FF3333'); - } - }, - onExpiry: function(periods) { - $.blockUI({ message: '

' }); - - setTimeout(function() { - submitAll(true); - }, 4000); - }, - description: "
" - }); - } + //once OK button pressed start countdown + $('#timelimit-start-ok').click(function() { + + //store date when user has started activity with time limit + $.ajax({ + async: true, + url: '', + data: 'sessionMapID=${sessionMapID}', + type: 'post' + }); + + $.unblockUI(); + initAssessmentTimeLimitWebsocket(); + isWaitingForConfirmation = false; + }); + } else { + initAssessmentTimeLimitWebsocket(); + } + });*/ - $(document).ready(function(){ - //show timelimit-start-dialog in order to start countdown - if (${sessionMap.isTimeLimitNotLaunched}) { - - $.blockUI({ - message: $('#timelimit-start-dialog'), - css: { width: '325px', height: '120px'}, - overlayCSS: { opacity: '.98'} - }); - - //once OK button pressed start countdown - $('#timelimit-start-ok').click(function() { - - //store date when user has started activity with time limit - $.ajax({ - async: true, - url: '', - data: 'sessionMapID=${sessionMapID}', - type: 'post' - }); - - $.unblockUI(); - initAssessmentTimeLimitWebsocket(); - isWaitingForConfirmation = false; - }); - - } else { - initAssessmentTimeLimitWebsocket(); - } - }); -
- //autosave feature function learnerAutosave(){ - if (isWaitingForConfirmation) return; + // if (isWaitingForConfirmation) return; //copy value from CKEditor (only available in essay type of questions) to textarea before ajax submit $("textarea[id^='question']").each(function() { Index: lams_tool_assessment/web/pages/monitoring/parts/timeLimit.jsp =================================================================== diff -u --- lams_tool_assessment/web/pages/monitoring/parts/timeLimit.jsp (revision 0) +++ lams_tool_assessment/web/pages/monitoring/parts/timeLimit.jsp (revision ca3fa00d5084315963b72ba5db58180fee898754) @@ -0,0 +1,70 @@ +<%@ include file="/common/taglibs.jsp"%> + +
+
+
+ + + +
+ + +
+ + + + + + + + + + + + + +
+ ${assessment.timeLimit}  + + +
+ +
+
+ +
+
+ + + + + + + + + + +
+
+
+
Index: lams_tool_assessment/web/pages/monitoring/summary.jsp =================================================================== diff -u -r53429b6384ba7befc3a644a91d7a1389ae5a7dfa -rca3fa00d5084315963b72ba5db58180fee898754 --- lams_tool_assessment/web/pages/monitoring/summary.jsp (.../summary.jsp) (revision 53429b6384ba7befc3a644a91d7a1389ae5a7dfa) +++ lams_tool_assessment/web/pages/monitoring/summary.jsp (.../summary.jsp) (revision ca3fa00d5084315963b72ba5db58180fee898754) @@ -289,7 +289,6 @@ jqgrids.each(function(index) { var gridId = $(this).attr('id'); var gridParentWidth = jQuery('#gbox_' + gridId).parent().width(); - console.log(gridParentWidth); jQuery('#' + gridId).setGridWidth(gridParentWidth, true); }); }; @@ -302,6 +301,77 @@ var url = "?&sessionMapID=${sessionMapID}&reqID="+(new Date()).getTime(); return downloadFile(url, 'messageArea_Busy', '', 'messageArea', 'btn-disable-on-submit'); }; + + var relativeTimeLimit = ${assessment.timeLimit}; + + function timeLimitControl(type, toggle, adjust) { + if (type == 'relative') { + var relativeTimeLimitSpan = $('#relative-time-limit-value'), + displayedRelativeTimeLimit = +relativeTimeLimitSpan.text(); + + if (toggle !== null) { + + if (toggle === false) { + displayedRelativeTimeLimit = relativeTimeLimit = 0; + callTimeLimitController(function(){ + relativeTimeLimitSpan.text(displayedRelativeTimeLimit); + $('#relative-time-limit-disabled').removeClass('hidden'); + $('#relative-time-limit-cancel').addClass('hidden'); + $('#relative-time-limit-enabled').addClass('hidden'); + $('#relative-time-limit-start').removeClass('hidden').addClass('disabled'); + }); + return; + } + + if (toggle === true && displayedRelativeTimeLimit > 0) { + relativeTimeLimit = displayedRelativeTimeLimit; + + callTimeLimitController(function(){ + relativeTimeLimitSpan.text(displayedRelativeTimeLimit); + $('#relative-time-limit-disabled').addClass('hidden'); + $('#relative-time-limit-cancel').removeClass('hidden'); + $('#relative-time-limit-enabled').removeClass('hidden'); + $('#relative-time-limit-start').addClass('hidden').removeClass('disabled'); + }); + } + return; + } + + var adjustedRelativeTimeLimit = displayedRelativeTimeLimit + adjust; + if (adjustedRelativeTimeLimit < 1) { + displayedRelativeTimeLimit = 1; + relativeTimeLimitSpan.text(displayedRelativeTimeLimit); + return; + } + + if (relativeTimeLimit > 0) { + relativeTimeLimit = displayedRelativeTimeLimit = adjustedRelativeTimeLimit; + callTimeLimitController(function(){ + relativeTimeLimitSpan.text(displayedRelativeTimeLimit); + }); + return; + } + + displayedRelativeTimeLimit = adjustedRelativeTimeLimit; + relativeTimeLimitSpan.text(displayedRelativeTimeLimit); + $('#relative-time-limit-start').removeClass('disabled'); + } + } + + function callTimeLimitController(callback) { + $.ajax({ + 'url' : '', + 'type': 'post', + 'dataType' : 'text', + 'cache' : 'false', + 'data': { + 'toolContentID' : '${assessment.contentId}', + 'relativeTimeLimit' : relativeTimeLimit, + '' : '' + }, + success : callback + }); + }
@@ -446,4 +516,6 @@ <%@ include file="parts/advanceoptions.jsp"%> +<%@ include file="parts/timeLimit.jsp"%> + <%@ include file="parts/dateRestriction.jsp"%>