Index: lams_central/conf/security/Owasp.CsrfGuard.properties =================================================================== diff -u -rca3fa00d5084315963b72ba5db58180fee898754 -r0336d51e6688a15639cb7d414223e947a1e8d118 --- lams_central/conf/security/Owasp.CsrfGuard.properties (.../Owasp.CsrfGuard.properties) (revision ca3fa00d5084315963b72ba5db58180fee898754) +++ lams_central/conf/security/Owasp.CsrfGuard.properties (.../Owasp.CsrfGuard.properties) (revision 0336d51e6688a15639cb7d414223e947a1e8d118) @@ -128,7 +128,8 @@ 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.assessmentUpdateTimeLimit=/lams/tool/laasse10/monitoring/updateTimeLimit.do +org.owasp.csrfguard.protected.assessmentUpdateIndividualTimeLimit=/lams/tool/laasse10/monitoring/updateIndividualTimeLimit.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 -re321be7f93ea35c8418b5de59bc1806d985fd80f -r0336d51e6688a15639cb7d414223e947a1e8d118 --- lams_tool_assessment/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision e321be7f93ea35c8418b5de59bc1806d985fd80f) +++ lams_tool_assessment/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 0336d51e6688a15639cb7d414223e947a1e8d118) @@ -397,7 +397,6 @@ 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.start = Start @@ -408,8 +407,8 @@ label.monitoring.summary.time.limit.minus.minute.5 = Minus 5 minutes label.monitoring.summary.time.limit.finish.now = Finish now label.monitoring.summary.time.limit.finish.now.confirm = Are you sure you want to make all learners finish their work right now? +label.monitoring.summary.time.limit.individual.placeholder = Type user name - #======= End labels: Exported 372 labels for en AU ===== Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/AssessmentUserDAO.java =================================================================== diff -u -r998ba383ec2a06647d309f910ebefe0a33fa30a4 -r0336d51e6688a15639cb7d414223e947a1e8d118 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/AssessmentUserDAO.java (.../AssessmentUserDAO.java) (revision 998ba383ec2a06647d309f910ebefe0a33fa30a4) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/AssessmentUserDAO.java (.../AssessmentUserDAO.java) (revision 0336d51e6688a15639cb7d414223e947a1e8d118) @@ -58,4 +58,8 @@ List getRawLeaderMarksByToolContentId(Long toolContentId); Object[] getStatsMarksForLeaders(Long toolContentId); + + List getExistingIndividualTimeLimitUsers(long toolContentId); + + List getPossibleIndividualTimeLimitUsers(long toolContentId, String searchString); } Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentUserDAOHibernate.java =================================================================== diff -u -r998ba383ec2a06647d309f910ebefe0a33fa30a4 -r0336d51e6688a15639cb7d414223e947a1e8d118 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentUserDAOHibernate.java (.../AssessmentUserDAOHibernate.java) (revision 998ba383ec2a06647d309f910ebefe0a33fa30a4) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentUserDAOHibernate.java (.../AssessmentUserDAOHibernate.java) (revision 0336d51e6688a15639cb7d414223e947a1e8d118) @@ -62,6 +62,16 @@ + " JOIN tl_laasse10_assessment a ON s.assessment_uid = a.uid " + " WHERE r.finish_date IS NOT NULL AND r.latest = 1 AND a.content_id = :toolContentId"; + private static final String FIND_POSSIBLE_INDIVIDUAL_TIME_LIMIT_USERS = "FROM " + AssessmentUser.class.getName() + + " AS u WHERE u.timeLimitAdjustment IS NULL AND u.assessment.contentId = :toolContentId " + + "AND (u.firstName LIKE CONCAT('%', :searchString, '%') " + + "OR u.lastName LIKE CONCAT('%', :searchString, '%') " + + "OR u.loginName LIKE CONCAT('%', :searchString, '%')) ORDER BY u.firstName, u.lastName"; + + private static final String FIND_EXISTING_INDIVIDUAL_TIME_LIMIT_USERS = "FROM " + AssessmentUser.class.getName() + + " AS u WHERE u.timeLimitAdjustment IS NOT NULL AND u.assessment.contentId = :toolContentId " + + " ORDER BY u.firstName, u.lastName"; + @SuppressWarnings("rawtypes") @Override public AssessmentUser getUserByUserIDAndSessionID(Long userID, Long sessionId) { @@ -323,4 +333,16 @@ return list; } + @Override + public List getPossibleIndividualTimeLimitUsers(long toolContentId, String searchString) { + return getSession().createQuery(FIND_POSSIBLE_INDIVIDUAL_TIME_LIMIT_USERS, AssessmentUser.class) + .setParameter("toolContentId", toolContentId).setParameter("searchString", searchString) + .getResultList(); + } + + @Override + public List getExistingIndividualTimeLimitUsers(long toolContentId) { + return getSession().createQuery(FIND_EXISTING_INDIVIDUAL_TIME_LIMIT_USERS, AssessmentUser.class) + .setParameter("toolContentId", toolContentId).getResultList(); + } } Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dbupdates/patch20200617.sql =================================================================== diff -u -r29bc39cd5828fbffdc6a414fd18b91e6a89f66c4 -r0336d51e6688a15639cb7d414223e947a1e8d118 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dbupdates/patch20200617.sql (.../patch20200617.sql) (revision 29bc39cd5828fbffdc6a414fd18b91e6a89f66c4) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dbupdates/patch20200617.sql (.../patch20200617.sql) (revision 0336d51e6688a15639cb7d414223e947a1e8d118) @@ -8,6 +8,8 @@ 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; +ALTER TABLE tl_laasse10_user ADD COLUMN time_limit_adjustment SMALLINT; + -- Put all sql statements above here -- If there were no errors, commit and restore autocommit to on Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/model/AssessmentUser.java =================================================================== diff -u -r1ee503e3d0e0228ea8a45025fddf15d9623c0377 -r0336d51e6688a15639cb7d414223e947a1e8d118 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/model/AssessmentUser.java (.../AssessmentUser.java) (revision 1ee503e3d0e0228ea8a45025fddf15d9623c0377) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/model/AssessmentUser.java (.../AssessmentUser.java) (revision 0336d51e6688a15639cb7d414223e947a1e8d118) @@ -79,6 +79,9 @@ @JoinColumn(name = "assessment_uid") private Assessment assessment; + @Column(name = "time_limit_adjustment") + private Integer timeLimitAdjustment; + // *************** NON Persist Fields ******************** // the user access some reousrce question date time. Use in monitoring summary page @@ -234,6 +237,14 @@ this.sessionFinished = sessionFinished; } + public Integer getTimeLimitAdjustment() { + return timeLimitAdjustment; + } + + public void setTimeLimitAdjustment(Integer timeLimitAdjustment) { + this.timeLimitAdjustment = timeLimitAdjustment; + } + @Override public boolean equals(Object obj) { if (this == obj) { Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java =================================================================== diff -u -r1ff532f29d381dbae40ea6f179058b5bd6c01b9b -r0336d51e6688a15639cb7d414223e947a1e8d118 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision 1ff532f29d381dbae40ea6f179058b5bd6c01b9b) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision 0336d51e6688a15639cb7d414223e947a1e8d118) @@ -3650,4 +3650,14 @@ public Collection getAllGroupUsers(Long toolSessionId) { return toolService.getToolSession(toolSessionId).getLearners(); } -} + + @Override + public List getExistingIndividualTimeLimitUsers(long toolContentId) { + return assessmentUserDao.getExistingIndividualTimeLimitUsers(toolContentId); + } + + @Override + public List getPossibleIndividualTimeLimitUsers(long toolContentId, String searchString) { + return assessmentUserDao.getPossibleIndividualTimeLimitUsers(toolContentId, searchString); + } +} \ No newline at end of file Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/IAssessmentService.java =================================================================== diff -u -r1ff532f29d381dbae40ea6f179058b5bd6c01b9b -r0336d51e6688a15639cb7d414223e947a1e8d118 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/IAssessmentService.java (.../IAssessmentService.java) (revision 1ff532f29d381dbae40ea6f179058b5bd6c01b9b) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/IAssessmentService.java (.../IAssessmentService.java) (revision 0336d51e6688a15639cb7d414223e947a1e8d118) @@ -539,4 +539,8 @@ String getConfigValue(String key); Collection getAllGroupUsers(Long toolSessionId); + + List getExistingIndividualTimeLimitUsers(long toolContentId); + + List getPossibleIndividualTimeLimitUsers(long toolContentId, String searchString); } \ No newline at end of file Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/LearningWebsocketServer.java =================================================================== diff -u -r1ff532f29d381dbae40ea6f179058b5bd6c01b9b -r0336d51e6688a15639cb7d414223e947a1e8d118 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/LearningWebsocketServer.java (.../LearningWebsocketServer.java) (revision 1ff532f29d381dbae40ea6f179058b5bd6c01b9b) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/LearningWebsocketServer.java (.../LearningWebsocketServer.java) (revision 0336d51e6688a15639cb7d414223e947a1e8d118) @@ -3,6 +3,7 @@ import java.io.IOException; import java.time.Duration; import java.time.LocalDateTime; +import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; @@ -11,6 +12,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import javax.websocket.CloseReason; import javax.websocket.CloseReason.CloseCodes; @@ -47,6 +49,7 @@ private LocalDateTime absoluteTimeLimit; // mapping of user ID (not UID) and when the learner entered the activity private final Map timeLimitLaunchedDate = new ConcurrentHashMap<>(); + private Map timeLimitAdjustment = new HashMap<>(); } /** @@ -98,6 +101,14 @@ updateAllUsers = true; } + Map existingTimeLimitAdjustment = LearningWebsocketServer.getAssessmentService() + .getExistingIndividualTimeLimitUsers(toolContentId).stream().collect(Collectors + .toMap(AssessmentUser::getUserId, AssessmentUser::getTimeLimitAdjustment)); + if (!existingTimeLimitAdjustment.equals(timeCache.timeLimitAdjustment)) { + timeCache.timeLimitAdjustment = existingTimeLimitAdjustment; + updateAllUsers = true; + } + for (Session websocket : entry.getValue()) { String login = websocket.getUserPrincipal().getName(); AssessmentUser user = LearningWebsocketServer.getAssessmentService() @@ -239,6 +250,11 @@ LocalDateTime now = LocalDateTime.now(); long secondsLeft = Duration.between(now, finish).toSeconds(); + Integer adjustment = timeCache.timeLimitAdjustment.get(userId); + if (adjustment != null) { + secondsLeft += adjustment * 60; + } + return Math.max(0, secondsLeft); } Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java =================================================================== diff -u -r29bc39cd5828fbffdc6a414fd18b91e6a89f66c4 -r0336d51e6688a15639cb7d414223e947a1e8d118 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java (.../MonitoringController.java) (revision 29bc39cd5828fbffdc6a414fd18b91e6a89f66c4) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java (.../MonitoringController.java) (revision 0336d51e6688a15639cb7d414223e947a1e8d118) @@ -78,6 +78,7 @@ import org.lamsfoundation.lams.tool.assessment.util.AssessmentEscapeUtils; import org.lamsfoundation.lams.usermanagement.User; import org.lamsfoundation.lams.usermanagement.dto.UserDTO; +import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; import org.lamsfoundation.lams.util.CommonConstants; import org.lamsfoundation.lams.util.Configuration; import org.lamsfoundation.lams.util.ConfigurationKeys; @@ -116,6 +117,9 @@ private IAssessmentService service; @Autowired + private IUserManagementService userManagementService; + + @Autowired private IQbService qbService; @Autowired @@ -833,6 +837,51 @@ service.saveOrUpdateAssessment(assessment); } + @RequestMapping(path = "/getPossibleIndividualTimeLimitUsers", method = RequestMethod.GET) + @ResponseBody + public String getPossibleIndividualTimeLimitUsers( + @RequestParam(name = AssessmentConstants.PARAM_TOOL_CONTENT_ID) long toolContentId, + @RequestParam(name = "term") String searchString) { + List users = service.getPossibleIndividualTimeLimitUsers(toolContentId, searchString); + + ArrayNode responseJSON = JsonNodeFactory.instance.arrayNode(); + for (AssessmentUser user : users) { + // this format is required by jQuery UI autocomplete + ObjectNode userJSON = JsonNodeFactory.instance.objectNode(); + userJSON.put("value", user.getUid()); + userJSON.put("label", user.getFirstName() + " " + user.getLastName() + " (" + user.getLoginName() + ")"); + responseJSON.add(userJSON); + } + return responseJSON.toString(); + } + + @RequestMapping(path = "/getExistingIndividualTimeLimitUsers", method = RequestMethod.GET) + @ResponseBody + public String getExistingIndividualTimeLimitUsers( + @RequestParam(name = AssessmentConstants.PARAM_TOOL_CONTENT_ID) long toolContentId) { + List users = service.getExistingIndividualTimeLimitUsers(toolContentId); + + ArrayNode responseJSON = JsonNodeFactory.instance.arrayNode(); + for (AssessmentUser user : users) { + // this format is required by jQuery UI autocomplete + ObjectNode userJSON = JsonNodeFactory.instance.objectNode(); + userJSON.put("uid", user.getUid()); + userJSON.put("name", user.getFirstName() + " " + user.getLastName() + " (" + user.getLoginName() + ")"); + userJSON.put("adjustment", user.getTimeLimitAdjustment()); + responseJSON.add(userJSON); + } + return responseJSON.toString(); + } + + @RequestMapping(path = "/updateIndividualTimeLimit", method = RequestMethod.POST) + @ResponseStatus(HttpStatus.OK) + public void updateIndividualTimeLimit(@RequestParam long userUid, + @RequestParam(required = false) Integer adjustment) { + AssessmentUser user = (AssessmentUser) userManagementService.findById(AssessmentUser.class, userUid); + user.setTimeLimitAdjustment(adjustment); + userManagementService.save(user); + } + @SuppressWarnings("unchecked") private SessionMap getSessionMap(HttpServletRequest request) { String sessionMapID = WebUtil.readStrParam(request, AssessmentConstants.ATTR_SESSION_MAP_ID); Index: lams_tool_assessment/web/pages/monitoring/monitoring.jsp =================================================================== diff -u -r29bc39cd5828fbffdc6a414fd18b91e6a89f66c4 -r0336d51e6688a15639cb7d414223e947a1e8d118 --- lams_tool_assessment/web/pages/monitoring/monitoring.jsp (.../monitoring.jsp) (revision 29bc39cd5828fbffdc6a414fd18b91e6a89f66c4) +++ lams_tool_assessment/web/pages/monitoring/monitoring.jsp (.../monitoring.jsp) (revision 0336d51e6688a15639cb7d414223e947a1e8d118) @@ -23,6 +23,10 @@ .countdown-timeout { color: #FF3333 !important; } + + #time-limit-table th { + vertical-align: middle; + } @@ -59,6 +63,7 @@ + Index: lams_tool_assessment/web/pages/monitoring/parts/timeLimit.jsp =================================================================== diff -u -re321be7f93ea35c8418b5de59bc1806d985fd80f -r0336d51e6688a15639cb7d414223e947a1e8d118 --- lams_tool_assessment/web/pages/monitoring/parts/timeLimit.jsp (.../timeLimit.jsp) (revision e321be7f93ea35c8418b5de59bc1806d985fd80f) +++ lams_tool_assessment/web/pages/monitoring/parts/timeLimit.jsp (.../timeLimit.jsp) (revision 0336d51e6688a15639cb7d414223e947a1e8d118) @@ -13,9 +13,9 @@
- +
- + - + @@ -123,6 +123,62 @@ + + + + + + + + + + + + + + + + +
@@ -70,7 +70,7 @@
+ + +
+ + +
+
Index: lams_tool_assessment/web/pages/monitoring/summary.jsp =================================================================== diff -u -re321be7f93ea35c8418b5de59bc1806d985fd80f -r0336d51e6688a15639cb7d414223e947a1e8d118 --- lams_tool_assessment/web/pages/monitoring/summary.jsp (.../summary.jsp) (revision e321be7f93ea35c8418b5de59bc1806d985fd80f) +++ lams_tool_assessment/web/pages/monitoring/summary.jsp (.../summary.jsp) (revision 0336d51e6688a15639cb7d414223e947a1e8d118) @@ -288,6 +288,7 @@ if (absoluteTimeLimit) { updateAbsoluteTimeLimitCounter(); } + initInidividualTimeLimitAutocomplete(); }); function resizeJqgrid(jqgrids) { @@ -415,6 +416,25 @@ $('#absolute-time-limit-start').prop('disabled', false); return; } + + if (type == 'individual') { + // this method is called with updateTimeLimit.call() so we can change meaning of "this" + // and identify row and userUid + var button = $(this), + row = button.closest('.individual-time-limit-row'), + userUid = row.data('userUid'); + + // disable individual time adjustment + if (toggle === false) { + updateIndividualTimeLimitOnServer(userUid); + return; + } + var existingAdjustment = +$('.individual-time-limit-value', row).text(), + newAdjustment = existingAdjustment + adjust; + + updateIndividualTimeLimitOnServer(userUid, newAdjustment); + return; + } } function updateTimeLimitOnServer() { @@ -536,6 +556,90 @@ } } + + function initInidividualTimeLimitAutocomplete(){ + $('#individual-time-limit-autocomplete').autocomplete({ + 'source' : '?toolContentID=${assessment.contentId}', + 'delay' : 700, + 'minLength' : 3, + 'select' : function(event, ui){ + // userUid and default 0 adjustment + updateIndividualTimeLimitOnServer(ui.item.value, 0); + + // clear search field + $(this).val(''); + return false; + }, + 'focus': function() { + // Stop the autocomplete of resetting the value to the selected one + // It puts LAMS user ID instead of user name + event.preventDefault(); + } + }); + + refreshInidividualTimeLimitUsers(); + } + + + function updateIndividualTimeLimitOnServer(userUid, adjustment) { + $.ajax({ + 'url' : '', + 'type': 'post', + 'cache' : 'false', + 'data': { + 'userUid' : userUid, + 'adjustment' : adjustment, + '' : '' + }, + success : function(){ + refreshInidividualTimeLimitUsers(); + } + }); + } + + + function refreshInidividualTimeLimitUsers() { + var table = $('#time-limit-table'); + + $.ajax({ + 'url' : '', + 'dataType' : 'json', + 'cache' : 'false', + 'data': { + 'toolContentID' : '${assessment.contentId}' + }, + success : function(users) { + // remove existing users + $('.individual-time-limit-row', table).remove(); + + if (!users) { + return; + } + + var template = $('#individual-time-limit-template-row'), + now = new Date().getTime(); + $.each(users, function(){ + var row = template.clone() + .attr('id', 'individual-time-limit-row-' + this.uid) + .data('userUid', this.uid) + .addClass('individual-time-limit-row') + .appendTo(table); + $('.individual-time-limit-user-name', row).text(this.name); + $('.individual-time-limit-value', row).text(this.adjustment); + // var individualFinishTime = now + (this.adjustment * 60 * 1000), + // individualFinishDate = new Date(individualFinishTime).toString(); + // $('time.timeago', row).attr('datetime', individualFinishDate); + + row.removeClass('hidden'); + }); + + // $("time.timeago").timeago(); + } + }); + } + + // END OF TIME LIMIT +