Index: lams_tool_assessment/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -rca3fa00d5084315963b72ba5db58180fee898754 -r29bc39cd5828fbffdc6a414fd18b91e6a89f66c4 --- lams_tool_assessment/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision ca3fa00d5084315963b72ba5db58180fee898754) +++ lams_tool_assessment/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 29bc39cd5828fbffdc6a414fd18b91e6a89f66c4) @@ -405,10 +405,8 @@ 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/dbupdates/patch20200617.sql =================================================================== diff -u --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dbupdates/patch20200617.sql (revision 0) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dbupdates/patch20200617.sql (revision 29bc39cd5828fbffdc6a414fd18b91e6a89f66c4) @@ -0,0 +1,16 @@ +-- Turn off autocommit, so nothing is committed if there is an error +SET AUTOCOMMIT = 0; +SET FOREIGN_KEY_CHECKS=0; +-- Put all sql statements below here + +--LDEV-5041 Add absolute time limit in Assessment + +ALTER TABLE tl_laasse10_assessment CHANGE COLUMN time_limit relative_time_limit SMALLINT UNSIGNED NOT NULL DEFAULT 0; +ALTER TABLE tl_laasse10_assessment ADD COLUMN absolute_time_limit DATETIME AFTER relative_time_limit; + +-- Put all sql statements above here + +-- If there were no errors, commit and restore autocommit to on +COMMIT; +SET AUTOCOMMIT = 1; +SET FOREIGN_KEY_CHECKS=1; Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/model/Assessment.java =================================================================== diff -u -rce97cec9b77669b1a07fe59949d2bd4d0aa3f901 -r29bc39cd5828fbffdc6a414fd18b91e6a89f66c4 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/model/Assessment.java (.../Assessment.java) (revision ce97cec9b77669b1a07fe59949d2bd4d0aa3f901) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/model/Assessment.java (.../Assessment.java) (revision 29bc39cd5828fbffdc6a414fd18b91e6a89f66c4) @@ -23,6 +23,8 @@ package org.lamsfoundation.lams.tool.assessment.model; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.Date; import java.util.Iterator; import java.util.Set; @@ -78,9 +80,12 @@ @Column(name = "use_select_leader_tool_ouput") private boolean useSelectLeaderToolOuput; - @Column(name = "time_limit") - private int timeLimit; + @Column(name = "relative_time_limit") + private int relativeTimeLimit; + @Column(name = "absolute_time_limit") + private LocalDateTime absoluteTimeLimit; + @Column(name = "questions_per_page") private int questionsPerPage; @@ -424,18 +429,30 @@ /** * @return Returns the time limitation, that students have to complete an attempt. */ - public int getTimeLimit() { - return timeLimit; + public int getRelativeTimeLimit() { + return relativeTimeLimit; } /** * @param timeLimit * the time limitation, that students have to complete an attempt. */ - public void setTimeLimit(int timeLimit) { - this.timeLimit = timeLimit; + public void setRelativeTimeLimit(int timeLimit) { + this.relativeTimeLimit = timeLimit; } + public LocalDateTime getAbsoluteTimeLimit() { + return absoluteTimeLimit; + } + + public Long getAbsoluteTimeLimitSeconds() { + return absoluteTimeLimit == null ? null : absoluteTimeLimit.atZone(ZoneId.systemDefault()).toEpochSecond(); + } + + public void setAbsoluteTimeLimit(LocalDateTime absoluteTimeLimit) { + this.absoluteTimeLimit = absoluteTimeLimit; + } + /** * @return Returns the instructions set by the teacher. * Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java =================================================================== diff -u -r998ba383ec2a06647d309f910ebefe0a33fa30a4 -r29bc39cd5828fbffdc6a414fd18b91e6a89f66c4 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision 998ba383ec2a06647d309f910ebefe0a33fa30a4) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision 29bc39cd5828fbffdc6a414fd18b91e6a89f66c4) @@ -3401,7 +3401,7 @@ assessment.setReflectOnActivity( JsonUtil.optBoolean(toolContentJSON, RestTags.REFLECT_ON_ACTIVITY, Boolean.FALSE)); assessment.setShuffled(JsonUtil.optBoolean(toolContentJSON, "shuffled", Boolean.FALSE)); - assessment.setTimeLimit(JsonUtil.optInt(toolContentJSON, "timeLimit", 0)); + assessment.setRelativeTimeLimit(JsonUtil.optInt(toolContentJSON, "timeLimit", 0)); assessment.setUseSelectLeaderToolOuput( JsonUtil.optBoolean(toolContentJSON, RestTags.USE_SELECT_LEADER_TOOL_OUTPUT, Boolean.FALSE)); // submission deadline set in monitoring Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/LearningController.java =================================================================== diff -u -r998ba383ec2a06647d309f910ebefe0a33fa30a4 -r29bc39cd5828fbffdc6a414fd18b91e6a89f66c4 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/LearningController.java (.../LearningController.java) (revision 998ba383ec2a06647d309f910ebefe0a33fa30a4) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/LearningController.java (.../LearningController.java) (revision 29bc39cd5828fbffdc6a414fd18b91e6a89f66c4) @@ -178,7 +178,7 @@ // forwards to the waitForLeader pages boolean isNonLeader = !user.getUserId().equals(groupLeader.getUserId()); - if (assessment.getTimeLimit() != 0 && isNonLeader && !isLastAttemptFinishedByLeader) { + if (assessment.getRelativeTimeLimit() != 0 && isNonLeader && !isLastAttemptFinishedByLeader) { //show waitForLeaderLaunchTimeLimit page if the leader hasn't started activity or hasn't pressed OK button to launch time limit if (lastLeaderResult == null || lastLeaderResult.getTimeLimitLaunchedDate() == null) { Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/LearningWebsocketServer.java =================================================================== diff -u -rca3fa00d5084315963b72ba5db58180fee898754 -r29bc39cd5828fbffdc6a414fd18b91e6a89f66c4 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/LearningWebsocketServer.java (.../LearningWebsocketServer.java) (revision ca3fa00d5084315963b72ba5db58180fee898754) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/LearningWebsocketServer.java (.../LearningWebsocketServer.java) (revision 29bc39cd5828fbffdc6a414fd18b91e6a89f66c4) @@ -44,6 +44,8 @@ private static class TimeCache { private int relativeTimeLimit; + private LocalDateTime absoluteTimeLimit; + // mapping of user ID (not UID) and when the learner entered the activity private final Map timeLimitLaunchedDate = new ConcurrentHashMap<>(); } @@ -75,17 +77,26 @@ .getAssessmentByContentId(toolContentId); long assessmentUid = assessment.getUid(); TimeCache timeCache = timeCaches.get(toolContentId); + // first time a learner entered the activity, so there is not cache yet if (timeCache == null) { timeCache = new TimeCache(); timeCaches.put(toolContentId, timeCache); } boolean updateAllUsers = false; - int existingRelativeTimeLimit = assessment.getTimeLimit() * 60; + // compare relative and absolute time limits with cache + // it they changed, update all learners + int existingRelativeTimeLimit = assessment.getRelativeTimeLimit() * 60; if (timeCache.relativeTimeLimit != existingRelativeTimeLimit) { timeCache.relativeTimeLimit = existingRelativeTimeLimit; updateAllUsers = true; } + LocalDateTime existingAbsoluteTimeLimit = assessment.getAbsoluteTimeLimit(); + if (timeCache.absoluteTimeLimit == null ? existingAbsoluteTimeLimit != null + : !timeCache.absoluteTimeLimit.equals(existingAbsoluteTimeLimit)) { + timeCache.absoluteTimeLimit = existingAbsoluteTimeLimit; + updateAllUsers = true; + } for (Session websocket : entry.getValue()) { String login = websocket.getUserPrincipal().getName(); @@ -94,18 +105,21 @@ long userId = user.getUserId(); boolean updateUser = updateAllUsers; - if (timeCache.relativeTimeLimit > 0) { + // check if there is a point in updating learner launch date + if (timeCache.relativeTimeLimit > 0 || timeCache.absoluteTimeLimit != null) { AssessmentResult result = LearningWebsocketServer.getAssessmentService() .getLastAssessmentResult(assessmentUid, userId); if (result == null) { continue; } LocalDateTime existingLaunchDate = result.getTimeLimitLaunchedDate(); if (existingLaunchDate == null) { + // learner entered the activity, so store his launch date in cache and DB existingLaunchDate = assessmentService.launchTimeLimit(assessmentUid, userId); } LocalDateTime launchedDate = timeCache.timeLimitLaunchedDate.get(userId); + // user (re)entered the activity, so update him with time limit if (launchedDate == null || !launchedDate.equals(existingLaunchDate)) { updateUser = true; timeCache.timeLimitLaunchedDate.put(userId, existingLaunchDate); @@ -141,7 +155,7 @@ private static IAssessmentService assessmentService; static { - // run the singleton thread + // run the singleton thread in given periods executor.scheduleAtFixedRate(sendWorker, 0, CHECK_INTERVAL, TimeUnit.SECONDS); } @@ -169,6 +183,7 @@ TimeCache timeCache = timeCaches.get(toolContentID); if (timeCache != null) { + // clear cached learner data, so he gets updated with current time limit via websocket timeCache.timeLimitLaunchedDate.remove(user.getUserId()); } @@ -204,12 +219,24 @@ } private static Long getSecondsLeft(TimeCache timeCache, long userUid) { - if (timeCache.relativeTimeLimit == 0) { + if (timeCache.relativeTimeLimit == 0 && timeCache.absoluteTimeLimit == null) { + // no time limit is set at all return null; } + // when user entered the activity + LocalDateTime launchedDate = timeCache.timeLimitLaunchedDate.get(userUid); + // what is the time limit for him + LocalDateTime finish = null; + if (timeCache.absoluteTimeLimit != null) { + // the limit is same for everyone + finish = timeCache.absoluteTimeLimit; + } else { + // the limit is his entry plus relative time limit + finish = launchedDate.plusSeconds(timeCache.relativeTimeLimit); + } + LocalDateTime now = LocalDateTime.now(); - LocalDateTime finish = timeCache.timeLimitLaunchedDate.get(userUid).plusSeconds(timeCache.relativeTimeLimit); long secondsLeft = Duration.between(now, finish).toSeconds(); return Math.max(0, secondsLeft); @@ -218,6 +245,7 @@ private static void sendUpdate(Session websocket, Long secondsLeft) throws IOException { ObjectNode responseJSON = JsonNodeFactory.instance.objectNode(); if (secondsLeft == null) { + // time limit feature was disabled, so destroy counter on learner screen responseJSON.put("clearTimer", true); } else { responseJSON.put("secondsLeft", secondsLeft); Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java =================================================================== diff -u -rca3fa00d5084315963b72ba5db58180fee898754 -r29bc39cd5828fbffdc6a414fd18b91e6a89f66c4 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java (.../MonitoringController.java) (revision ca3fa00d5084315963b72ba5db58180fee898754) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java (.../MonitoringController.java) (revision 29bc39cd5828fbffdc6a414fd18b91e6a89f66c4) @@ -25,6 +25,8 @@ import java.io.IOException; import java.security.InvalidParameterException; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -810,16 +812,24 @@ } } - @RequestMapping(path = "/timeLimitControl", method = RequestMethod.POST) + @RequestMapping(path = "/updateTimeLimit", method = RequestMethod.POST) @ResponseStatus(HttpStatus.OK) - public void timeLimitControl(@RequestParam(name = AssessmentConstants.PARAM_TOOL_CONTENT_ID) long toolContentId, - @RequestParam int relativeTimeLimit) { + public void updateTimeLimit(@RequestParam(name = AssessmentConstants.PARAM_TOOL_CONTENT_ID) long toolContentId, + @RequestParam int relativeTimeLimit, @RequestParam(required = false) Long absoluteTimeLimit) { if (relativeTimeLimit < 0) { throw new InvalidParameterException( "Relative time limit must not be negative and it is " + relativeTimeLimit); } + if (absoluteTimeLimit != null && relativeTimeLimit != 0) { + throw new InvalidParameterException( + "Relative time limit must not be provided when absolute time limit is set"); + } + Assessment assessment = service.getAssessmentByContentId(toolContentId); - assessment.setTimeLimit(relativeTimeLimit); + assessment.setRelativeTimeLimit(relativeTimeLimit); + // set time limit as seconds from start of epoch, using current server time zone + assessment.setAbsoluteTimeLimit(absoluteTimeLimit == null ? null + : LocalDateTime.ofEpochSecond(absoluteTimeLimit, 0, OffsetDateTime.now().getOffset())); service.saveOrUpdateAssessment(assessment); } Index: lams_tool_assessment/web/pages/learning/learning.jsp =================================================================== diff -u -rca3fa00d5084315963b72ba5db58180fee898754 -r29bc39cd5828fbffdc6a414fd18b91e6a89f66c4 --- lams_tool_assessment/web/pages/learning/learning.jsp (.../learning.jsp) (revision ca3fa00d5084315963b72ba5db58180fee898754) +++ lams_tool_assessment/web/pages/learning/learning.jsp (.../learning.jsp) (revision 29bc39cd5828fbffdc6a414fd18b91e6a89f66c4) @@ -12,7 +12,9 @@ - +<%-- + + --%> @@ -40,6 +42,10 @@ [data-toggle="collapse"].collapsed .if-not-collapsed, [data-toggle="collapse"]:not(.collapsed) .if-collapsed { display: none; } + + .countdown-timeout { + color: #FF3333 !important; + } @@ -127,7 +133,7 @@ - //timelimit feature + // TIME LIMIT // websocket needs pinging and reconnection feature in case it fails // it works pretty much the same as command websocket in Page.tag @@ -180,12 +186,13 @@ var input = JSON.parse(e.data); if (input.clearTimer == true) { - // teacher has stopped the timer, destroy it + // teacher stopped the timer, destroy it $('#countdown').countdown('destroy').remove(); - counterInitialised = false; } else { - // teacher has updated the timer - var secondsLeft = +input.secondsLeft; + // teacher updated the timer + var secondsLeft = +input.secondsLeft, + counterInitialised = $('#countdown').length > 0; + if (counterInitialised) { // just set the new time $('#countdown').countdown('option', 'until', secondsLeft + 'S'); @@ -202,7 +209,6 @@ } function displayCountdown(secondsLeft){ - counterIntialised = true; var countdown = '
'; $.blockUI({ @@ -226,10 +232,13 @@ 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'); - } + // check for 30 seconds or less and display timer in red + var secondsLeft = $.countdown.periodsToSeconds(periods); + if (secondsLeft <= 30) { + $(this).addClass('countdown-timeout'); + } else { + $(this).removeClass('countdown-timeout'); + } }, onExpiry: function(periods) { $.blockUI({ message: '

' }); Index: lams_tool_assessment/web/pages/monitoring/monitoring.jsp =================================================================== diff -u -re6dc4db4137cfd6b07a4aa79711b9d12b39fb78e -r29bc39cd5828fbffdc6a414fd18b91e6a89f66c4 --- lams_tool_assessment/web/pages/monitoring/monitoring.jsp (.../monitoring.jsp) (revision e6dc4db4137cfd6b07a4aa79711b9d12b39fb78e) +++ lams_tool_assessment/web/pages/monitoring/monitoring.jsp (.../monitoring.jsp) (revision 29bc39cd5828fbffdc6a414fd18b91e6a89f66c4) @@ -14,10 +14,15 @@ + @@ -46,12 +51,14 @@ + + Index: lams_tool_assessment/web/pages/monitoring/parts/timeLimit.jsp =================================================================== diff -u -rca3fa00d5084315963b72ba5db58180fee898754 -r29bc39cd5828fbffdc6a414fd18b91e6a89f66c4 --- lams_tool_assessment/web/pages/monitoring/parts/timeLimit.jsp (.../timeLimit.jsp) (revision ca3fa00d5084315963b72ba5db58180fee898754) +++ lams_tool_assessment/web/pages/monitoring/parts/timeLimit.jsp (.../timeLimit.jsp) (revision 29bc39cd5828fbffdc6a414fd18b91e6a89f66c4) @@ -10,15 +10,16 @@ - + +
+ + + + + + + + + + + + +
- ${assessment.timeLimit}  + ${assessment.relativeTimeLimit}  @@ -31,39 +32,88 @@ - - - -
+
+ +
+
+ +
+
+ + + + + + + + + + +
Index: lams_tool_assessment/web/pages/monitoring/summary.jsp =================================================================== diff -u -rca3fa00d5084315963b72ba5db58180fee898754 -r29bc39cd5828fbffdc6a414fd18b91e6a89f66c4 --- lams_tool_assessment/web/pages/monitoring/summary.jsp (.../summary.jsp) (revision ca3fa00d5084315963b72ba5db58180fee898754) +++ lams_tool_assessment/web/pages/monitoring/summary.jsp (.../summary.jsp) (revision 29bc39cd5828fbffdc6a414fd18b91e6a89f66c4) @@ -283,6 +283,11 @@ // trigger the resize when the window first opens so that the grid uses all the space available. setTimeout(function(){ window.dispatchEvent(new Event('resize')); }, 300); + + // create counter if absolute time limit is set + if (absoluteTimeLimit) { + updateAbsoluteTimeLimitCounter(); + } }); function resizeJqgrid(jqgrids) { @@ -302,76 +307,217 @@ return downloadFile(url, 'messageArea_Busy', '', 'messageArea', 'btn-disable-on-submit'); }; - var relativeTimeLimit = ${assessment.timeLimit}; - function timeLimitControl(type, toggle, adjust) { + // TIME LIMIT + + // in minutes since learner entered the activity + var relativeTimeLimit = ${assessment.relativeTimeLimit}, + // in seconds since epoch started + absoluteTimeLimit = ${empty assessment.absoluteTimeLimit ? 'null' : assessment.absoluteTimeLimitSeconds}; + + function updateTimeLimit(type, toggle, adjust) { + // relavite time limit set if (type == 'relative') { - var relativeTimeLimitSpan = $('#relative-time-limit-value'), - displayedRelativeTimeLimit = +relativeTimeLimitSpan.text(); + // what is set at the moment on screen, not at server + var displayedRelativeTimeLimit = +$('#relative-time-limit-value').text(); + // start/stop 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'); - }); + // stop, i.e. set time limit to 0 + relativeTimeLimit = 0; + updateTimeLimitOnServer(); return; } + // start, i.e. set backend time limit to whatever is set on screen 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'); - }); + // when teacher enables relative time limit, absolute one gets disabled + absoluteTimeLimit = null; + updateTimeLimitOnServer(); } return; } + // no negative time limit is allowed + if (displayedRelativeTimeLimit == 0 && adjust < 0){ + return; + } + var adjustedRelativeTimeLimit = displayedRelativeTimeLimit + adjust; + // at least one minute is required + // if teacher wants to set less, he should disable the limit or click "finish now" if (adjustedRelativeTimeLimit < 1) { - displayedRelativeTimeLimit = 1; - relativeTimeLimitSpan.text(displayedRelativeTimeLimit); - return; + adjustedRelativeTimeLimit = 1; } + // is time limit already enforced? if so, update the server if (relativeTimeLimit > 0) { - relativeTimeLimit = displayedRelativeTimeLimit = adjustedRelativeTimeLimit; - callTimeLimitController(function(){ - relativeTimeLimitSpan.text(displayedRelativeTimeLimit); - }); + relativeTimeLimit = adjustedRelativeTimeLimit; + updateTimeLimitOnServer(); return; } + // if time limit is not enforced yet, just update the screen displayedRelativeTimeLimit = adjustedRelativeTimeLimit; - relativeTimeLimitSpan.text(displayedRelativeTimeLimit); + $('#relative-time-limit-value').text(displayedRelativeTimeLimit); $('#relative-time-limit-start').removeClass('disabled'); + return; } + + if (type == 'absolute') { + // get existing value on counter, if it is set already + var counter = $('#absolute-time-limit-counter'), + secondsLeft = null; + if (counter.length === 1) { + var periods = counter.countdown('getTimes'); + secondsLeft = $.countdown.periodsToSeconds(periods); + } + + if (toggle !== null) { + + // start/stop + if (toggle === false) { + absoluteTimeLimit = null; + updateAbsoluteTimeLimitCounter(); + return; + } + + // turn on the time limit, if there is any value on counter set already + if (toggle === true && secondsLeft) { + updateAbsoluteTimeLimitCounter(secondsLeft, true); + } + return; + } + + // counter is not set yet and user clicked negative value + if (!secondsLeft && adjust < 0){ + return; + } + + // adjust time + secondsLeft += adjust * 60; + if (secondsLeft < 60) { + secondsLeft = 60; + } + + // is time limit already enforced, update the server + // if time limit is not enforced yet, just update the screen + updateAbsoluteTimeLimitCounter(secondsLeft); + $('#absolute-time-limit-start').removeClass('disabled'); + return; + } } - function callTimeLimitController(callback) { + function updateTimeLimitOnServer() { + + // absolute time limit has higher priority + if (absoluteTimeLimit != null) { + relativeTimeLimit = 0; + } + $.ajax({ - 'url' : '', + 'url' : '', 'type': 'post', - 'dataType' : 'text', 'cache' : 'false', 'data': { 'toolContentID' : '${assessment.contentId}', 'relativeTimeLimit' : relativeTimeLimit, + 'absoluteTimeLimit' : absoluteTimeLimit, '' : '' }, - success : callback + success : function(){ + // update widgets + $('#relative-time-limit-value').text(relativeTimeLimit); + + if (relativeTimeLimit > 0) { + $('#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'); + } else { + $('#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'); + } + + if (absoluteTimeLimit === null) { + // no absolute time limit? destroy the counter + $('#absolute-time-limit-counter').countdown('destroy'); + $('#absolute-time-limit-value').empty(); + + $('#absolute-time-limit-disabled').removeClass('hidden'); + $('#absolute-time-limit-cancel').addClass('hidden'); + $('#absolute-time-limit-enabled').addClass('hidden'); + $('#absolute-time-limit-start').removeClass('hidden').addClass('disabled'); + } else { + $('#absolute-time-limit-disabled').addClass('hidden'); + $('#absolute-time-limit-cancel').removeClass('hidden'); + $('#absolute-time-limit-enabled').removeClass('hidden'); + $('#absolute-time-limit-start').addClass('hidden').removeClass('disabled'); + } + } }); } + + function updateAbsoluteTimeLimitCounter(secondsLeft, start) { + var now = Math.round(new Date().getTime() / 1000), + // preset means that counter is set just on screen and the time limit is not enforced for learners + preset = start !== true && absoluteTimeLimit == null; + + if (secondsLeft) { + if (!preset) { + // time limit is already enforced on server, so update it there now + absoluteTimeLimit = now + secondsLeft; + updateTimeLimitOnServer(); + } + } else { + if (absoluteTimeLimit == null) { + // disable the counter + updateTimeLimitOnServer(); + return; + } + // counter initialisation on page load + secondsLeft = absoluteTimeLimit - now; + } + + var counter = $('#absolute-time-limit-counter'); + + if (counter.length == 0) { + counter = $('
').attr('id', 'absolute-time-limit-counter').appendTo('#absolute-time-limit-value') + .countdown({ + until: '+' + secondsLeft +'S', + format: 'hMS', + compact: true, + alwaysExpire : true, + onTick: function(periods) { + // check for 30 seconds or less and display timer in red + var secondsLeft = $.countdown.periodsToSeconds(periods); + if (secondsLeft <= 30) { + counter.addClass('countdown-timeout'); + } else { + counter.removeClass('countdown-timeout'); + } + }, + expiryText : 'Expired' + }); + } else { + // if counter is paused, we can not adjust time, so resume it for a moment + counter.countdown('resume'); + counter.countdown('option', 'until', secondsLeft + 'S'); + } + + if (preset) { + counter.countdown('pause'); + $('#absolute-time-limit-start').removeClass('disabled'); + } else { + counter.countdown('resume'); + } + } +