Index: lams_central/conf/security/Owasp.CsrfGuard.properties =================================================================== diff -u -ra61b6ad192148c0ae514f37a9b488c3a15535ee9 -r05580687f2a97ad551fc8810156fdab05888d2e6 --- lams_central/conf/security/Owasp.CsrfGuard.properties (.../Owasp.CsrfGuard.properties) (revision a61b6ad192148c0ae514f37a9b488c3a15535ee9) +++ lams_central/conf/security/Owasp.CsrfGuard.properties (.../Owasp.CsrfGuard.properties) (revision 05580687f2a97ad551fc8810156fdab05888d2e6) @@ -138,6 +138,7 @@ org.owasp.csrfguard.protected.dokuAuthoringSave=/lams/tool/ladoku11/authoring/update.do org.owasp.csrfguard.protected.dokuAuthoringDefineLater=/lams/tool/ladoku11/authoring/definelater.do +org.owasp.csrfguard.protected.dokuMonitoringUpdateLearnerMark=/lams/tool/ladoku11/monitoring/updateLearnerMark.do org.owasp.csrfguard.protected.forumAuthoringSave=/lams/tool/lafrum11/authoring/update.do org.owasp.csrfguard.protected.forumAuthoringDefineLater=/lams/tool/lafrum11/authoring/definelater.do Index: lams_central/web/includes/javascript/jquery.tablesorter-editable.js =================================================================== diff -u --- lams_central/web/includes/javascript/jquery.tablesorter-editable.js (revision 0) +++ lams_central/web/includes/javascript/jquery.tablesorter-editable.js (revision 05580687f2a97ad551fc8810156fdab05888d2e6) @@ -0,0 +1,4 @@ +(function(factory){if (typeof define === 'function' && define.amd){define(['jquery'], factory);} else if (typeof module === 'object' && typeof module.exports === 'object'){module.exports = factory(require('jquery'));} else {factory(jQuery);}}(function(jQuery){ + +/*! Widget: editable - updated 2018-08-27 (v2.31.0) */ +!function(b){"use strict";var p=b.tablesorter.editable={namespace:".tseditable",lastEdited:"tseditable-last-edited-cell",editComplete:function(e,t,n,i){e.$table.find("."+p.lastEdited).removeClass(p.lastEdited).trigger(t.editable_editComplete,[e]),i&&setTimeout(function(){n.focus()},50)},selectAll:function(n){setTimeout(function(){var e,t;document.queryCommandSupported("SelectAll")?document.execCommand("selectAll",!1,null):document.body.createTextRange?((e=document.body.createTextRange()).moveToElementText(n),e.select()):window.getSelection&&(t=window.getSelection(),(e=document.createRange()).selectNodeContents(n),t.removeAllRanges(),t.addRange(e))},100)},getColumns:function(e,t){var n,i,o,a,l,d=t.editable_columns,s=[];if("string"==typeof d)for(a=(n=d.replace(/\s+/,"").split(/,/)).length-1;0<=a;){if(0<=n[a].indexOf("-"))for(o=n[a].split("-"),i=parseInt(o[0],10)||0,(o=parseInt(o[1],10)||e.columns-1)").wrapInner(t.editable_wrapContent).children().length||b.isFunction(t.editable_wrapContent),c=p.getColumns(e,t).join(",");for(e.$tbodies.find(c).find("[contenteditable]").prop("contenteditable",!1),a=(i=e.$tbodies.find(c).not("."+t.editable_noEdit)).length,o=0;o"+t.html()+"",t.html(b(e).text().trim()))},0)})},destroy:function(e,t){var n=p.namespace,i=p.getColumns(e,t),o="updateComplete pagerComplete ".split(" ").join(n+" ").replace(/\s+/g," ");e.$table.off(o),o="focus focusout keydown paste ".split(" ").join(n+" ").replace(/\s+/g," "),e.$tbodies.off(o).find(i.join(",")).find("[contenteditable]").prop("contenteditable",!1)}};b.tablesorter.addWidget({id:"editable",options:{editable_columns:[],editable_enterToAccept:!0,editable_autoAccept:!0,editable_autoResort:!1,editable_wrapContent:"
",editable_trimContent:!0,editable_validate:null,editable_focused:null,editable_blur:null,editable_selectAll:!1,editable_noEdit:"no-edit",editable_editComplete:"editComplete"},init:function(e,t,n,i){i.editable_columns.length&&(p.update(n,i),p.bindEvents(n,i))},remove:function(e,t,n,i){i||p.destroy(t,n)}})}(jQuery);return jQuery;})); Index: lams_tool_doku/conf/language/lams/ApplicationResources_en_AU.properties =================================================================== diff -u -r3c67ff837c0fe3595c3e06e9f3e1d396fcc2c0d5 -r05580687f2a97ad551fc8810156fdab05888d2e6 --- lams_tool_doku/conf/language/lams/ApplicationResources_en_AU.properties (.../ApplicationResources_en_AU.properties) (revision 3c67ff837c0fe3595c3e06e9f3e1d396fcc2c0d5) +++ lams_tool_doku/conf/language/lams/ApplicationResources_en_AU.properties (.../ApplicationResources_en_AU.properties) (revision 05580687f2a97ad551fc8810156fdab05888d2e6) @@ -93,7 +93,9 @@ label.monitoring.learner.marks.first.name =First name label.monitoring.learner.marks.last.name =Last name label.monitoring.learner.marks.mark =Mark +label.monitoring.learner.marks.mark.tip =(click to edit) messsage.monitoring.learner.marks.no.data = No learner data found +messsage.monitoring.learner.marks.update.fail = Error while updating a learner mark outcome.authoring.title =Learning outcomes outcome.authoring.input =Search and select by outcome name or code Index: lams_tool_doku/src/java/org/lamsfoundation/lams/tool/dokumaran/web/controller/MonitoringController.java =================================================================== diff -u -r3c67ff837c0fe3595c3e06e9f3e1d396fcc2c0d5 -r05580687f2a97ad551fc8810156fdab05888d2e6 --- lams_tool_doku/src/java/org/lamsfoundation/lams/tool/dokumaran/web/controller/MonitoringController.java (.../MonitoringController.java) (revision 3c67ff837c0fe3595c3e06e9f3e1d396fcc2c0d5) +++ lams_tool_doku/src/java/org/lamsfoundation/lams/tool/dokumaran/web/controller/MonitoringController.java (.../MonitoringController.java) (revision 05580687f2a97ad551fc8810156fdab05888d2e6) @@ -29,6 +29,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import javax.servlet.ServletException; @@ -38,6 +39,10 @@ import javax.servlet.http.HttpSession; import org.apache.log4j.Logger; +import org.lamsfoundation.lams.gradebook.GradebookUserActivity; +import org.lamsfoundation.lams.gradebook.service.IGradebookService; +import org.lamsfoundation.lams.security.ISecurityService; +import org.lamsfoundation.lams.tool.ToolSession; import org.lamsfoundation.lams.tool.dokumaran.DokumaranConstants; import org.lamsfoundation.lams.tool.dokumaran.dto.ReflectDTO; import org.lamsfoundation.lams.tool.dokumaran.dto.SessionDTO; @@ -47,14 +52,18 @@ import org.lamsfoundation.lams.tool.dokumaran.model.DokumaranUser; import org.lamsfoundation.lams.tool.dokumaran.service.DokumaranConfigurationException; import org.lamsfoundation.lams.tool.dokumaran.service.IDokumaranService; +import org.lamsfoundation.lams.tool.service.ILamsCoreToolService; import org.lamsfoundation.lams.usermanagement.dto.UserDTO; import org.lamsfoundation.lams.util.WebUtil; import org.lamsfoundation.lams.web.session.SessionManager; import org.lamsfoundation.lams.web.util.AttributeNames; import org.lamsfoundation.lams.web.util.SessionMap; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import com.fasterxml.jackson.databind.node.ArrayNode; @@ -75,6 +84,16 @@ @Autowired private IDokumaranService dokumaranService; + @Autowired + private IGradebookService gradebookService; + + @Autowired + @Qualifier("lamsCoreToolService") + private ILamsCoreToolService toolService; + + @Autowired + private ISecurityService securityService; + @RequestMapping("/summary") private String summary(HttpServletRequest request, HttpServletResponse response) throws DokumaranConfigurationException, URISyntaxException { @@ -139,7 +158,7 @@ @RequestMapping("/getLearnerMarks") @ResponseBody - public String getLearnerMarks(HttpServletRequest request, HttpServletResponse response) + private String getLearnerMarks(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Long toolSessionId = WebUtil.readLongParam(request, "toolSessionId"); @@ -177,19 +196,40 @@ ObjectNode responsedata = JsonNodeFactory.instance.objectNode(); responsedata.put("total_rows", users.size()); + ToolSession toolSession = toolService.getToolSessionById(toolSessionId); + Map gradebookUserActivities = gradebookService + .getGradebookUserActivities(toolSession.getToolActivity().getActivityId()).stream() + .filter(g -> g.getMark() != null) + .collect(Collectors.toMap(g -> g.getLearner().getUserId(), GradebookUserActivity::getMark)); + for (DokumaranUser user : users) { ObjectNode responseRow = JsonNodeFactory.instance.objectNode(); + responseRow.put("userId", user.getUserId()); responseRow.put("firstName", user.getFirstName()); responseRow.put("lastName", user.getLastName()); + Double mark = gradebookUserActivities.get(user.getUserId().intValue()); + responseRow.put("mark", mark == null ? "" : String.valueOf(mark)); rows.add(responseRow); } + responsedata.set("rows", rows); response.setContentType("application/json;charset=utf-8"); return responsedata.toString(); } + @RequestMapping(path = "/updateLearnerMark", method = RequestMethod.POST) + private void updateLearnerMark(@RequestParam long toolSessionId, @RequestParam int userId, + @RequestParam Double mark) { + ToolSession toolSession = toolService.getToolSessionById(toolSessionId); + long lessonId = toolSession.getLesson().getLessonId(); + securityService.isLessonMonitor(lessonId, userId, "update Doku learner mark", true); + + gradebookService.updateGradebookUserActivityMark(mark, null, userId, toolSessionId, true); + + } + @RequestMapping("/fixFaultySession") private void fixFaultySession(HttpServletRequest request, HttpServletResponse response) throws DokumaranConfigurationException, ServletException, IOException { @@ -240,4 +280,9 @@ } + private Integer getUserId() { + HttpSession ss = SessionManager.getSession(); + UserDTO user = (UserDTO) ss.getAttribute(AttributeNames.USER); + return user != null ? user.getUserID() : null; + } } Index: lams_tool_doku/web/pages/monitoring/summary.jsp =================================================================== diff -u -r3c67ff837c0fe3595c3e06e9f3e1d396fcc2c0d5 -r05580687f2a97ad551fc8810156fdab05888d2e6 --- lams_tool_doku/web/pages/monitoring/summary.jsp (.../summary.jsp) (revision 3c67ff837c0fe3595c3e06e9f3e1d396fcc2c0d5) +++ lams_tool_doku/web/pages/monitoring/summary.jsp (.../summary.jsp) (revision 05580687f2a97ad551fc8810156fdab05888d2e6) @@ -49,6 +49,10 @@ #time-limit-buttons { padding-bottom: 20px; } + + .tablesorter tbody > tr > td > div[contenteditable=true]:focus { + outline: #337ab7 2px solid; + } @@ -58,6 +62,7 @@ + @@ -187,28 +192,77 @@ // marks table for each group - $(".tablesorter").tablesorter({ + var tablesorters = $(".tablesorter"); + // intialise tablesorter tables + tablesorters.tablesorter({ theme: 'bootstrap', headerTemplate : '{content} {icon}', sortInitialOrder: 'asc', sortList: [[0]], - widgets: [ "uitheme", "resizable" ], + widgets: [ "uitheme", "resizable", "editable" ], headers: { 0: { sorter: true}, 1: { sorter: true}, 2: { sorter: false} }, sortList : [[0,1]], - showProcessing: true, + showProcessing: false, widgetOptions: { - resizable: true + resizable: true, + + // only marks is editable + editable_columns : [2], + editable_enterToAccept : true, // press enter to accept content, or click outside if false + editable_autoAccept : false, // accepts any changes made to the table cell automatically + editable_autoResort : false, // auto resort after the content has changed. + editable_validate : function (text, original, columnIndex) { + // removing all text produces " ", so get rid of it + text = text ? text.replace(/ /g, '').trim() : null; + // acceptable values are empty text or a number + return !text || !isNaN(text) ? text : original; + }, + editable_selectAll : function(txt, columnIndex, $element) { + // note $element is the div inside of the table cell, so use $element.closest('td') to get the cell + // only select everthing within the element when the content starts with the letter "B" + return true; + }, + editable_wrapContent : '
', // wrap all editable cell content... makes this widget work in IE, and with autocomplete + editable_trimContent : true, // trim content ( removes outer tabs & carriage returns ) + editable_editComplete : 'editComplete' // event fired after the table content has been edited } }); + + // update mark on edit + tablesorters.each(function(){ + // config event variable new in v2.17.6 + $(this).children('tbody').on('editComplete', 'td', function(event, config) { + var $this = $(this), + mark = $this.text() ? +$this.text() : null, + toolSessionId = +$this.closest('.tablesorter').attr('toolSessionId'), + userId = +$this.closest('tr').attr('userId'); - $(".tablesorter").each(function() { + $.ajax({ + async: true, + url: '', + data : { + 'toolSessionId' : toolSessionId, + 'userId' : userId, + 'mark' : mark, + '' : '' + }, + type: 'post', + error: function (request, status, error) { + alert(''); + } + }); + + }); + }); + + // pager processing + tablesorters.each(function() { var toolSessionId = $(this).attr('toolSessionId'); $(this).tablesorterPager({ processAjaxOnInit: true, initialRows: { - total: 10, - filtered: 10 + total: 10 }, savePages: false, container: $(this).find(".ts-pager"), @@ -221,11 +275,11 @@ if (data && data.hasOwnProperty('rows')) { var rows = [], json = {}; - - for (i = 0; i < data.rows.length; i++){ + + for (i = 0; i < data.rows.length; i++){ var userData = data.rows[i]; - rows += ''; + rows += ''; rows += ''; rows += userData['firstName']; @@ -236,7 +290,7 @@ rows += ''; rows += ''; - rows += 0; + rows += userData['mark']; rows += ''; rows += ''; @@ -245,7 +299,6 @@ json.total = data.total_rows; json.rows = $(rows); return json; - } } }) @@ -384,7 +437,9 @@ - +   + +